Force descriptions to be str objects

Fixes #159
openapi3
Cristi Vîjdea 2018-08-03 18:57:42 +03:00
parent f2e05ee4c0
commit 2965e08e39
9 changed files with 33 additions and 14 deletions

View File

@ -5,6 +5,7 @@ import json
from collections import OrderedDict from collections import OrderedDict
from coreapi.compat import force_bytes from coreapi.compat import force_bytes
from django.utils.safestring import SafeData, SafeText
from ruamel import yaml from ruamel import yaml
from . import openapi from . import openapi

View File

@ -5,7 +5,6 @@ from collections import OrderedDict, defaultdict
import uritemplate import uritemplate
from coreapi.compat import urlparse from coreapi.compat import urlparse
from django.utils.encoding import force_text
from rest_framework import versioning from rest_framework import versioning
from rest_framework.compat import URLPattern, URLResolver, get_original_route 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 EndpointEnumerator as _EndpointEnumerator
@ -18,7 +17,7 @@ from .app_settings import swagger_settings
from .errors import SwaggerGenerationError from .errors import SwaggerGenerationError
from .inspectors.field import get_basic_type_info, get_queryset_field from .inspectors.field import get_basic_type_info, get_queryset_field
from .openapi import ReferenceResolver from .openapi import ReferenceResolver
from .utils import get_consumes, get_produces from .utils import force_real_str, get_consumes, get_produces
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -435,7 +434,7 @@ class OpenAPISchemaGenerator(object):
attrs['pattern'] = getattr(view_cls, 'lookup_value_regex', attrs.get('pattern', None)) attrs['pattern'] = getattr(view_cls, 'lookup_value_regex', attrs.get('pattern', None))
if model_field and getattr(model_field, 'help_text', False): if model_field and getattr(model_field, 'help_text', False):
description = force_text(model_field.help_text) description = model_field.help_text
elif model_field and getattr(model_field, 'primary_key', False): elif model_field and getattr(model_field, 'primary_key', False):
description = get_pk_description(model, model_field) description = get_pk_description(model, model_field)
else: else:
@ -443,7 +442,7 @@ class OpenAPISchemaGenerator(object):
field = openapi.Parameter( field = openapi.Parameter(
name=variable, name=variable,
description=description, description=force_real_str(description),
required=True, required=True,
in_=openapi.IN_PATH, in_=openapi.IN_PATH,
**attrs **attrs

View File

@ -1,12 +1,11 @@
import inspect import inspect
import logging import logging
from django.utils.encoding import force_text
from rest_framework import serializers from rest_framework import serializers
from rest_framework.utils import encoders, json from rest_framework.utils import encoders, json
from .. import openapi from .. import openapi
from ..utils import decimal_as_float, is_list_view from ..utils import decimal_as_float, force_real_str, is_list_view
#: Sentinel value that inspectors must return to signal that they do not know how to handle an object #: Sentinel value that inspectors must return to signal that they do not know how to handle an object
NotHandled = object() NotHandled = object()
@ -196,9 +195,9 @@ class FieldInspector(BaseInspector):
""" """
assert swagger_object_type in (openapi.Schema, openapi.Parameter, openapi.Items) assert swagger_object_type in (openapi.Schema, openapi.Parameter, openapi.Items)
assert not isinstance(field, openapi.SwaggerDict), "passed field is already a SwaggerDict object" assert not isinstance(field, openapi.SwaggerDict), "passed field is already a SwaggerDict object"
title = force_text(field.label) if field.label else None title = force_real_str(field.label) if field.label else None
title = title if swagger_object_type == openapi.Schema else None # only Schema has title title = title if swagger_object_type == openapi.Schema else None # only Schema has title
description = force_text(field.help_text) if field.help_text else None description = force_real_str(field.help_text) if field.help_text else None
description = description if swagger_object_type != openapi.Items else None # Items has no description either description = description if swagger_object_type != openapi.Items else None # Items has no description either
def SwaggerType(existing_object=None, **instance_kwargs): def SwaggerType(existing_object=None, **instance_kwargs):

View File

@ -3,6 +3,8 @@ from collections import OrderedDict
import coreschema import coreschema
from rest_framework.pagination import CursorPagination, LimitOffsetPagination, PageNumberPagination from rest_framework.pagination import CursorPagination, LimitOffsetPagination, PageNumberPagination
from drf_yasg.utils import force_real_str
from .. import openapi from .. import openapi
from .base import FilterInspector, PaginatorInspector from .base import FilterInspector, PaginatorInspector
@ -48,7 +50,7 @@ class CoreAPICompatInspector(PaginatorInspector, FilterInspector):
in_=location_to_in[field.location], in_=location_to_in[field.location],
type=coreapi_types.get(type(field.schema), openapi.TYPE_STRING), type=coreapi_types.get(type(field.schema), openapi.TYPE_STRING),
required=field.required, required=field.required,
description=field.schema.description if field.schema else None, description=force_real_str(field.schema.description) if field.schema else None,
) )

