Skip to content

Commit

Permalink
Fix: Remove required coreapi dependency. (#854)
Browse files Browse the repository at this point in the history
* Feature: Migrate to PyYAML for yaml generator.

Closes #833

* Chore: Update swagger ui and redoc dependencies.

* Fix: Remove required coreapi dependency.
  • Loading branch information
onegreyonewhite authored Jun 15, 2023
1 parent afea1bc commit a836b04
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 7 deletions.
2 changes: 0 additions & 2 deletions requirements/base.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
coreapi>=2.3.3
coreschema>=0.0.4
djangorestframework>=3.10.3
django>=2.2.16
pyyaml>=5.1
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def drf_yasg_setup(**kwargs):
install_requires=requirements,
extras_require={
'validation': requirements_validation,
'coreapi': ['coreapi>=2.3.3', 'coreschema>=0.0.4']
},
license='BSD License',
description='Automated generation of real Swagger/OpenAPI 2.0 schemas from Django Rest Framework code.',
Expand Down
84 changes: 80 additions & 4 deletions src/drf_yasg/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
import uritemplate
from django.urls import URLPattern, URLResolver
from rest_framework import versioning
from rest_framework.schemas import SchemaGenerator
from rest_framework.schemas.openapi import SchemaGenerator
from rest_framework.schemas.generators import EndpointEnumerator as _EndpointEnumerator
from rest_framework.schemas.generators import endpoint_ordering, get_pk_name
from rest_framework.schemas.utils import get_pk_description
from rest_framework.schemas.utils import get_pk_description, is_list_view
from rest_framework.settings import api_settings

from . import openapi
Expand All @@ -25,6 +25,24 @@
PATH_PARAMETER_RE = re.compile(r'{(?P<parameter>\w+)}')


def common_path(paths):
split_paths = [path.strip('/').split('/') for path in paths]
s1 = min(split_paths)
s2 = max(split_paths)
common = s1
for i, c in enumerate(s1):
if c != s2[i]:
common = s1[:i]
break
return '/' + '/'.join(common)


def is_custom_action(action):
return action not in {
'retrieve', 'list', 'create', 'update', 'partial_update', 'destroy'
}


class EndpointEnumerator(_EndpointEnumerator):
def __init__(self, patterns=None, urlconf=None, request=None):
super(EndpointEnumerator, self).__init__(patterns, urlconf)
Expand Down Expand Up @@ -163,6 +181,15 @@ class OpenAPISchemaGenerator:
endpoint_enumerator_class = EndpointEnumerator
reference_resolver_class = ReferenceResolver

# Map HTTP methods onto actions.
default_mapping = {
'get': 'retrieve',
'post': 'create',
'put': 'update',
'patch': 'partial_update',
'delete': 'destroy',
}

def __init__(self, info, version='', url=None, patterns=None, urlconf=None):
"""
Expand All @@ -185,6 +212,7 @@ def __init__(self, info, version='', url=None, patterns=None, urlconf=None):
self.version = version
self.consumes = []
self.produces = []
self.coerce_method_names = api_settings.SCHEMA_COERCE_METHOD_NAMES

if url is None and swagger_settings.DEFAULT_API_URL is not None:
url = swagger_settings.DEFAULT_API_URL
Expand Down Expand Up @@ -337,7 +365,41 @@ def get_operation_keys(self, subpath, method, view):
:param view: the view associated with the operation
:rtype: list[str]
"""
return self._gen.get_keys(subpath, method, view)
if hasattr(view, 'action'):
# Viewsets have explicitly named actions.
action = view.action
else:
# Views have no associated action, so we determine one from the method.
if is_list_view(subpath, method, view):
action = 'list'
else:
action = self.default_mapping[method.lower()]

named_path_components = [
component for component
in subpath.strip('/').split('/')
if '{' not in component
]

if is_custom_action(action):
# Custom action, eg "/users/{pk}/activate/", "/users/active/"
mapped_methods = {
# Don't count head mapping, e.g. not part of the schema
method for method in view.action_map if method != 'head'
}
if len(mapped_methods) > 1:
action = self.default_mapping[method.lower()]
if action in self.coerce_method_names:
action = self.coerce_method_names[action]
return named_path_components + [action]
else:
return named_path_components[:-1] + [action]

if action in self.coerce_method_names:
action = self.coerce_method_names[action]

# Default action, eg "/users/", "/users/{pk}/"
return named_path_components + [action]

def determine_path_prefix(self, paths):
"""
Expand All @@ -357,7 +419,21 @@ def determine_path_prefix(self, paths):
:param list[str] paths: list of paths
:rtype: str
"""
return self._gen.determine_path_prefix(paths)
prefixes = []
for path in paths:
components = path.strip('/').split('/')
initial_components = []
for component in components:
if '{' in component:
break
initial_components.append(component)
prefix = '/'.join(initial_components[:-1])
if not prefix:
# We can just break early in the case that there's at least
# one URL that doesn't have a path prefix.
return '/'
prefixes.append('/' + prefix + '/')
return common_path(prefixes)

def should_include_endpoint(self, path, method, view, public):
"""Check if a given endpoint should be included in the resulting schema.
Expand Down
5 changes: 4 additions & 1 deletion src/drf_yasg/inspectors/query.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from collections import OrderedDict

import coreschema
try:
import coreschema
except ImportError:
coreschema = None
from rest_framework.pagination import CursorPagination, LimitOffsetPagination, PageNumberPagination

from .. import openapi
Expand Down
2 changes: 2 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ deps =
# other dependencies
-r requirements/validation.txt
-r requirements/test.txt
coreapi>=2.3.3
coreschema>=0.0.4

commands =
pytest -n 2 --cov --cov-config .coveragerc --cov-append --cov-report="" {posargs}
Expand Down

0 comments on commit a836b04

Please sign in to comment.