# Source code for drf_yasg.renderers

    from django.core.exceptions import ImproperlyConfigured
    from django.shortcuts import resolve_url
    from django.template.loader import render_to_string
    from django.utils.encoding import force_str
    from django.utils.functional import Promise
    from rest_framework.renderers import BaseRenderer, JSONRenderer, TemplateHTMLRenderer
    from rest_framework.utils import encoders, json

    from .app_settings import redoc_settings, swagger_settings
    from .codecs import VALIDATORS, OpenAPICodecJson, OpenAPICodecYaml
    from .openapi import Swagger
    from .utils import filter_none

    class _SpecRenderer(BaseRenderer):
        """Base class for text renderers. Handles encoding and validation."""

        charset = "utf-8"
        validators = []
        codec_class = None

        @classmethod
        def with_validators(cls, validators):
            assert all(vld in VALIDATORS for vld in validators), (
                "allowed validators are " + ", ".join(VALIDATORS)
            )
            return type(cls.__name__, (cls,), {"validators": validators})

        def render(self, data, media_type=None, renderer_context=None):
            assert self.codec_class, "must override codec_class"
            codec = self.codec_class(self.validators)

            if not isinstance(data, Swagger):  # pragma: no cover
                # if `swagger` is not a ``Swagger`` object, it means we somehow got a
                # non-success ``Response`` in that case, it's probably better to let the
                # default ``JSONRenderer`` render it
                # see https://github.com/axnsan12/drf-yasg/issues/58
                return JSONRenderer().render(data, media_type, renderer_context)

            return codec.encode(data)

    class OpenAPIRenderer(_SpecRenderer):
        """Renders the schema as a JSON document with the ``application/openapi+json``
        specific mime type."""

        media_type = "application/openapi+json"
        format = "openapi"
        codec_class = OpenAPICodecJson

    class SwaggerJSONRenderer(_SpecRenderer):
        """Renders the schema as a JSON document with the generic ``application/json`` mime
        type."""

        media_type = "application/json"
        format = "json"
        codec_class = OpenAPICodecJson

    class SwaggerYAMLRenderer(_SpecRenderer):
        """Renders the schema as a YAML document."""

        media_type = "application/yaml"
        format = "yaml"
        codec_class = OpenAPICodecYaml

    class _UIRenderer(BaseRenderer):
        """Base class for web UI renderers. Handles loading and passing settings to the
        appropriate template."""

        media_type = "text/html"
        charset = "utf-8"
        template = ""

        def render(self, swagger, accepted_media_type=None, renderer_context=None):
            if not isinstance(swagger, Swagger):  # pragma: no cover
                try:
                    # if `swagger` is not a ``Swagger`` object, it means we somehow got a
                    # non-success ``Response`` in that case, it's probably better to let the
                    # default ``TemplateHTMLRenderer`` render it
                    # see https://github.com/axnsan12/drf-yasg/issues/58
                    return TemplateHTMLRenderer().render(
                        swagger, accepted_media_type, renderer_context
                    )
                except ImproperlyConfigured:
                    # Fall back to using eg '404 Not Found'
                    response = renderer_context["response"]
                    return "%d %s" % (response.status_code, response.status_text.title())

            self.set_context(renderer_context, swagger)
            return render_to_string(
                self.template, renderer_context, renderer_context["request"]
            )

        def set_context(self, renderer_context, swagger=None):
            renderer_context["title"] = swagger.info.title or "" if swagger else ""
            renderer_context["version"] = swagger.info.version or "" if swagger else ""
            renderer_context["oauth2_config"] = json.dumps(
                self.get_oauth2_config(), cls=encoders.JSONEncoder
            )
            renderer_context["USE_SESSION_AUTH"] = swagger_settings.USE_SESSION_AUTH
            renderer_context.update(self.get_auth_urls())

        def resolve_url(self, to):
            if isinstance(to, Promise):
                to = str(to)

            if to is None:
                return None

            args, kwargs = None, None
            if not isinstance(to, str):
                if len(to) > 2:
                    to, args, kwargs = to
                elif len(to) == 2:
                    to, kwargs = to

            args = args or ()
            kwargs = kwargs or {}

            return resolve_url(to, *args, **kwargs)

        def get_auth_urls(self):
            urls = {
                "LOGIN_URL": self.resolve_url(swagger_settings.LOGIN_URL),
                "LOGOUT_URL": self.resolve_url(swagger_settings.LOGOUT_URL),
            }

            return filter_none(urls)

        def get_oauth2_config(self):
            data = swagger_settings.OAUTH2_CONFIG
            assert isinstance(data, dict), "OAUTH2_CONFIG must be a dict"
            return data

    class SwaggerUIRenderer(_UIRenderer):
        """Renders a swagger-ui web interface for schema browsing."""

        template = "drf-yasg/swagger-ui.html"
        format = "swagger"

        def set_context(self, renderer_context, swagger=None):
            super(SwaggerUIRenderer, self).set_context(renderer_context, swagger)
            swagger_ui_settings = self.get_swagger_ui_settings()

            request = renderer_context.get("request", None)
            oauth_redirect_url = force_str(swagger_ui_settings.get("oauth2RedirectUrl", ""))
            if request and oauth_redirect_url:
                swagger_ui_settings["oauth2RedirectUrl"] = request.build_absolute_uri(
                    oauth_redirect_url
                )

            renderer_context["swagger_settings"] = json.dumps(
                swagger_ui_settings, cls=encoders.JSONEncoder
            )

        def get_swagger_ui_settings(self):
            data = {
                "url": self.resolve_url(swagger_settings.SPEC_URL),
                "operationsSorter": swagger_settings.OPERATIONS_SORTER,
                "tagsSorter": swagger_settings.TAGS_SORTER,
                "docExpansion": swagger_settings.DOC_EXPANSION,
                "deepLinking": swagger_settings.DEEP_LINKING,
                "showExtensions": swagger_settings.SHOW_EXTENSIONS,
                "defaultModelRendering": swagger_settings.DEFAULT_MODEL_RENDERING,
                "defaultModelExpandDepth": swagger_settings.DEFAULT_MODEL_DEPTH,
                "defaultModelsExpandDepth": swagger_settings.DEFAULT_MODEL_DEPTH,
                "showCommonExtensions": swagger_settings.SHOW_COMMON_EXTENSIONS,
                "oauth2RedirectUrl": swagger_settings.OAUTH2_REDIRECT_URL,
                "supportedSubmitMethods": swagger_settings.SUPPORTED_SUBMIT_METHODS,
                "displayOperationId": swagger_settings.DISPLAY_OPERATION_ID,
                "persistAuth": swagger_settings.PERSIST_AUTH,
                "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)
            if swagger_settings.VALIDATOR_URL != "":
                data["validatorUrl"] = self.resolve_url(swagger_settings.VALIDATOR_URL)

            return data

    class ReDocRenderer(_UIRenderer):
        """Renders a ReDoc web interface for schema browsing."""

        template = "drf-yasg/redoc.html"
        format = "redoc"

        def set_context(self, renderer_context, swagger=None):
            super(ReDocRenderer, self).set_context(renderer_context, swagger)
            renderer_context["redoc_settings"] = json.dumps(
                self.get_redoc_settings(), cls=encoders.JSONEncoder
            )

        def get_redoc_settings(self):
            data = {
                "url": self.resolve_url(redoc_settings.SPEC_URL),
                "lazyRendering": redoc_settings.LAZY_RENDERING,
                "hideHostname": redoc_settings.HIDE_HOSTNAME,
                "expandResponses": redoc_settings.EXPAND_RESPONSES,
                "pathInMiddlePanel": redoc_settings.PATH_IN_MIDDLE,
                "nativeScrollbars": redoc_settings.NATIVE_SCROLLBARS,
                "requiredPropsFirst": redoc_settings.REQUIRED_PROPS_FIRST,
                "fetchSchemaWithQuery": redoc_settings.FETCH_SCHEMA_WITH_QUERY,
                "hideDownloadButton": redoc_settings.HIDE_DOWNLOAD_BUTTON,
            }

            return filter_none(data)

    class ReDocOldRenderer(ReDocRenderer):
        """Renders a ReDoc 1.x.x web interface for schema browsing."""

        template = "drf-yasg/redoc-old.html"