Skip to content

Commit

Permalink
Fix in-place modification of swagger_auto_schema arguments (#75)
Browse files Browse the repository at this point in the history
Fixes #74
  • Loading branch information
axnsan12 authored Mar 5, 2018
1 parent ee46f59 commit 6ea8711
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 5 deletions.
9 changes: 9 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ Changelog
#########


*********
**1.4.5**
*********

*Release date: Mar 05, 2018*

- **FIXED:** fixed an issue with modification of ``swagger_auto_schema`` arguments in-place during introspection, which
would sometimes cause an incomplete Swagger document to be generated after the first pass (:issue:`74`, :pr:`75`)

*********
**1.4.4**
*********
Expand Down
3 changes: 2 additions & 1 deletion src/drf_yasg/generators.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import copy
import logging
import re
from collections import OrderedDict, defaultdict
Expand Down Expand Up @@ -388,7 +389,7 @@ def get_overrides(self, view, method):
if method in overrides:
overrides = overrides[method]

return overrides
return copy.deepcopy(overrides)

def get_path_parameters(self, path, view_cls):
"""Return a list of Parameter instances corresponding to any templated path variables.
Expand Down
13 changes: 9 additions & 4 deletions src/drf_yasg/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@

logger = logging.getLogger(__name__)

#: used to forcibly remove the body of a request via :func:`.swagger_auto_schema`
no_body = object()

unset = object()
class no_body(object):
"""Used as a sentinel value to forcibly remove the body of a request via :func:`.swagger_auto_schema`."""
pass


class unset(object):
"""Used as a sentinel value for function parameters not set by the caller where ``None`` would be a valid value."""
pass


def swagger_auto_schema(method=None, methods=None, auto_schema=unset, request_body=None, query_serializer=None,
Expand All @@ -33,7 +38,7 @@ def swagger_auto_schema(method=None, methods=None, auto_schema=unset, request_bo
:param .inspectors.SwaggerAutoSchema auto_schema: custom class to use for generating the Operation object;
this overrides both the class-level ``swagger_schema`` attribute and the ``DEFAULT_AUTO_SCHEMA_CLASS``
setting, and can be set to ``None`` to prevent this operation from being generated
:param .Schema,.SchemaRef,.Serializer request_body: custom request body, or :data:`.no_body`. The value given here
:param .Schema,.SchemaRef,.Serializer request_body: custom request body, or :class:`.no_body`. The value given here
will be used as the ``schema`` property of a :class:`.Parameter` with ``in: 'body'``.
A Schema or SchemaRef is not valid if this request consumes form-data, because ``form`` and ``body`` parameters
Expand Down
34 changes: 34 additions & 0 deletions tests/test_schema_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
from collections import OrderedDict

import pytest
from rest_framework import routers, serializers, viewsets
from rest_framework.response import Response

from drf_yasg import codecs, openapi
from drf_yasg.codecs import yaml_sane_load
from drf_yasg.errors import SwaggerGenerationError
from drf_yasg.generators import OpenAPISchemaGenerator
from drf_yasg.utils import swagger_auto_schema


def test_schema_is_valid(swagger, codec_yaml):
Expand Down Expand Up @@ -79,3 +82,34 @@ def test_securiy_requirements(swagger_settings, mock_schema_request):

swagger = generator.get_schema(mock_schema_request, public=True)
assert swagger['security'] == []


def test_replaced_serializer():
class DetailSerializer(serializers.Serializer):
detail = serializers.CharField()

class DetailViewSet(viewsets.ViewSet):
serializer_class = DetailSerializer

@swagger_auto_schema(responses={404: openapi.Response("Not found or Not accessible", DetailSerializer)})
def retrieve(self, request, pk=None):
serializer = DetailSerializer({'detail': None})
return Response(serializer.data)

router = routers.DefaultRouter()
router.register(r'details', DetailViewSet, base_name='details')

generator = OpenAPISchemaGenerator(
info=openapi.Info(title="Test generator", default_version="v1"),
version="v2",
url='',
patterns=router.urls
)

for _ in range(3):
swagger = generator.get_schema(None, True)
assert 'Detail' in swagger['definitions']
assert 'detail' in swagger['definitions']['Detail']['properties']
responses = swagger['paths']['/details/{id}/']['get']['responses']
assert '404' in responses
assert responses['404']['schema']['$ref'] == "#/definitions/Detail"

0 comments on commit 6ea8711

Please sign in to comment.