mirror of
https://github.com/goauthentik/authentik.git
synced 2026-06-17 19:09:11 +03:00
root: add mypy (#16904)
* add mypy Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix some stuff Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix more stuff Signed-off-by: Jens Langhammer <jens@goauthentik.io> * actually do overrides on a per-module basis Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix small stuff Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add pydantic plugin Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
@@ -2,16 +2,28 @@
|
||||
|
||||
import os
|
||||
from json import dumps
|
||||
from sys import exit as sysexit
|
||||
from time import time
|
||||
|
||||
from authentik import authentik_version
|
||||
|
||||
|
||||
def must_or_fail(input: str | None, error: str) -> str:
|
||||
if not input:
|
||||
print(f"::error::{error}")
|
||||
sysexit(1)
|
||||
return input
|
||||
|
||||
|
||||
# Decide if we should push the image or not
|
||||
should_push = True
|
||||
if len(os.environ.get("DOCKER_USERNAME", "")) < 1:
|
||||
# Don't push if we don't have DOCKER_USERNAME, i.e. no secrets are available
|
||||
should_push = False
|
||||
if os.environ.get("GITHUB_REPOSITORY").lower() == "goauthentik/authentik-internal":
|
||||
if (
|
||||
must_or_fail(os.environ.get("GITHUB_REPOSITORY"), "Repo required").lower()
|
||||
== "goauthentik/authentik-internal"
|
||||
):
|
||||
# Don't push on the internal repo
|
||||
should_push = False
|
||||
|
||||
@@ -20,13 +32,16 @@ if os.environ.get("GITHUB_HEAD_REF", "") != "":
|
||||
branch_name = os.environ["GITHUB_HEAD_REF"]
|
||||
safe_branch_name = branch_name.replace("refs/heads/", "").replace("/", "-").replace("'", "-")
|
||||
|
||||
image_names = os.getenv("IMAGE_NAME").split(",")
|
||||
image_names = must_or_fail(os.getenv("IMAGE_NAME"), "Image name required").split(",")
|
||||
image_arch = os.getenv("IMAGE_ARCH") or None
|
||||
|
||||
is_pull_request = bool(os.getenv("PR_HEAD_SHA"))
|
||||
is_release = "dev" not in image_names[0]
|
||||
|
||||
sha = os.environ["GITHUB_SHA"] if not is_pull_request else os.getenv("PR_HEAD_SHA")
|
||||
sha = must_or_fail(
|
||||
os.environ["GITHUB_SHA"] if not is_pull_request else os.getenv("PR_HEAD_SHA"),
|
||||
"could not determine SHA",
|
||||
)
|
||||
|
||||
# 2042.1.0 or 2042.1.0-rc1
|
||||
version = authentik_version()
|
||||
@@ -58,7 +73,7 @@ else:
|
||||
image_main_tag = image_tags[0].split(":")[-1]
|
||||
|
||||
|
||||
def get_attest_image_names(image_with_tags: list[str]):
|
||||
def get_attest_image_names(image_with_tags: list[str]) -> str:
|
||||
"""Attestation only for GHCR"""
|
||||
image_tags = []
|
||||
for image_name in set(name.split(":")[0] for name in image_with_tags):
|
||||
@@ -82,7 +97,6 @@ if os.getenv("RELEASE", "false").lower() == "true":
|
||||
image_build_args = [f"VERSION={os.getenv('REF')}"]
|
||||
else:
|
||||
image_build_args = [f"GIT_BUILD_HASH={sha}"]
|
||||
image_build_args = "\n".join(image_build_args)
|
||||
|
||||
with open(os.environ["GITHUB_OUTPUT"], "a+", encoding="utf-8") as _output:
|
||||
print(f"shouldPush={str(should_push).lower()}", file=_output)
|
||||
@@ -95,4 +109,4 @@ with open(os.environ["GITHUB_OUTPUT"], "a+", encoding="utf-8") as _output:
|
||||
print(f"imageMainTag={image_main_tag}", file=_output)
|
||||
print(f"imageMainName={image_tags[0]}", file=_output)
|
||||
print(f"cacheTo={cache_to}", file=_output)
|
||||
print(f"imageBuildArgs={image_build_args}", file=_output)
|
||||
print(f"imageBuildArgs={"\n".join(image_build_args)}", file=_output)
|
||||
|
||||
@@ -34,6 +34,7 @@ jobs:
|
||||
- codespell
|
||||
- pending-migrations
|
||||
- ruff
|
||||
- mypy
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
@@ -320,6 +320,9 @@ ci--meta-debug:
|
||||
python -V
|
||||
node --version
|
||||
|
||||
ci-mypy: ci--meta-debug
|
||||
uv run mypy --strict $(PY_SOURCES)
|
||||
|
||||
ci-black: ci--meta-debug
|
||||
uv run black --check $(PY_SOURCES)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""custom runserver command"""
|
||||
|
||||
from typing import TextIO
|
||||
from io import StringIO
|
||||
|
||||
from daphne.management.commands.runserver import Command as RunServer
|
||||
from daphne.server import Server
|
||||
@@ -33,4 +33,4 @@ class Command(RunServer):
|
||||
super().__init__(*args, **kwargs)
|
||||
# Redirect standard stdout banner from Daphne into the void
|
||||
# as there are a couple more steps that happen before startup is fully done
|
||||
self.stdout = TextIO()
|
||||
self.stdout = StringIO()
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
|
||||
from django.contrib.auth.signals import user_logged_in
|
||||
from django.core.cache import cache
|
||||
from django.core.signals import Signal
|
||||
from django.db.models import Model
|
||||
from django.db.models.signals import post_delete, post_save, pre_save
|
||||
from django.dispatch import receiver
|
||||
from django.dispatch import Signal, receiver
|
||||
from django.http.request import HttpRequest
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ if TYPE_CHECKING:
|
||||
class ASNDict(TypedDict):
|
||||
"""ASN Details"""
|
||||
|
||||
asn: int
|
||||
asn: int | None
|
||||
as_org: str | None
|
||||
network: str | None
|
||||
|
||||
@@ -60,7 +60,7 @@ class ASNContextProcessor(MMDBContextProcessor):
|
||||
except (GeoIP2Error, ValueError):
|
||||
return None
|
||||
|
||||
def asn_to_dict(self, asn: ASN | None) -> ASNDict:
|
||||
def asn_to_dict(self, asn: ASN | None) -> ASNDict | dict:
|
||||
"""Convert ASN to dict"""
|
||||
if not asn:
|
||||
return {}
|
||||
|
||||
@@ -19,10 +19,10 @@ if TYPE_CHECKING:
|
||||
class GeoIPDict(TypedDict):
|
||||
"""GeoIP Details"""
|
||||
|
||||
continent: str
|
||||
country: str
|
||||
lat: float
|
||||
long: float
|
||||
continent: str | None
|
||||
country: str | None
|
||||
lat: float | None
|
||||
long: float | None
|
||||
city: str
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ class GeoIPContextProcessor(MMDBContextProcessor):
|
||||
except (GeoIP2Error, ValueError):
|
||||
return None
|
||||
|
||||
def city_to_dict(self, city: City | None) -> GeoIPDict:
|
||||
def city_to_dict(self, city: City | None) -> GeoIPDict | dict:
|
||||
"""Convert City to dict"""
|
||||
if not city:
|
||||
return {}
|
||||
|
||||
@@ -60,7 +60,7 @@ class UserInfoView(View):
|
||||
for scope in scopes:
|
||||
if scope in special_scope_map:
|
||||
scope_descriptions.append(
|
||||
PermissionDict(id=scope, name=str(special_scope_map[scope]))
|
||||
PermissionDict(id=str(scope), name=str(special_scope_map[scope]))
|
||||
)
|
||||
return scope_descriptions
|
||||
|
||||
|
||||
@@ -239,32 +239,33 @@ class AssertionProcessor:
|
||||
).from_http(self.http_request)
|
||||
LOGGER.warning("Failed to evaluate property mapping", exc=exc)
|
||||
return name_id
|
||||
if name_id.attrib["Format"] == SAML_NAME_ID_FORMAT_EMAIL:
|
||||
if self.auth_n_request.name_id_policy == SAML_NAME_ID_FORMAT_EMAIL:
|
||||
name_id.text = self.http_request.user.email
|
||||
return name_id
|
||||
if name_id.attrib["Format"] in [
|
||||
if self.auth_n_request.name_id_policy in [
|
||||
SAML_NAME_ID_FORMAT_PERSISTENT,
|
||||
SAML_NAME_ID_FORMAT_UNSPECIFIED,
|
||||
]:
|
||||
name_id.text = persistent
|
||||
return name_id
|
||||
if name_id.attrib["Format"] == SAML_NAME_ID_FORMAT_X509:
|
||||
if self.auth_n_request.name_id_policy == SAML_NAME_ID_FORMAT_X509:
|
||||
# This attribute is statically set by the LDAP source
|
||||
name_id.text = self.http_request.user.attributes.get(
|
||||
LDAP_DISTINGUISHED_NAME, persistent
|
||||
)
|
||||
return name_id
|
||||
if name_id.attrib["Format"] == SAML_NAME_ID_FORMAT_WINDOWS:
|
||||
if self.auth_n_request.name_id_policy == SAML_NAME_ID_FORMAT_WINDOWS:
|
||||
# This attribute is statically set by the LDAP source
|
||||
name_id.text = self.http_request.user.attributes.get("upn", persistent)
|
||||
return name_id
|
||||
if name_id.attrib["Format"] == SAML_NAME_ID_FORMAT_TRANSIENT:
|
||||
if self.auth_n_request.name_id_policy == SAML_NAME_ID_FORMAT_TRANSIENT:
|
||||
# Use the hash of the user's session, which changes every session
|
||||
session_key: str = self.http_request.session.session_key
|
||||
name_id.text = sha256(session_key.encode()).hexdigest()
|
||||
return name_id
|
||||
raise UnsupportedNameIDFormat(
|
||||
f"Assertion contains NameID with unsupported format {name_id.attrib['Format']}."
|
||||
"Assertion contains NameID with unsupported "
|
||||
f"format {self.auth_n_request.name_id_policy}."
|
||||
)
|
||||
|
||||
def get_assertion_subject(self) -> Element:
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from datetime import timedelta
|
||||
|
||||
from django.core.signals import Signal
|
||||
from django.dispatch import receiver
|
||||
from django.dispatch import Signal, receiver
|
||||
from django.utils.timezone import now
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""authentik identification signals"""
|
||||
|
||||
from django.core.signals import Signal
|
||||
from django.dispatch import Signal
|
||||
|
||||
# Arguments: request: HttpRequest, uid_field: Value entered by user
|
||||
identification_failed = Signal()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""authentik invitation signals"""
|
||||
|
||||
from django.core.signals import Signal
|
||||
from django.dispatch import Signal
|
||||
|
||||
# Arguments: request: HttpRequest, invitation: Invitation
|
||||
invitation_used = Signal()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""authentik prompt stage signals"""
|
||||
|
||||
from django.core.signals import Signal
|
||||
from django.dispatch import Signal
|
||||
|
||||
# Arguments: password: str, plan_context: dict[str, Any]
|
||||
password_validate = Signal()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""authentik user_write signals"""
|
||||
|
||||
from django.core.signals import Signal
|
||||
from django.dispatch import Signal
|
||||
|
||||
# Arguments: request: HttpRequest, user: User, data: dict[str, Any], created: bool
|
||||
user_write = Signal()
|
||||
|
||||
+89
-15
@@ -85,19 +85,24 @@ dev = [
|
||||
"constructs==10.4.2",
|
||||
"coverage[toml]==7.8.0",
|
||||
"debugpy==1.8.14",
|
||||
"django-stubs[compatible-mypy]==5.2.5",
|
||||
"djangorestframework-stubs[compatible-mypy]==3.16.3",
|
||||
"drf-jsonschema-serializer==3.0.0",
|
||||
"freezegun==1.5.1",
|
||||
"importlib-metadata==8.6.1",
|
||||
"k5test==0.10.4",
|
||||
"lxml-stubs==0.5.1",
|
||||
"mypy==1.18.2",
|
||||
"pdoc==15.0.3",
|
||||
"pytest==8.3.5",
|
||||
"pytest-django==4.11.1",
|
||||
"pytest-github-actions-annotate-failures==0.3.0",
|
||||
"pytest-randomly==3.16.0",
|
||||
"pytest-timeout==2.4.0",
|
||||
"pytest==8.3.5",
|
||||
"requests-mock==1.12.1",
|
||||
"ruff==0.11.9",
|
||||
"selenium==4.32.0",
|
||||
"types-ldap3==2.9.13.20250622",
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
@@ -172,20 +177,13 @@ exclude = ["**/migrations/**", "**/node_modules/**"]
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = [
|
||||
# pycodestyle
|
||||
"E",
|
||||
# Pyflakes
|
||||
"F",
|
||||
# isort
|
||||
"I",
|
||||
# pyupgrade
|
||||
"UP",
|
||||
# flake8-bugbear
|
||||
"B",
|
||||
# django
|
||||
"DJ",
|
||||
# pylint
|
||||
"PL",
|
||||
"E", # pycodestyle
|
||||
"F", # Pyflakes
|
||||
"I", # isort
|
||||
"UP", # pyupgrade
|
||||
"B", # flake8-bugbear
|
||||
"DJ", # django
|
||||
"PL", # pylint
|
||||
]
|
||||
ignore = [
|
||||
"DJ001", # Avoid using `null=True` on string-based fields,
|
||||
@@ -196,6 +194,82 @@ max-args = 7
|
||||
max-branches = 18
|
||||
max-returns = 10
|
||||
|
||||
[tool.mypy]
|
||||
plugins = ["mypy_django_plugin.main", "mypy_drf_plugin.main", "pydantic.mypy"]
|
||||
exclude = ['^gen-py-api/']
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
module = [
|
||||
"authentik.admin.*",
|
||||
"authentik.api.*",
|
||||
"authentik.blueprints.*",
|
||||
"authentik.brands.*",
|
||||
"authentik.core.*",
|
||||
"authentik.crypto.*",
|
||||
"authentik.enterprise.*",
|
||||
"authentik.events.*",
|
||||
"authentik.flows.*",
|
||||
"authentik.lib.*",
|
||||
"authentik.outposts.*",
|
||||
"authentik.policies.*",
|
||||
"authentik.policies.dummy.*",
|
||||
"authentik.policies.event_matcher.*",
|
||||
"authentik.policies.expiry.*",
|
||||
"authentik.policies.expression.*",
|
||||
"authentik.policies.geoip.*",
|
||||
"authentik.policies.password.*",
|
||||
"authentik.policies.reputation.*",
|
||||
"authentik.providers.ldap.*",
|
||||
"authentik.providers.oauth2.*",
|
||||
"authentik.providers.proxy.*",
|
||||
"authentik.providers.rac.*",
|
||||
"authentik.providers.radius.*",
|
||||
"authentik.providers.saml.*",
|
||||
"authentik.providers.scim.*",
|
||||
"authentik.rbac.*",
|
||||
"authentik.recovery.*",
|
||||
"authentik.root.*",
|
||||
"authentik.sources.kerberos.*",
|
||||
"authentik.sources.ldap.*",
|
||||
"authentik.sources.oauth.*",
|
||||
"authentik.sources.plex.*",
|
||||
"authentik.sources.saml.*",
|
||||
"authentik.sources.scim.*",
|
||||
"authentik.stages.authenticator_duo.*",
|
||||
"authentik.stages.authenticator_email.*",
|
||||
"authentik.stages.authenticator_sms.*",
|
||||
"authentik.stages.authenticator_static.*",
|
||||
"authentik.stages.authenticator_totp.*",
|
||||
"authentik.stages.authenticator_validate.*",
|
||||
"authentik.stages.authenticator_webauthn.*",
|
||||
"authentik.stages.authenticator.*",
|
||||
"authentik.stages.captcha.*",
|
||||
"authentik.stages.consent.*",
|
||||
"authentik.stages.deny.*",
|
||||
"authentik.stages.dummy.*",
|
||||
"authentik.stages.email.*",
|
||||
"authentik.stages.identification.*",
|
||||
"authentik.stages.invitation.*",
|
||||
"authentik.stages.password.*",
|
||||
"authentik.stages.prompt.*",
|
||||
"authentik.stages.redirect.*",
|
||||
"authentik.stages.user_delete.*",
|
||||
"authentik.stages.user_login.*",
|
||||
"authentik.stages.user_logout.*",
|
||||
"authentik.stages.user_write.*",
|
||||
"authentik.tasks.*",
|
||||
"authentik.tasks.schedules.*",
|
||||
"authentik.tenants.*",
|
||||
"django_dramatiq_postgres.*",
|
||||
"lifecycle.*",
|
||||
"tests.e2e.*",
|
||||
"tests.integration.*",
|
||||
]
|
||||
ignore_errors = true
|
||||
|
||||
[tool.django-stubs]
|
||||
django_settings_module = "authentik.root.settings"
|
||||
|
||||
[tool.coverage.run]
|
||||
source = ["authentik"]
|
||||
relative_files = true
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate config for development"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from yaml import safe_dump
|
||||
|
||||
from authentik.lib.generators import generate_id
|
||||
|
||||
|
||||
def generate_local_config():
|
||||
def generate_local_config() -> dict[str, Any]:
|
||||
"""Generate a local development configuration"""
|
||||
# TODO: This should be generated and validated against a schema, such as Pydantic.
|
||||
|
||||
|
||||
@@ -242,10 +242,14 @@ dev = [
|
||||
{ name = "constructs" },
|
||||
{ name = "coverage" },
|
||||
{ name = "debugpy" },
|
||||
{ name = "django-stubs", extra = ["compatible-mypy"] },
|
||||
{ name = "djangorestframework-stubs", extra = ["compatible-mypy"] },
|
||||
{ name = "drf-jsonschema-serializer" },
|
||||
{ name = "freezegun" },
|
||||
{ name = "importlib-metadata" },
|
||||
{ name = "k5test" },
|
||||
{ name = "lxml-stubs" },
|
||||
{ name = "mypy" },
|
||||
{ name = "pdoc" },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-django" },
|
||||
@@ -255,6 +259,7 @@ dev = [
|
||||
{ name = "requests-mock" },
|
||||
{ name = "ruff" },
|
||||
{ name = "selenium" },
|
||||
{ name = "types-ldap3" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
@@ -339,10 +344,14 @@ dev = [
|
||||
{ name = "constructs", specifier = "==10.4.2" },
|
||||
{ name = "coverage", extras = ["toml"], specifier = "==7.8.0" },
|
||||
{ name = "debugpy", specifier = "==1.8.14" },
|
||||
{ name = "django-stubs", extras = ["compatible-mypy"], specifier = "==5.2.5" },
|
||||
{ name = "djangorestframework-stubs", extras = ["compatible-mypy"], specifier = "==3.16.3" },
|
||||
{ name = "drf-jsonschema-serializer", specifier = "==3.0.0" },
|
||||
{ name = "freezegun", specifier = "==1.5.1" },
|
||||
{ name = "importlib-metadata", specifier = "==8.6.1" },
|
||||
{ name = "k5test", specifier = "==0.10.4" },
|
||||
{ name = "lxml-stubs", specifier = "==0.5.1" },
|
||||
{ name = "mypy", specifier = "==1.18.2" },
|
||||
{ name = "pdoc", specifier = "==15.0.3" },
|
||||
{ name = "pytest", specifier = "==8.3.5" },
|
||||
{ name = "pytest-django", specifier = "==4.11.1" },
|
||||
@@ -352,6 +361,7 @@ dev = [
|
||||
{ name = "requests-mock", specifier = "==1.12.1" },
|
||||
{ name = "ruff", specifier = "==0.11.9" },
|
||||
{ name = "selenium", specifier = "==4.32.0" },
|
||||
{ name = "types-ldap3", specifier = "==2.9.13.20250622" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1075,6 +1085,39 @@ s3 = [
|
||||
{ name = "boto3" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-stubs"
|
||||
version = "5.2.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
{ name = "django-stubs-ext" },
|
||||
{ name = "types-pyyaml" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e1/8e/286150f593481c33f54d14efb58d72178f159d57d31043529d38bbc98e2f/django_stubs-5.2.5.tar.gz", hash = "sha256:fc78384e28d8c5292d60983075a5934f644f7c304c25ae2793fc57aa66d5018b", size = 247794, upload-time = "2025-09-12T19:29:49.636Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/02/cdbf7652ef2c9a7a1fed7c279484b7f3806f15b1bb34aec9fef8e8cfacbf/django_stubs-5.2.5-py3-none-any.whl", hash = "sha256:223c1a3324cd4873b7629dec6e9adbe224a94508284c1926b25fddff7a92252b", size = 490196, upload-time = "2025-09-12T19:29:47.954Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
compatible-mypy = [
|
||||
{ name = "mypy" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-stubs-ext"
|
||||
version = "5.2.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/16/94/c9b8f4c47084a0fa666da9066c36771098101932688adf2c17a40fab79c2/django_stubs_ext-5.2.5.tar.gz", hash = "sha256:ecc628df29d36cede638567c4e33ff485dd7a99f1552ad0cece8c60e9c3a8872", size = 6489, upload-time = "2025-09-12T19:29:06.008Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/fe/a85a105fddffadb4a8d50e500caeee87d836b679d51a19d52dfa0cc6c660/django_stubs_ext-5.2.5-py3-none-any.whl", hash = "sha256:9b4b8ac9d32f7e6c304fd05477f8688fae6ed57f6a0f9f4d074f9e55b5a3da14", size = 9310, upload-time = "2025-09-12T19:29:04.62Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-tenants"
|
||||
version = "3.8.0"
|
||||
@@ -1117,6 +1160,28 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/59/81/3d62f7ff71f7c45ec6664ebf03a4c736bf77f49481604361d40f8f4471e4/djangorestframework_guardian-0.4.0-py3-none-any.whl", hash = "sha256:30c2a349318c1cd603d6953d50d58159f9a0c833f5f8f5a811407d5984a39e14", size = 6064, upload-time = "2025-07-01T07:22:09.661Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "djangorestframework-stubs"
|
||||
version = "3.16.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "django-stubs" },
|
||||
{ name = "requests" },
|
||||
{ name = "types-pyyaml" },
|
||||
{ name = "types-requests" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c7/90/4512c9a464fec3fc6879f60c6ab1925da013bc5d6fa1a6951c750b02c89d/djangorestframework_stubs-3.16.3.tar.gz", hash = "sha256:200c620192da20a50a85b3e7d35cc7ec795aa9a58371a7f91368d4023e1b78f7", size = 34820, upload-time = "2025-09-18T18:06:01.989Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/64/00ff39218f80e7e9411ff1950c13b179d96e033a08dfb4048d5b90bc54fa/djangorestframework_stubs-3.16.3-py3-none-any.whl", hash = "sha256:d6e77922c2489aa6b5716d321b592b8aab047d5fd7ae9293a52c5cf1640e62c6", size = 54618, upload-time = "2025-09-18T18:06:00.265Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
compatible-mypy = [
|
||||
{ name = "django-stubs", extra = ["compatible-mypy"] },
|
||||
{ name = "mypy" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dnspython"
|
||||
version = "2.8.0"
|
||||
@@ -1827,6 +1892,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/42/85b3aa8f06ca0d24962f8100f001828e1f1f1a38c954c16e71154ed7d53a/lxml-6.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:21db1ec5525780fd07251636eb5f7acb84003e9382c72c18c542a87c416ade03", size = 3672642, upload-time = "2025-06-26T16:27:09.888Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lxml-stubs"
|
||||
version = "0.5.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/99/da/1a3a3e5d159b249fc2970d73437496b908de8e4716a089c69591b4ffa6fd/lxml-stubs-0.5.1.tar.gz", hash = "sha256:e0ec2aa1ce92d91278b719091ce4515c12adc1d564359dfaf81efa7d4feab79d", size = 14778, upload-time = "2024-01-10T09:37:46.521Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/c9/e0f8e4e6e8a69e5959b06499582dca6349db6769cc7fdfb8a02a7c75a9ae/lxml_stubs-0.5.1-py3-none-any.whl", hash = "sha256:1f689e5dbc4b9247cb09ae820c7d34daeb1fdbd1db06123814b856dae7787272", size = 13584, upload-time = "2024-01-10T09:37:44.931Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markdown-it-py"
|
||||
version = "4.0.0"
|
||||
@@ -2112,6 +2186,26 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/69/b547032297c7e63ba2af494edba695d781af8a0c6e89e4d06cf848b21d80/multidict-6.6.4-py3-none-any.whl", hash = "sha256:27d8f8e125c07cb954e54d75d04905a9bba8a439c1d84aca94949d4d03d8601c", size = 12313, upload-time = "2025-08-11T12:08:46.891Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mypy"
|
||||
version = "1.18.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "mypy-extensions" },
|
||||
{ name = "pathspec" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c0/77/8f0d0001ffad290cef2f7f216f96c814866248a0b92a722365ed54648e7e/mypy-1.18.2.tar.gz", hash = "sha256:06a398102a5f203d7477b2923dda3634c36727fa5c237d8f859ef90c42a9924b", size = 3448846, upload-time = "2025-09-19T00:11:10.519Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/04/7f462e6fbba87a72bc8097b93f6842499c428a6ff0c81dd46948d175afe8/mypy-1.18.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:07b8b0f580ca6d289e69209ec9d3911b4a26e5abfde32228a288eb79df129fcc", size = 12898728, upload-time = "2025-09-19T00:10:01.33Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/5b/61ed4efb64f1871b41fd0b82d29a64640f3516078f6c7905b68ab1ad8b13/mypy-1.18.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ed4482847168439651d3feee5833ccedbf6657e964572706a2adb1f7fa4dfe2e", size = 11910758, upload-time = "2025-09-19T00:10:42.607Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/46/d297d4b683cc89a6e4108c4250a6a6b717f5fa96e1a30a7944a6da44da35/mypy-1.18.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3ad2afadd1e9fea5cf99a45a822346971ede8685cc581ed9cd4d42eaf940986", size = 12475342, upload-time = "2025-09-19T00:11:00.371Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/45/4798f4d00df13eae3bfdf726c9244bcb495ab5bd588c0eed93a2f2dd67f3/mypy-1.18.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a431a6f1ef14cf8c144c6b14793a23ec4eae3db28277c358136e79d7d062f62d", size = 13338709, upload-time = "2025-09-19T00:11:03.358Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/09/479f7358d9625172521a87a9271ddd2441e1dab16a09708f056e97007207/mypy-1.18.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7ab28cc197f1dd77a67e1c6f35cd1f8e8b73ed2217e4fc005f9e6a504e46e7ba", size = 13529806, upload-time = "2025-09-19T00:10:26.073Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/cf/ac0f2c7e9d0ea3c75cd99dff7aec1c9df4a1376537cb90e4c882267ee7e9/mypy-1.18.2-cp313-cp313-win_amd64.whl", hash = "sha256:0e2785a84b34a72ba55fb5daf079a1003a34c05b22238da94fcae2bbe46f3544", size = 9833262, upload-time = "2025-09-19T00:10:40.035Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/87/e3/be76d87158ebafa0309946c4a73831974d4d6ab4f4ef40c3b53a385a66fd/mypy-1.18.2-py3-none-any.whl", hash = "sha256:22a1748707dd62b58d2ae53562ffc4d7f8bcc727e8ac7cbc69c053ddc874d47e", size = 2352367, upload-time = "2025-09-19T00:10:15.489Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "1.1.0"
|
||||
@@ -3171,6 +3265,48 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/bb/d43e5c75054e53efce310e79d63df0ac3f25e34c926be5dffb7d283fb2a8/typeguard-2.13.3-py3-none-any.whl", hash = "sha256:5e3e3be01e887e7eafae5af63d1f36c849aaa94e3a0112097312aabfa16284f1", size = 17605, upload-time = "2021-12-10T21:09:37.844Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "types-ldap3"
|
||||
version = "2.9.13.20250622"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "types-pyasn1" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/51/aa/238da23c20f48359c667b69c932a45bdcc325149042f677dc420a721844c/types_ldap3-2.9.13.20250622.tar.gz", hash = "sha256:4770a8701daddf1ee936d46414205a329211b6f6e655c2764f6b70c89b023c47", size = 33634, upload-time = "2025-06-22T03:19:16.32Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/fd/0339a618985d19d9b0630f78822d1becb0661be6abe8adbadd9569b875e1/types_ldap3-2.9.13.20250622-py3-none-any.whl", hash = "sha256:c18d0320327fa0017eb3d95acdf38921542d80939255e4ba130ca2d13ca3375f", size = 56498, upload-time = "2025-06-22T03:19:15.495Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "types-pyasn1"
|
||||
version = "0.6.0.20250914"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/69/92/bfe2385ee347c9d528adbd0fd8e5d7da6bcd18572cc42fd94e44c182dd69/types_pyasn1-0.6.0.20250914.tar.gz", hash = "sha256:236102553b76c938953037b7ae93d11d395d9413b7f2f8083d3b19d740f7eda6", size = 17109, upload-time = "2025-09-14T02:56:08.041Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/9d/5eb611d0db5b980cbb7d3eaca5baf187d5346f6371fdb6c708847539cea6/types_pyasn1-0.6.0.20250914-py3-none-any.whl", hash = "sha256:68ffeef3c28e1ed120b8b81a242f238f137543e68d466d84a97edcf3e4203b5b", size = 24052, upload-time = "2025-09-14T02:56:07.247Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "types-pyyaml"
|
||||
version = "6.0.12.20250915"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7e/69/3c51b36d04da19b92f9e815be12753125bd8bc247ba0470a982e6979e71c/types_pyyaml-6.0.12.20250915.tar.gz", hash = "sha256:0f8b54a528c303f0e6f7165687dd33fafa81c807fcac23f632b63aa624ced1d3", size = 17522, upload-time = "2025-09-15T03:01:00.728Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/e0/1eed384f02555dde685fff1a1ac805c1c7dcb6dd019c916fe659b1c1f9ec/types_pyyaml-6.0.12.20250915-py3-none-any.whl", hash = "sha256:e7d4d9e064e89a3b3cae120b4990cd370874d2bf12fa5f46c97018dd5d3c9ab6", size = 20338, upload-time = "2025-09-15T03:00:59.218Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "types-requests"
|
||||
version = "2.32.4.20250913"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/36/27/489922f4505975b11de2b5ad07b4fe1dca0bca9be81a703f26c5f3acfce5/types_requests-2.32.4.20250913.tar.gz", hash = "sha256:abd6d4f9ce3a9383f269775a9835a4c24e5cd6b9f647d64f88aa4613c33def5d", size = 23113, upload-time = "2025-09-13T02:40:02.309Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/20/9a227ea57c1285986c4cf78400d0a91615d25b24e257fd9e2969606bdfae/types_requests-2.32.4.20250913-py3-none-any.whl", hash = "sha256:78c9c1fffebbe0fa487a418e0fa5252017e9c60d1a2da394077f1780f655d7e1", size = 20658, upload-time = "2025-09-13T02:40:01.115Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.15.0"
|
||||
|
||||
Reference in New Issue
Block a user