Skip to content

Commit

Permalink
improve docstring extraction #96
Browse files Browse the repository at this point in the history
  • Loading branch information
tfranzel committed Jun 19, 2020
1 parent c32793c commit 57ecb4a
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 7 deletions.
11 changes: 5 additions & 6 deletions drf_spectacular/openapi.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import inspect
import re
import typing
from operator import attrgetter
Expand All @@ -23,8 +22,8 @@
from drf_spectacular.plumbing import (
ComponentRegistry, ResolvedComponent, anyisinstance, append_meta, build_array_type,
build_basic_type, build_choice_field, build_object_type, build_parameter_type, error,
follow_field_source, force_instance, get_override, has_override, is_basic_type, is_field,
is_serializer, resolve_regex_path_parameter, safe_ref, warn,
follow_field_source, force_instance, get_doc, get_override, has_override, is_basic_type,
is_field, is_serializer, resolve_regex_path_parameter, safe_ref, warn,
)
from drf_spectacular.settings import spectacular_settings
from drf_spectacular.types import OpenApiTypes
Expand Down Expand Up @@ -171,8 +170,8 @@ def dict_helper(parameters):
def get_description(self):
""" override this for custom behaviour """
action_or_method = getattr(self.view, getattr(self.view, 'action', self.method.lower()), None)
view_doc = inspect.getdoc(self.view) or ''
action_doc = inspect.getdoc(action_or_method) or ''
view_doc = get_doc(self.view.__class__)
action_doc = get_doc(action_or_method)
return action_doc or view_doc

def get_summary(self):
Expand Down Expand Up @@ -645,7 +644,7 @@ def _map_basic_serializer(self, serializer, direction):
return build_object_type(
properties=properties,
required=required,
description=inspect.getdoc(serializer),
description=get_doc(serializer.__class__),
)

def _map_field_validators(self, field, schema):
Expand Down
38 changes: 37 additions & 1 deletion drf_spectacular/plumbing.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
from django import __version__ as DJANGO_VERSION
from django.urls.resolvers import _PATH_PARAMETER_COMPONENT_RE, get_resolver # type: ignore
from django.utils.module_loading import import_string
from rest_framework import exceptions, fields, serializers, versioning
from rest_framework import (
exceptions, fields, generics, mixins, serializers, versioning, views, viewsets,
)
from uritemplate import URITemplate

from drf_spectacular.settings import spectacular_settings
Expand Down Expand Up @@ -131,6 +133,40 @@ def get_override(obj, prop):
return obj._spectacular_annotation[prop]


def get_doc(obj):
""" get doc string with fallback on obj's base classes (ignoring DRF documentation). """
if not inspect.isclass(obj):
return inspect.getdoc(obj) or ''

def safe_index(lst, item):
try:
return lst.index(item)
except ValueError:
return float("inf")

lib_doc_excludes = [
serializers.Serializer,
serializers.ModelSerializer,
serializers.HyperlinkedModelSerializer,
viewsets.ModelViewSet,
viewsets.GenericViewSet,
viewsets.ViewSet,
viewsets.ReadOnlyModelViewSet,
generics.GenericAPIView,
mixins.ListModelMixin,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
views.APIView,
]
lib_barrier = min(safe_index(obj.__mro__, c) for c in lib_doc_excludes)
for cls in obj.__mro__[:lib_barrier]:
if cls.__doc__:
return inspect.cleandoc(cls.__doc__)
return ''


def build_basic_type(obj):
"""
resolve either enum or actual type and yield schema template for modification
Expand Down
3 changes: 3 additions & 0 deletions tests/test_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class XViewset(mixins.ListModelMixin, viewsets.GenericViewSet):
class XView(APIView):
""" underspecified library view """
def get(self):
""" docstring for GET """
return Response(1.234) # pragma: no cover


Expand All @@ -53,6 +54,7 @@ def get(self, request):
schema = generate_schema('x', view=XView)
operation = schema['paths']['/x']['get']
assert get_response_schema(operation)['type'] == 'number'
assert operation['description'].strip() == 'docstring for GET'


@api_view()
Expand All @@ -72,3 +74,4 @@ def view_replacement(self):
schema = generate_schema('x', view_function=x_view_function)
operation = schema['paths']['/x']['get']
assert get_response_schema(operation)['type'] == 'number'
assert operation['description'].strip() == 'underspecified library view'

0 comments on commit 57ecb4a

Please sign in to comment.