sc-oa 0.7.0.17__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sc_oa-0.7.0.17.dist-info/METADATA +35 -0
- sc_oa-0.7.0.17.dist-info/RECORD +26 -0
- sc_oa-0.7.0.17.dist-info/WHEEL +5 -0
- sc_oa-0.7.0.17.dist-info/licenses/LICENSE +23 -0
- sc_oa-0.7.0.17.dist-info/top_level.txt +1 -0
- sphinxcontrib/__init__.py +10 -0
- sphinxcontrib/openapi/__init__.py +93 -0
- sphinxcontrib/openapi/__main__.py +86 -0
- sphinxcontrib/openapi/_lib2to3.py +378 -0
- sphinxcontrib/openapi/directive.py +58 -0
- sphinxcontrib/openapi/locale/es_ES/LC_MESSAGES/openapi.mo +0 -0
- sphinxcontrib/openapi/locale/es_ES/LC_MESSAGES/openapi.po +170 -0
- sphinxcontrib/openapi/locale/fr_FR/LC_MESSAGES/openapi.mo +0 -0
- sphinxcontrib/openapi/locale/fr_FR/LC_MESSAGES/openapi.po +170 -0
- sphinxcontrib/openapi/locale/openapi.pot +168 -0
- sphinxcontrib/openapi/openapi20.py +263 -0
- sphinxcontrib/openapi/openapi30.py +750 -0
- sphinxcontrib/openapi/renderers/__init__.py +18 -0
- sphinxcontrib/openapi/renderers/_description.py +26 -0
- sphinxcontrib/openapi/renderers/_httpdomain.py +620 -0
- sphinxcontrib/openapi/renderers/_httpdomain_old.py +59 -0
- sphinxcontrib/openapi/renderers/_model.py +426 -0
- sphinxcontrib/openapi/renderers/_toc.py +48 -0
- sphinxcontrib/openapi/renderers/abc.py +46 -0
- sphinxcontrib/openapi/schema_utils.py +137 -0
- sphinxcontrib/openapi/utils.py +137 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""OpenAPI schema utility functions."""
|
|
2
|
+
|
|
3
|
+
from io import StringIO
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
_DEFAULT_EXAMPLES = {
|
|
7
|
+
"string": "string",
|
|
8
|
+
"integer": 1,
|
|
9
|
+
"number": 1.0,
|
|
10
|
+
"boolean": True,
|
|
11
|
+
"array": [],
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
_DEFAULT_STRING_EXAMPLES = {
|
|
16
|
+
"date": "2020-01-01",
|
|
17
|
+
"date-time": "2020-01-01T01:01:01Z",
|
|
18
|
+
"password": "********",
|
|
19
|
+
"byte": "QG1pY2hhZWxncmFoYW1ldmFucw==",
|
|
20
|
+
"ipv4": "127.0.0.1",
|
|
21
|
+
"ipv6": "::1",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def example_from_schema(schema):
|
|
26
|
+
"""
|
|
27
|
+
Generates an example request/response body from the provided schema.
|
|
28
|
+
|
|
29
|
+
>>> schema = {
|
|
30
|
+
... "type": "object",
|
|
31
|
+
... "required": ["id", "name"],
|
|
32
|
+
... "properties": {
|
|
33
|
+
... "id": {
|
|
34
|
+
... "type": "integer",
|
|
35
|
+
... "format": "int64"
|
|
36
|
+
... },
|
|
37
|
+
... "name": {
|
|
38
|
+
... "type": "string",
|
|
39
|
+
... "example": "John Smith"
|
|
40
|
+
... },
|
|
41
|
+
... "tag": {
|
|
42
|
+
... "type": "string"
|
|
43
|
+
... }
|
|
44
|
+
... }
|
|
45
|
+
... }
|
|
46
|
+
>>> example = example_from_schema(schema)
|
|
47
|
+
>>> assert example == {
|
|
48
|
+
... "id": 1,
|
|
49
|
+
... "name": "John Smith",
|
|
50
|
+
... "tag": "string"
|
|
51
|
+
... }
|
|
52
|
+
"""
|
|
53
|
+
# If an example was provided then we use that
|
|
54
|
+
if "example" in schema:
|
|
55
|
+
return schema["example"]
|
|
56
|
+
|
|
57
|
+
elif "oneOf" in schema:
|
|
58
|
+
return example_from_schema(schema["oneOf"][0])
|
|
59
|
+
|
|
60
|
+
elif "anyOf" in schema:
|
|
61
|
+
return example_from_schema(schema["anyOf"][0])
|
|
62
|
+
|
|
63
|
+
elif "allOf" in schema:
|
|
64
|
+
# Combine schema examples
|
|
65
|
+
example = {}
|
|
66
|
+
for sub_schema in schema["allOf"]:
|
|
67
|
+
example.update(example_from_schema(sub_schema))
|
|
68
|
+
return example
|
|
69
|
+
|
|
70
|
+
elif "enum" in schema:
|
|
71
|
+
return schema["enum"][0]
|
|
72
|
+
|
|
73
|
+
elif "type" not in schema:
|
|
74
|
+
# Any type
|
|
75
|
+
return _DEFAULT_EXAMPLES["integer"]
|
|
76
|
+
|
|
77
|
+
elif schema["type"] == "object" or "properties" in schema:
|
|
78
|
+
example = {}
|
|
79
|
+
for prop, prop_schema in schema.get("properties", {}).items():
|
|
80
|
+
example[prop] = example_from_schema(prop_schema)
|
|
81
|
+
return example
|
|
82
|
+
|
|
83
|
+
elif schema["type"] == "array":
|
|
84
|
+
items = schema["items"]
|
|
85
|
+
min_length = schema.get("minItems", 0)
|
|
86
|
+
max_length = schema.get("maxItems", max(min_length, 2))
|
|
87
|
+
assert min_length <= max_length
|
|
88
|
+
# Try generate at least 2 example array items
|
|
89
|
+
gen_length = min(2, max_length) if min_length <= 2 else min_length
|
|
90
|
+
|
|
91
|
+
example_items = []
|
|
92
|
+
if items == {}:
|
|
93
|
+
# Any-type arrays
|
|
94
|
+
example_items.extend(_DEFAULT_EXAMPLES.values())
|
|
95
|
+
elif isinstance(items, dict) and "oneOf" in items:
|
|
96
|
+
# Mixed-type arrays
|
|
97
|
+
example_items.append(_DEFAULT_EXAMPLES[sorted(items["oneOf"])[0]])
|
|
98
|
+
else:
|
|
99
|
+
example_items.append(example_from_schema(items))
|
|
100
|
+
|
|
101
|
+
# Generate array containing example_items and satisfying min_length and max_length
|
|
102
|
+
return [example_items[i % len(example_items)] for i in range(gen_length)]
|
|
103
|
+
|
|
104
|
+
elif schema["type"] == "string":
|
|
105
|
+
example_string = _DEFAULT_STRING_EXAMPLES.get(
|
|
106
|
+
schema.get("format", None), _DEFAULT_EXAMPLES["string"]
|
|
107
|
+
)
|
|
108
|
+
min_length = schema.get("minLength", 0)
|
|
109
|
+
max_length = schema.get("maxLength", max(min_length, len(example_string)))
|
|
110
|
+
gen_length = (
|
|
111
|
+
min(len(example_string), max_length)
|
|
112
|
+
if min_length <= len(example_string)
|
|
113
|
+
else min_length
|
|
114
|
+
)
|
|
115
|
+
assert 0 <= min_length <= max_length
|
|
116
|
+
if min_length <= len(example_string) <= max_length:
|
|
117
|
+
return example_string
|
|
118
|
+
else:
|
|
119
|
+
example_builder = StringIO()
|
|
120
|
+
for i in range(gen_length):
|
|
121
|
+
example_builder.write(example_string[i % len(example_string)])
|
|
122
|
+
example_builder.seek(0)
|
|
123
|
+
return example_builder.read()
|
|
124
|
+
|
|
125
|
+
elif schema["type"] in ("integer", "number"):
|
|
126
|
+
example = _DEFAULT_EXAMPLES[schema["type"]]
|
|
127
|
+
if "minimum" in schema and "maximum" in schema:
|
|
128
|
+
# Take average
|
|
129
|
+
example = schema["minimum"] + (schema["maximum"] - schema["minimum"]) / 2
|
|
130
|
+
elif "minimum" in schema and example <= schema["minimum"]:
|
|
131
|
+
example = schema["minimum"] + 1
|
|
132
|
+
elif "maximum" in schema and example >= schema["maximum"]:
|
|
133
|
+
example = schema["maximum"] - 1
|
|
134
|
+
return float(example) if schema["type"] == "number" else int(example)
|
|
135
|
+
|
|
136
|
+
else:
|
|
137
|
+
return _DEFAULT_EXAMPLES[schema["type"]]
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""
|
|
2
|
+
sphinxcontrib.openapi.utils
|
|
3
|
+
---------------------------
|
|
4
|
+
|
|
5
|
+
Common functionality shared across the various renderers.
|
|
6
|
+
|
|
7
|
+
:copyright: (c) 2016, Ihor Kalnytskyi.
|
|
8
|
+
:license: BSD, see LICENSE for details.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import unicode_literals
|
|
12
|
+
|
|
13
|
+
import collections
|
|
14
|
+
import collections.abc
|
|
15
|
+
|
|
16
|
+
from contextlib import closing
|
|
17
|
+
import jsonschema
|
|
18
|
+
import yaml
|
|
19
|
+
try:
|
|
20
|
+
from m2r2 import convert as convert_markdown
|
|
21
|
+
except ImportError:
|
|
22
|
+
convert_markdown = None
|
|
23
|
+
|
|
24
|
+
from urllib.parse import urlsplit
|
|
25
|
+
from urllib.request import urlopen
|
|
26
|
+
|
|
27
|
+
import os.path
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class OpenApiRefResolver(jsonschema.RefResolver):
|
|
31
|
+
"""
|
|
32
|
+
Overrides resolve_remote to support both YAML and JSON
|
|
33
|
+
OpenAPI schemas.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
import requests
|
|
38
|
+
_requests = requests
|
|
39
|
+
except ImportError:
|
|
40
|
+
_requests = None
|
|
41
|
+
|
|
42
|
+
def resolve_remote(self, uri):
|
|
43
|
+
scheme, _, path, _, _ = urlsplit(uri)
|
|
44
|
+
_, extension = os.path.splitext(path)
|
|
45
|
+
|
|
46
|
+
if extension not in [".yml", ".yaml"] or scheme in self.handlers:
|
|
47
|
+
return super(OpenApiRefResolver, self).resolve_remote(uri)
|
|
48
|
+
|
|
49
|
+
if scheme in [u"http", u"https"] and self._requests:
|
|
50
|
+
response = self._requests.get(uri)
|
|
51
|
+
result = yaml.safe_load(response)
|
|
52
|
+
else:
|
|
53
|
+
# Otherwise, pass off to urllib and assume utf-8
|
|
54
|
+
with closing(urlopen(uri)) as url:
|
|
55
|
+
response = url.read().decode("utf-8")
|
|
56
|
+
result = yaml.safe_load(response)
|
|
57
|
+
|
|
58
|
+
if self.cache_remote:
|
|
59
|
+
self.store[uri] = result
|
|
60
|
+
return result
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _resolve_refs(uri, spec):
|
|
64
|
+
"""Resolve JSON references in a given dictionary.
|
|
65
|
+
|
|
66
|
+
OpenAPI spec may contain JSON references to its nodes or external
|
|
67
|
+
sources, so any attempt to rely that there's some expected attribute
|
|
68
|
+
in the spec may fail. So we need to resolve JSON references before
|
|
69
|
+
we use it (i.e. replace with referenced object). For details see:
|
|
70
|
+
|
|
71
|
+
https://tools.ietf.org/html/draft-pbryan-zyp-json-ref-02
|
|
72
|
+
|
|
73
|
+
The input spec is modified in-place despite being returned from
|
|
74
|
+
the function.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
resolver = OpenApiRefResolver(uri, spec)
|
|
78
|
+
|
|
79
|
+
def _do_resolve(node):
|
|
80
|
+
if isinstance(node, collections.abc.Mapping) and '$ref' in node:
|
|
81
|
+
ref = node['$ref']
|
|
82
|
+
with resolver.resolving(node['$ref']) as resolved:
|
|
83
|
+
ret = _do_resolve(resolved) # might have recursive references
|
|
84
|
+
# Restore the $ref in case we want to
|
|
85
|
+
# separate the entities in the document
|
|
86
|
+
if isinstance(ret, collections.abc.Mapping):
|
|
87
|
+
ret['$entity_ref'] = ref
|
|
88
|
+
return ret
|
|
89
|
+
elif isinstance(node, collections.abc.Mapping):
|
|
90
|
+
for k, v in node.items():
|
|
91
|
+
node[k] = _do_resolve(v)
|
|
92
|
+
elif isinstance(node, (list, tuple)):
|
|
93
|
+
for i in range(len(node)):
|
|
94
|
+
node[i] = _do_resolve(node[i])
|
|
95
|
+
return node
|
|
96
|
+
|
|
97
|
+
return _do_resolve(spec)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def normalize_spec(spec, **options):
|
|
101
|
+
# OpenAPI spec may contain JSON references, so we need resolve them
|
|
102
|
+
# before we access the actual values trying to build an httpdomain
|
|
103
|
+
# markup. Since JSON references may be relative, it's crucial to
|
|
104
|
+
# pass a document URI in order to properly resolve them.
|
|
105
|
+
spec = _resolve_refs(options.get('uri', ''), spec)
|
|
106
|
+
|
|
107
|
+
# OpenAPI spec may contain common endpoint's parameters top-level.
|
|
108
|
+
# In order to do not place if-s around the code to handle special
|
|
109
|
+
# cases, let's normalize the spec and push common parameters inside
|
|
110
|
+
# endpoints definitions.
|
|
111
|
+
for endpoint in spec['paths'].values():
|
|
112
|
+
parameters = endpoint.pop('parameters', [])
|
|
113
|
+
for method in endpoint.values():
|
|
114
|
+
method.setdefault('parameters', [])
|
|
115
|
+
method['parameters'].extend(parameters)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _conv_md(s):
|
|
119
|
+
try:
|
|
120
|
+
return convert_markdown(s)
|
|
121
|
+
except Exception:
|
|
122
|
+
return s
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def get_text_converter(options):
|
|
126
|
+
"""Decide on a text converter for prose."""
|
|
127
|
+
if 'format' in options:
|
|
128
|
+
if options['format'] == 'markdown':
|
|
129
|
+
if convert_markdown is None:
|
|
130
|
+
raise ValueError(
|
|
131
|
+
"Markdown conversion isn't available, "
|
|
132
|
+
"install the [markdown] extra."
|
|
133
|
+
)
|
|
134
|
+
return _conv_md
|
|
135
|
+
|
|
136
|
+
# No conversion needed.
|
|
137
|
+
return lambda s: s
|