This commit is contained in:
Iliyan Angelov
2025-09-26 00:45:31 +03:00
parent 1e316a763a
commit 3f5bcfad68
2821 changed files with 1043 additions and 38155 deletions

View File

@@ -1,11 +1,15 @@
# coding=utf-8
from pkg_resources import DistributionNotFound, get_distribution
__author__ = """Cristi V."""
__email__ = 'cristi@cvjd.me'
try:
__version__ = get_distribution(__name__).version
except DistributionNotFound: # pragma: no cover
# package is not installed
pass
from importlib.metadata import version
__version__ = version(__name__)
except ImportError: # Python < 3.8
try:
from pkg_resources import DistributionNotFound, get_distribution
__version__ = get_distribution(__name__).version
except DistributionNotFound: # pragma: no cover
# package is not installed
pass

View File

@@ -41,6 +41,9 @@ SWAGGER_DEFAULTS = {
'DEFAULT_API_URL': None,
'USE_SESSION_AUTH': True,
'USE_COMPAT_RENDERERS': getattr(settings, 'SWAGGER_USE_COMPAT_RENDERERS', True),
'CSRF_COOKIE_NAME': settings.CSRF_COOKIE_NAME,
'CSRF_HEADER_NAME': settings.CSRF_HEADER_NAME,
'SECURITY_DEFINITIONS': {
'Basic': {
'type': 'basic'

View File

@@ -6,9 +6,7 @@ import uuid
from contextlib import suppress
from collections import OrderedDict
from decimal import Decimal
from inspect import signature as inspect_signature
import pkg_resources
import typing
from django.core import validators
from django.db import models
@@ -23,7 +21,20 @@ from ..utils import (
decimal_as_float, field_value_to_representation, filter_none, get_serializer_class, get_serializer_ref_name
)
drf_version = pkg_resources.get_distribution("djangorestframework").version
try:
from importlib import metadata
drf_version = metadata.version("djangorestframework")
except ImportError: # Python < 3.8
import pkg_resources
drf_version = pkg_resources.get_distribution("djangorestframework").version
try:
from types import NoneType, UnionType
UNION_TYPES = (typing.Union, UnionType)
except ImportError: # Python < 3.10
NoneType = type(None)
UNION_TYPES = (typing.Union,)
logger = logging.getLogger(__name__)
@@ -189,7 +200,7 @@ def get_queryset_from_view(view, serializer=None):
if queryset is not None and serializer is not None:
# make sure the view is actually using *this* serializer
assert type(serializer) == call_view_method(view, 'get_serializer_class', 'serializer_class')
assert type(serializer) is call_view_method(view, 'get_serializer_class', 'serializer_class')
return queryset
except Exception: # pragma: no cover
@@ -476,15 +487,6 @@ def decimal_return_type():
return openapi.TYPE_STRING if rest_framework_settings.COERCE_DECIMAL_TO_STRING else openapi.TYPE_NUMBER
def get_origin_type(hint_class):
return getattr(hint_class, '__origin__', None) or hint_class
def hint_class_issubclass(hint_class, check_class):
origin_type = get_origin_type(hint_class)
return inspect.isclass(origin_type) and issubclass(origin_type, check_class)
hinting_type_info = [
(bool, (openapi.TYPE_BOOLEAN, None)),
(int, (openapi.TYPE_INTEGER, None)),
@@ -501,11 +503,15 @@ hinting_type_info = [
if hasattr(typing, 'get_args'):
# python >=3.8
typing_get_args = typing.get_args
typing_get_origin = typing.get_origin
else:
# python <3.8
def typing_get_args(tp):
return getattr(tp, '__args__', ())
def typing_get_origin(tp):
return getattr(tp, '__origin__', None)
def inspect_collection_hint_class(hint_class):
args = typing_get_args(hint_class)
@@ -521,12 +527,6 @@ def inspect_collection_hint_class(hint_class):
hinting_type_info.append(((typing.Sequence, typing.AbstractSet), inspect_collection_hint_class))
def _get_union_types(hint_class):
origin_type = get_origin_type(hint_class)
if origin_type is typing.Union:
return hint_class.__args__
def get_basic_type_info_from_hint(hint_class):
"""Given a class (eg from a SerializerMethodField's return type hint,
return its basic type information - ``type``, ``format``, ``pattern``,
@@ -536,12 +536,12 @@ def get_basic_type_info_from_hint(hint_class):
:return: the extracted attributes as a dictionary, or ``None`` if the field type is not known
:rtype: OrderedDict
"""
union_types = _get_union_types(hint_class)
if union_types:
if typing_get_origin(hint_class) in UNION_TYPES:
# Optional is implemented as Union[T, None]
if len(union_types) == 2 and isinstance(None, union_types[1]):
result = get_basic_type_info_from_hint(union_types[0])
filtered_types = [t for t in typing_get_args(hint_class) if t is not NoneType]
if len(filtered_types) == 1:
result = get_basic_type_info_from_hint(filtered_types[0])
if result:
result['x-nullable'] = True
@@ -549,8 +549,15 @@ def get_basic_type_info_from_hint(hint_class):
return None
# resolve the origin class if the class is generic
resolved_class = typing_get_origin(hint_class) or hint_class
# bail out early
if not inspect.isclass(resolved_class):
return None
for check_class, info in hinting_type_info:
if hint_class_issubclass(hint_class, check_class):
if issubclass(resolved_class, check_class):
if callable(info):
return info(hint_class)
@@ -613,17 +620,19 @@ class SerializerMethodFieldInspector(FieldInspector):
return self.probe_field_inspectors(serializer, swagger_object_type, use_references, read_only=True)
else:
# look for Python 3.5+ style type hinting of the return value
hint_class = inspect_signature(method).return_annotation
hint_class = typing.get_type_hints(method).get('return')
if not inspect.isclass(hint_class) and hasattr(hint_class, '__args__'):
hint_class = hint_class.__args__[0]
if inspect.isclass(hint_class) and not issubclass(hint_class, inspect._empty):
type_info = get_basic_type_info_from_hint(hint_class)
# annotations such as typing.Optional have an __instancecheck__
# hook and will not look like classes, but `issubclass` needs
# a class as its first argument, so only in that case abort
if inspect.isclass(hint_class) and issubclass(hint_class, inspect._empty):
return NotHandled
if type_info is not None:
SwaggerType, ChildSwaggerType = self._get_partial_types(field, swagger_object_type,
use_references, **kwargs)
return SwaggerType(**type_info)
type_info = get_basic_type_info_from_hint(hint_class)
if type_info is not None:
SwaggerType, ChildSwaggerType = self._get_partial_types(field, swagger_object_type,
use_references, **kwargs)
return SwaggerType(**type_info)
return NotHandled

View File

@@ -88,7 +88,7 @@ class SwaggerDict(OrderedDict):
def __init__(self, **attrs):
super(SwaggerDict, self).__init__()
self._extras__ = attrs
if type(self) == SwaggerDict:
if type(self) is SwaggerDict:
self._insert_extras__()
def __setattr__(self, key, value):
@@ -516,7 +516,7 @@ class _Ref(SwaggerDict):
:param bool ignore_unresolved: do not throw if the referenced object does not exist
"""
super(_Ref, self).__init__()
assert not type(self) == _Ref, "do not instantiate _Ref directly"
assert not type(self) is _Ref, "do not instantiate _Ref directly"
ref_name = "#/{scope}/{name}".format(scope=scope, name=name)
if not ignore_unresolved:
obj = resolver.get(name, scope)

View File

@@ -46,14 +46,14 @@ class OpenAPIRenderer(_SpecRenderer):
class SwaggerJSONRenderer(_SpecRenderer):
"""Renders the schema as a JSON document with the generic ``application/json`` mime type."""
media_type = 'application/json'
format = '.json'
format = 'json'
codec_class = OpenAPICodecJson
class SwaggerYAMLRenderer(_SpecRenderer):
"""Renders the schema as a YAML document."""
media_type = 'application/yaml'
format = '.yaml'
format = 'yaml'
codec_class = OpenAPICodecYaml
@@ -153,6 +153,9 @@ class SwaggerUIRenderer(_UIRenderer):
'refetchWithAuth': swagger_settings.REFETCH_SCHEMA_WITH_AUTH,
'refetchOnLogout': swagger_settings.REFETCH_SCHEMA_ON_LOGOUT,
'fetchSchemaWithQuery': swagger_settings.FETCH_SCHEMA_WITH_QUERY,
'csrfCookie': swagger_settings.CSRF_COOKIE_NAME,
# remove HTTP_ and convert underscores to dashes
'csrfHeader': swagger_settings.CSRF_HEADER_NAME[5:].replace('_', '-'),
}
data = filter_none(data)

View File

@@ -36,11 +36,26 @@ var swaggerUiConfig = {
],
layout: "StandaloneLayout",
filter: true,
csrfCookie: 'csrftoken',
csrfHeader: 'X-CSRFToken',
requestInterceptor: function (request) {
var headers = request.headers || {};
var csrftoken = document.querySelector("[name=csrfmiddlewaretoken]");
if (csrftoken) {
headers["X-CSRFToken"] = csrftoken.value;
csrftoken = csrftoken.value;
} else {
var cookies = document.cookie.split(/;\s+/);
var name = swaggerUiConfig.csrfCookie;
for (var i = 0; i < cookies.length; i++) {
if (cookies[i].indexOf(name) === 0) {
csrftoken = cookies[i].slice(cookies[i].indexOf('=') + 1);
break;
}
}
}
if (csrftoken) {
headers[swaggerUiConfig.csrfHeader] = csrftoken;
}
return request;

View File

@@ -17,6 +17,15 @@ from rest_framework.views import APIView
from .app_settings import swagger_settings
try:
import zoneinfo
except ImportError:
try:
from backports import zoneinfo
except ImportError:
zoneinfo = None
logger = logging.getLogger(__name__)
@@ -442,7 +451,7 @@ def force_real_str(s, encoding='utf-8', strings_only=False, errors='strict'):
"""
if s is not None:
s = force_str(s, encoding, strings_only, errors)
if type(s) != str:
if type(s) is not str:
s = '' + s
# Remove common indentation to get the correct Markdown rendering
@@ -465,7 +474,10 @@ def field_value_to_representation(field, value):
else:
value = str(value)
if isinstance(value, pytz.BaseTzInfo):
elif isinstance(value, pytz.BaseTzInfo):
value = str(value)
elif zoneinfo is not None and isinstance(value, zoneinfo.ZoneInfo):
value = str(value)
# JSON roundtrip ensures that the value is valid JSON;

View File

@@ -13,7 +13,9 @@ from .app_settings import swagger_settings
from .renderers import (
ReDocOldRenderer,
ReDocRenderer,
SwaggerJSONRenderer,
SwaggerUIRenderer,
SwaggerYAMLRenderer,
_SpecRenderer,
)
@@ -77,6 +79,20 @@ def get_schema_view(info=None, url=None, patterns=None, urlconf=None, public=Fal
validators = validators or []
_spec_renderers = tuple(renderer.with_validators(validators) for renderer in SPEC_RENDERERS)
# optionally copy renderers with the validators that are configured above
if swagger_settings.USE_COMPAT_RENDERERS:
warnings.warn(
"SwaggerJSONRenderer & SwaggerYAMLRenderer's `format` has changed to not include a `.` prefix, "
"please silence this warning by setting `SWAGGER_USE_COMPAT_RENDERERS = False` "
"in your Django settings and ensure your application works "
"(check your URLCONF and swagger/redoc URLs).",
DeprecationWarning)
_spec_renderers += tuple(
type(cls.__name__, (cls,), {'format': '.' + cls.format})
for cls in _spec_renderers
if issubclass(cls, (SwaggerJSONRenderer, SwaggerYAMLRenderer))
)
class SchemaView(APIView):
_ignore_model_permissions = True
schema = None # exclude from schema