Compare commits

...

15 Commits

Author SHA1 Message Date
Cristi Vîjdea 13311582ea Add 1.17.0 changelog
Closes #412
2019-10-03 02:02:02 +03:00
Aliaksei Urbanski 9a89d8ccb0 Improve testing (#415)
These changes:

 - Fix the lint and the djmaster jobs
 - Fix compatibility with upcoming Django 3.0
 - Replace jobs with matrix in .travis.yml
 - Add a test job for Python 3.8
 - Allow running tests on any branch
2019-10-03 01:38:02 +03:00
Cristi Vîjdea 16f67cd8c2 Fix CHANGELOG.rst syntax
Fixes #425
2019-09-29 19:15:42 +03:00
Cristi Vîjdea 99fa7c25ca Add missing newline for #444 2019-09-29 19:08:37 +03:00
grumbling-tom 97e70d9d16 custom_spec.rst: Update to indicate swagger_auto_schema import location. (#444) 2019-09-29 19:07:08 +03:00
Myungseo Kang ee086a6eec Fix typo in docs (#460) 2019-09-29 19:03:05 +03:00
johnthagen 4af38c970a Fix typo (#456) 2019-09-29 19:02:50 +03:00
Ilya Stepin 95337f85ad custom_spec: fix typo in docs (#447) 2019-09-29 19:02:37 +03:00
Étienne Noss 1352c2a23b fix Optional typing hint for SerializerMethodField (#428) 2019-09-29 19:01:28 +03:00
Cristi Vîjdea 8578b93eba Update swagger-ui to 3.23.11 and ReDoc to 2.0.0-rc.14
Supersedes #434
Fixes #398
2019-09-29 18:57:54 +03:00
johnthagen 212891b1b8 Update README to support DRF 3.10 (#424) 2019-09-29 18:51:54 +03:00
Étienne Noss ab6444a32e inspectors: add support for JSONField (#417) 2019-07-19 14:13:26 +03:00
Cristi Vijdea 2e0f9a19a9 Update swagger-ui to 3.23.1 2019-07-16 21:22:44 +03:00
Cristi Vijdea cda808fe11 Fix isort errors 2019-07-16 21:20:46 +03:00
Cristi Vijdea e6219ab8b7 Add packaging to requirements 2019-07-16 21:19:19 +03:00
25 changed files with 519 additions and 342 deletions
+3 -6
View File
@@ -4,12 +4,13 @@ python:
- '3.5'
- '3.6'
- '3.7'
- '3.8-dev'
dist: xenial
cache: pip
jobs:
matrix:
include:
- python: '3.6'
env: TOXENV=docs
@@ -38,6 +39,7 @@ jobs:
allow_failures:
- env: TOXENV=lint
- env: TOXENV=djmaster
- python: '3.8-dev'
fast_finish: true
@@ -59,11 +61,6 @@ after_success:
codecov
fi
branches:
only:
- master
- /^v?\d+\.\d+(\.\d+)?(-?\S+)?$/
stages:
- test
- name: publish
+1 -1
View File
@@ -6,7 +6,7 @@
Contributing
############
Contributions are always welcome and appreciated! Here are some ways you can contribut.
Contributions are always welcome and appreciated! Here are some ways you can contribute.
******
Issues
+1 -1
View File
@@ -13,7 +13,7 @@ Generate **real** Swagger/OpenAPI 2.0 specifications from a Django Rest Framewor
Compatible with
- **Django Rest Framework**: 3.8, 3.9
- **Django Rest Framework**: 3.8, 3.9, 3.10
- **Django**: 1.11, 2.1, 2.2
- **Python**: 2.7, 3.5, 3.6, 3.7
+12 -1
View File
@@ -2,6 +2,17 @@
Changelog
#########
**********
**1.17.0**
**********
*Release date: Oct 03, 2019*
- **ADDED:** added `JSONFieldInspector` for `JSONField` support (:pr:`417`)
- **IMPROVED:** updated ``swagger-ui`` to version 3.23.11
- **IMPROVED:** updated ``ReDoc`` to version 2.0.0-rc.14 (:issue:`398`)
- **FIXED:** fixed a type hint support issue (:pr:`428`, :issue:`450`)
- **FIXED:** fixed packaging issue caused by a missing requirement (:issue:`412`)
**********
**1.16.1**
@@ -9,8 +20,8 @@ Changelog
*Release date: Jul 16, 2019*
- **IMPROVED:** better enum type detection for nested `ChoiceField`\ s (:pr:`400`)
- **FIXED:** fixed DRF 3.10 compatibility (:pr:`408`, :issue:`410`, :issue:`411`)
- **IMPROVED:** better enum type detection for nested `ChoiceField`s (:pr:`400`)
**********
**1.16.0**
+3 -1
View File
@@ -45,6 +45,8 @@ some properties of the generated :class:`.Operation`. For example, in a ``ViewSe
.. code-block:: python
from drf_yasg.utils import swagger_auto_schema
@swagger_auto_schema(operation_description="partial_update description override", responses={404: 'slug not found'})
def partial_update(self, request, *args, **kwargs):
"""partial_update method docstring"""
@@ -376,7 +378,7 @@ A second example, of a :class:`~.inspectors.FieldInspector` that removes the ``t
class AnotherSerializer(serializers.ModelSerializer):
chilf = OneSerializer()
child = OneSerializer()
class Meta:
model = SomeParentModel
+2 -1
View File
@@ -85,6 +85,7 @@ to this list.
:class:`'drf_yasg.inspectors.ChoiceFieldInspector' <.inspectors.ChoiceFieldInspector>`, |br| \
:class:`'drf_yasg.inspectors.FileFieldInspector' <.inspectors.FileFieldInspector>`, |br| \
:class:`'drf_yasg.inspectors.DictFieldInspector' <.inspectors.DictFieldInspector>`, |br| \
:class:`'drf_yasg.inspectors.JSONFieldInspector' <.inspectors.JSONFieldInspector>`, |br| \
:class:`'drf_yasg.inspectors.HiddenFieldInspector' <.inspectors.HiddenFieldInspector>`, |br| \
:class:`'drf_yasg.inspectors.RecursiveFieldInspector' <.inspectors.RecursiveFieldInspector>`, |br| \
:class:`'drf_yasg.inspectors.SerializerMethodFieldInspector' <.inspectors.SerializerMethodFieldInspector>`, |br| \
@@ -454,7 +455,7 @@ FETCH_SCHEMA_WITH_QUERY
Fetch the OpenAPI document using the query parameters passed to the ReDoc page request.
**Default**: :python:`'True` |br|
**Default**: :python:`True` |br|
*Maps to parameter*: -
+260 -246
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -1,8 +1,8 @@
{
"name": "drf-yasg",
"dependencies": {
"redoc": "^2.0.0-rc.8-1",
"swagger-ui-dist": "^3.22.3"
"redoc": "^2.0.0-rc.14",
"swagger-ui-dist": "^3.23.11"
},
"repository": {
"type": "git",
+1
View File
@@ -4,6 +4,7 @@ ruamel.yaml>=0.15.34
inflection>=0.3.1
six>=1.10.0
uritemplate>=3.0.0
packaging
djangorestframework>=3.8
Django>=1.11.7
-3
View File
@@ -1,6 +1,5 @@
# test project requirements
Pillow>=4.3.0
django-cors-headers>=2.1.0
django-filter>=1.1.0,<2.0; python_version == "2.7"
django-filter>=1.1.0; python_version >= "3.5"
#djangorestframework-camel-case>=0.2.0
@@ -9,5 +8,3 @@ django-filter>=1.1.0; python_version >= "3.5"
djangorestframework-recursive>=0.1.2
dj-database-url>=0.4.2
user_agents>=1.1.0
# django-oauth-toolkit 1.2 does not support Django 1.11
django-oauth-toolkit>=1.1.0,<1.2.0
+1
View File
@@ -12,6 +12,7 @@ SWAGGER_DEFAULTS = {
'drf_yasg.inspectors.ChoiceFieldInspector',
'drf_yasg.inspectors.FileFieldInspector',
'drf_yasg.inspectors.DictFieldInspector',
'drf_yasg.inspectors.JSONFieldInspector',
'drf_yasg.inspectors.HiddenFieldInspector',
'drf_yasg.inspectors.RelatedFieldInspector',
'drf_yasg.inspectors.SerializerMethodFieldInspector',
+10 -10
View File
@@ -3,24 +3,16 @@ import logging
import re
from collections import OrderedDict, defaultdict
import uritemplate
import rest_framework
import uritemplate
from coreapi.compat import urlparse
from packaging.version import Version
from rest_framework import versioning
from rest_framework.compat import URLPattern, URLResolver, get_original_route
from rest_framework.schemas.generators import EndpointEnumerator as _EndpointEnumerator
from rest_framework.schemas.generators import endpoint_ordering, get_pk_name
from rest_framework.settings import api_settings
from packaging.version import Version
if Version(rest_framework.__version__) < Version('3.10'):
from rest_framework.schemas.generators import SchemaGenerator
from rest_framework.schemas.inspectors import get_pk_description
else:
from rest_framework.schemas import SchemaGenerator
from rest_framework.schemas.utils import get_pk_description
from . import openapi
from .app_settings import swagger_settings
from .errors import SwaggerGenerationError
@@ -28,6 +20,14 @@ from .inspectors.field import get_basic_type_info, get_queryset_field, get_query
from .openapi import ReferenceResolver, SwaggerDict
from .utils import force_real_str, get_consumes, get_produces
if Version(rest_framework.__version__) < Version('3.10'):
from rest_framework.schemas.generators import SchemaGenerator
from rest_framework.schemas.inspectors import get_pk_description
else:
from rest_framework.schemas import SchemaGenerator
from rest_framework.schemas.utils import get_pk_description
logger = logging.getLogger(__name__)
PATH_PARAMETER_RE = re.compile(r'{(?P<parameter>\w+)}')
+3 -3
View File
@@ -4,8 +4,8 @@ from .base import (
)
from .field import (
CamelCaseJSONFilter, ChoiceFieldInspector, DictFieldInspector, FileFieldInspector, HiddenFieldInspector,
InlineSerializerInspector, RecursiveFieldInspector, ReferencingSerializerInspector, RelatedFieldInspector,
SerializerMethodFieldInspector, SimpleFieldInspector, StringDefaultFieldInspector
InlineSerializerInspector, JSONFieldInspector, RecursiveFieldInspector, ReferencingSerializerInspector,
RelatedFieldInspector, SerializerMethodFieldInspector, SimpleFieldInspector, StringDefaultFieldInspector
)
from .query import CoreAPICompatInspector, DjangoRestResponsePagination
from .view import SwaggerAutoSchema
@@ -24,7 +24,7 @@ __all__ = [
# field inspectors
'InlineSerializerInspector', 'RecursiveFieldInspector', 'ReferencingSerializerInspector', 'RelatedFieldInspector',
'SimpleFieldInspector', 'FileFieldInspector', 'ChoiceFieldInspector', 'DictFieldInspector',
'SimpleFieldInspector', 'FileFieldInspector', 'ChoiceFieldInspector', 'DictFieldInspector', 'JSONFieldInspector',
'StringDefaultFieldInspector', 'CamelCaseJSONFilter', 'HiddenFieldInspector', 'SerializerMethodFieldInspector',
# view inspectors
+15 -1
View File
@@ -610,6 +610,8 @@ class SerializerMethodFieldInspector(FieldInspector):
# look for Python 3.5+ style type hinting of the return value
hint_class = inspect_signature(method).return_annotation
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)
@@ -745,11 +747,23 @@ class HiddenFieldInspector(FieldInspector):
return NotHandled
class JSONFieldInspector(FieldInspector):
"""Provides conversion for ``JSONField``."""
def field_to_swagger_object(self, field, swagger_object_type, use_references, **kwargs):
SwaggerType, ChildSwaggerType = self._get_partial_types(field, swagger_object_type, use_references, **kwargs)
if isinstance(field, serializers.JSONField) and swagger_object_type == openapi.Schema:
return SwaggerType(type=openapi.TYPE_OBJECT)
return NotHandled
class StringDefaultFieldInspector(FieldInspector):
"""For otherwise unhandled fields, return them as plain :data:`.TYPE_STRING` objects."""
def field_to_swagger_object(self, field, swagger_object_type, use_references, **kwargs): # pragma: no cover
# TODO unhandled fields: TimeField JSONField
# TODO unhandled fields: TimeField
SwaggerType, ChildSwaggerType = self._get_partial_types(field, swagger_object_type, use_references, **kwargs)
return SwaggerType(type=openapi.TYPE_STRING)
+2 -2
View File
@@ -2,7 +2,7 @@ import six
from django.shortcuts import resolve_url
from django.template.loader import render_to_string
from django.utils.encoding import force_text
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
@@ -124,7 +124,7 @@ class SwaggerUIRenderer(_UIRenderer):
swagger_ui_settings = self.get_swagger_ui_settings()
request = renderer_context.get('request', None)
oauth_redirect_url = force_text(swagger_ui_settings.get('oauth2RedirectUrl', ''))
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)
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -5,7 +5,7 @@ from collections import OrderedDict
from decimal import Decimal
from django.db import models
from django.utils.encoding import force_text
from django.utils.encoding import force_str
from rest_framework import serializers, status
from rest_framework.mixins import DestroyModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin
from rest_framework.parsers import FileUploadParser
@@ -434,7 +434,7 @@ def force_real_str(s, encoding='utf-8', strings_only=False, errors='strict'):
Fix for https://github.com/axnsan12/drf-yasg/issues/159
"""
if s is not None:
s = force_text(s, encoding, strings_only, errors)
s = force_str(s, encoding, strings_only, errors)
if type(s) != str:
s = '' + s
+2 -3
View File
@@ -1,8 +1,7 @@
import warnings
from functools import wraps
from functools import WRAPPER_ASSIGNMENTS, wraps
from django.utils.cache import add_never_cache_headers
from django.utils.decorators import available_attrs
from django.views.decorators.cache import cache_page
from django.views.decorators.vary import vary_on_headers
from rest_framework import exceptions
@@ -30,7 +29,7 @@ def deferred_never_cache(view_func):
never be cached.
"""
@wraps(view_func, assigned=available_attrs(view_func))
@wraps(view_func, assigned=WRAPPER_ASSIGNMENTS)
def _wrapped_view_func(request, *args, **kwargs):
response = view_func(request, *args, **kwargs)
+5 -4
View File
@@ -1,15 +1,16 @@
import rest_framework
from decimal import Decimal
import rest_framework
from django.contrib.auth import get_user_model
from packaging.version import Version
from rest_framework import serializers
from snippets.models import LANGUAGE_CHOICES, STYLE_CHOICES, Snippet, SnippetViewer
if Version(rest_framework.__version__) < Version('3.10'):
from rest_framework.compat import MaxLengthValidator, MinValueValidator
else:
from django.core.validators import MaxLengthValidator, MinValueValidator
from rest_framework import serializers
from snippets.models import LANGUAGE_CHOICES, STYLE_CHOICES, Snippet, SnippetViewer
class LanguageSerializer(serializers.Serializer):
+61
View File
@@ -1,4 +1,5 @@
import json
import sys
from collections import OrderedDict
import pytest
@@ -17,6 +18,11 @@ from drf_yasg.errors import SwaggerGenerationError
from drf_yasg.generators import OpenAPISchemaGenerator
from drf_yasg.utils import swagger_auto_schema
try:
import typing
except ImportError:
typing = None
def test_schema_is_valid(swagger, codec_yaml):
codec_yaml.encode(swagger)
@@ -273,3 +279,58 @@ def test_nested_choice_in_array_field(choices, field, expected_type):
swagger = generator.get_schema(None, True)
property_schema = swagger['definitions']['Array']['properties']['array']['items']
assert property_schema == openapi.Schema(title='Array', type=expected_type, enum=choices)
def test_json_field():
class TestJSONFieldSerializer(serializers.Serializer):
json = serializers.JSONField()
class JSONViewSet(viewsets.ModelViewSet):
serializer_class = TestJSONFieldSerializer
router = routers.DefaultRouter()
router.register(r'jsons', JSONViewSet, **_basename_or_base_name('jsons'))
generator = OpenAPISchemaGenerator(
info=openapi.Info(title='Test json field generator', default_version='v1'),
patterns=router.urls
)
swagger = generator.get_schema(None, True)
property_schema = swagger["definitions"]["TestJSONField"]["properties"]["json"]
assert property_schema == openapi.Schema(title='Json', type=openapi.TYPE_OBJECT)
@pytest.mark.parametrize('py_type, expected_type', [
(str, openapi.TYPE_STRING),
(int, openapi.TYPE_INTEGER),
(float, openapi.TYPE_NUMBER),
(bool, openapi.TYPE_BOOLEAN),
])
@pytest.mark.skipif(typing is None or sys.version_info.major < 3, reason="typing not supported")
def test_optional_return_type(py_type, expected_type):
class OptionalMethodSerializer(serializers.Serializer):
x = serializers.SerializerMethodField()
def get_x(self, instance):
pass
# Add the type annotation here in order to avoid a SyntaxError in py27
get_x.__annotations__["return"] = typing.Optional[py_type]
class OptionalMethodViewSet(viewsets.ViewSet):
@swagger_auto_schema(responses={200: openapi.Response("OK", OptionalMethodSerializer)})
def retrieve(self, request, pk=None):
return Response({'optional': None})
router = routers.DefaultRouter()
router.register(r'optional', OptionalMethodViewSet, **_basename_or_base_name('optional'))
generator = OpenAPISchemaGenerator(
info=openapi.Info(title='Test optional parameter', default_version='v1'),
patterns=router.urls
)
swagger = generator.get_schema(None, True)
property_schema = swagger["definitions"]["OptionalMethod"]["properties"]["x"]
assert property_schema == openapi.Schema(title='X', type=expected_type, readOnly=True)
+18 -5
View File
@@ -8,9 +8,10 @@ envlist =
py27-django111-drf39-typing,
py27-django111-drf{38,39},
py{35,36}-django{111,21,22}-drf{38,39},
py37-django{21,22}-drf{38,39},
py37-django{21,22}-drf310,
py37-django{21,22}-drf{38,39,310},
py38-django22-drf310,
djmaster, lint, docs
skip_missing_interpreters = true
[testenv:.package]
# no additional dependencies besides PEP 517
@@ -19,8 +20,17 @@ deps =
[testenv]
deps =
django111: Django>=1.11,<2.0
django111: django-cors-headers>=2.1.0
django111: django-oauth-toolkit>=1.1.0,<1.2.0
django21: Django>=2.1,<2.2
django21: django-cors-headers>=2.1.0
django21: django-oauth-toolkit>=1.2.0
django22: Django>=2.2,<2.3
django22: django-cors-headers>=2.1.0
django22: django-oauth-toolkit>=1.2.0
drf38: djangorestframework>=3.8,<3.9
drf39: djangorestframework>=3.9,<3.10
@@ -28,9 +38,12 @@ deps =
typing: typing>=3.6.6
# test with the latest build of django-rest-framework to get early warning of compatibility issues
djmaster: https://github.com/encode/django-rest-framework/archive/master.tar.gz
# test with the latest builds of Django and django-rest-framework
# to get early warning of compatibility issues
djmaster: https://github.com/django/django/archive/master.tar.gz
djmaster: https://github.com/ottoyiu/django-cors-headers/archive/master.tar.gz
djmaster: https://github.com/encode/django-rest-framework/archive/master.tar.gz
djmaster: django-oauth-toolkit>=1.2.0
# other dependencies
-r requirements/validation.txt
@@ -77,5 +90,5 @@ known_standard_library =
known_third_party =
coreapi,coreschema,datadiff,dj_database_url,django,django_filters,djangorestframework_camel_case,
rest_framework_recursive,flex,gunicorn,inflection,pytest,rest_framework,ruamel,setuptools_scm,
swagger_spec_validator,uritemplate,user_agents,whitenoise,oauth2_provider
swagger_spec_validator,uritemplate,user_agents,whitenoise,oauth2_provider,packaging
known_first_party = drf_yasg,testproj,articles,people,snippets,todo,users,urlconfs