View File

@ -8,7 +8,7 @@ from rest_framework.status import is_success
from .. import openapi from .. import openapi
from ..errors import SwaggerGenerationError from ..errors import SwaggerGenerationError
from ..utils import ( from ..utils import (
force_serializer_instance, get_consumes, get_produces, guess_response_status, is_list_view, no_body, force_real_str, force_serializer_instance, get_consumes, get_produces, guess_response_status, is_list_view, no_body,
param_list_to_odict param_list_to_odict
) )
from .base import ViewInspector from .base import ViewInspector
@ -42,7 +42,7 @@ class SwaggerAutoSchema(ViewInspector):
return openapi.Operation( return openapi.Operation(
operation_id=operation_id, operation_id=operation_id,
description=description, description=force_real_str(description),
responses=responses, responses=responses,
parameters=parameters, parameters=parameters,
consumes=consumes, consumes=consumes,
@ -246,7 +246,7 @@ class SwaggerAutoSchema(ViewInspector):
for sc, serializer in response_serializers.items(): for sc, serializer in response_serializers.items():
if isinstance(serializer, str): if isinstance(serializer, str):
response = openapi.Response( response = openapi.Response(
description=serializer description=force_real_str(serializer)
) )
elif not serializer: elif not serializer:
continue continue

View File

@ -3,6 +3,7 @@ import logging
from collections import OrderedDict from collections import OrderedDict
from django.db import models from django.db import models
from django.utils.encoding import force_text
from rest_framework import serializers, status from rest_framework import serializers, status
from rest_framework.mixins import DestroyModelMixin, RetrieveModelMixin, UpdateModelMixin from rest_framework.mixins import DestroyModelMixin, RetrieveModelMixin, UpdateModelMixin
from rest_framework.request import is_form_media_type from rest_framework.request import is_form_media_type
@ -319,3 +320,17 @@ def get_serializer_ref_name(serializer):
if ref_name.endswith('Serializer'): if ref_name.endswith('Serializer'):
ref_name = ref_name[:-len('Serializer')] ref_name = ref_name[:-len('Serializer')]
return ref_name return ref_name
def force_real_str(s, encoding='utf-8', strings_only=False, errors='strict'):
"""
Force `s` into a ``str`` instance.
Fix for https://github.com/axnsan12/drf-yasg/issues/159
"""
if s is not None:
s = force_text(s, encoding, strings_only, errors)
if type(s) != str:
s = '' + s
return s

View File

@ -1,9 +1,10 @@
from django.db import models from django.db import models
from django.utils.safestring import mark_safe
class Identity(models.Model): class Identity(models.Model):
firstName = models.CharField(max_length=30, null=True) firstName = models.CharField(max_length=30, null=True)
lastName = models.CharField(max_length=30, null=True) lastName = models.CharField(max_length=30, null=True, help_text=mark_safe("<strong>Here's some HTML!</strong>"))
class Person(models.Model): class Person(models.Model):

View File

@ -1,3 +1,5 @@
from six import StringIO
import copy import copy
import json import json
import os import os
@ -9,7 +11,6 @@ from django.contrib.auth.models import User
from django.core.management import call_command from django.core.management import call_command
from rest_framework.test import APIRequestFactory from rest_framework.test import APIRequestFactory
from rest_framework.views import APIView from rest_framework.views import APIView
from six import StringIO
from drf_yasg import codecs, openapi from drf_yasg import codecs, openapi
from drf_yasg.codecs import yaml_sane_dump, yaml_sane_load from drf_yasg.codecs import yaml_sane_dump, yaml_sane_load

View File

@ -905,6 +905,7 @@ definitions:
minLength: 1 minLength: 1
lastName: lastName:
title: LastName title: LastName
description: <strong>Here's some HTML!</strong>
type: string type: string
maxLength: 30 maxLength: 30
minLength: 1 minLength: 1