drf-to-mkdoc 0.1.3__py3-none-any.whl → 0.1.5__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.
Potentially problematic release.
This version of drf-to-mkdoc might be problematic. Click here for more details.
- drf_to_mkdoc/management/commands/build_docs.py +6 -15
- drf_to_mkdoc/utils/common.py +67 -13
- {drf_to_mkdoc-0.1.3.dist-info → drf_to_mkdoc-0.1.5.dist-info}/METADATA +1 -1
- {drf_to_mkdoc-0.1.3.dist-info → drf_to_mkdoc-0.1.5.dist-info}/RECORD +7 -7
- {drf_to_mkdoc-0.1.3.dist-info → drf_to_mkdoc-0.1.5.dist-info}/WHEEL +0 -0
- {drf_to_mkdoc-0.1.3.dist-info → drf_to_mkdoc-0.1.5.dist-info}/licenses/LICENSE +0 -0
- {drf_to_mkdoc-0.1.3.dist-info → drf_to_mkdoc-0.1.5.dist-info}/top_level.txt +0 -0
|
@@ -35,22 +35,15 @@ class Command(BaseCommand):
|
|
|
35
35
|
# Generate the model documentation JSON first
|
|
36
36
|
self.stdout.write("Generating model documentation...")
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
self.stdout.write(self.style.SUCCESS("Model documentation generated."))
|
|
43
|
-
except Exception as e:
|
|
44
|
-
self.stdout.write(self.style.WARNING(f"Failed to generate model docs: {e}"))
|
|
38
|
+
call_command(
|
|
39
|
+
"generate_model_docs", "--pretty"
|
|
40
|
+
)
|
|
41
|
+
self.stdout.write(self.style.SUCCESS("Model documentation generated."))
|
|
45
42
|
|
|
46
43
|
# Generate the documentation content
|
|
47
44
|
self.stdout.write("Generating documentation content...")
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
self.stdout.write(self.style.SUCCESS("Documentation content generated."))
|
|
51
|
-
except Exception as e:
|
|
52
|
-
self.stdout.write(self.style.ERROR(f"Failed to generate docs: {e}"))
|
|
53
|
-
raise
|
|
45
|
+
call_command("generate_docs")
|
|
46
|
+
self.stdout.write(self.style.SUCCESS("Documentation content generated."))
|
|
54
47
|
|
|
55
48
|
# Build the MkDocs site
|
|
56
49
|
self.stdout.write("Building MkDocs site...")
|
|
@@ -72,5 +65,3 @@ class Command(BaseCommand):
|
|
|
72
65
|
raise CommandError(
|
|
73
66
|
"MkDocs not found. Please install it with: pip install mkdocs mkdocs-material"
|
|
74
67
|
) from e
|
|
75
|
-
except Exception as e:
|
|
76
|
-
raise CommandError(f"Error building documentation: {e!s}") from e
|
drf_to_mkdoc/utils/common.py
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
+
from asyncio.log import logger
|
|
1
2
|
import importlib
|
|
2
3
|
import yaml
|
|
3
4
|
import json
|
|
4
5
|
import re
|
|
5
6
|
from functools import lru_cache
|
|
6
7
|
from pathlib import Path
|
|
7
|
-
from typing import Any
|
|
8
|
+
from typing import Any, Optional
|
|
8
9
|
|
|
9
10
|
from django.apps import apps
|
|
10
11
|
from django.core.exceptions import AppRegistryNotReady
|
|
11
12
|
from django.urls import resolve
|
|
13
|
+
from django.utils.module_loading import import_string
|
|
12
14
|
from drf_spectacular.generators import SchemaGenerator
|
|
13
15
|
from drf_to_mkdoc.conf.settings import drf_to_mkdoc_settings
|
|
14
16
|
|
|
@@ -24,13 +26,20 @@ class QueryParamTypeError(Exception):
|
|
|
24
26
|
pass
|
|
25
27
|
|
|
26
28
|
|
|
27
|
-
def substitute_path_params(path: str) -> str:
|
|
28
|
-
|
|
29
|
-
path = path.replace("{", "<").replace("}", ">")
|
|
30
|
-
return re.sub(r"<[^>]+>", "1", path)
|
|
29
|
+
def substitute_path_params(path: str, parameters: list[dict[str, Any]]) -> str:
|
|
30
|
+
django_path = convert_to_django_path(path, parameters)
|
|
31
31
|
|
|
32
|
+
django_path = re.compile(r"\{[^}]+\}").sub("1", django_path)
|
|
33
|
+
django_path = re.sub(r"<int:[^>]+>", "1", django_path)
|
|
34
|
+
django_path = re.sub(r"<uuid:[^>]+>", "12345678-1234-5678-9abc-123456789012", django_path)
|
|
35
|
+
django_path = re.sub(r"<float:[^>]+>", "1.0", django_path)
|
|
36
|
+
django_path = re.sub(r"<(?:string|str):[^>]+>", "dummy", django_path)
|
|
37
|
+
django_path = re.sub(r"<path:[^>]+>", "dummy/path", django_path)
|
|
38
|
+
django_path = re.sub(r"<[^:>]+>", "dummy", django_path) # Catch remaining simple params
|
|
32
39
|
|
|
33
|
-
|
|
40
|
+
return django_path
|
|
41
|
+
|
|
42
|
+
def load_schema() -> Optional[dict[str, Any]]:
|
|
34
43
|
"""Load the OpenAPI schema from doc-schema.yaml"""
|
|
35
44
|
schema_file = Path(drf_to_mkdoc_settings.CONFIG_DIR) / "doc-schema.yaml"
|
|
36
45
|
if not schema_file.exists():
|
|
@@ -40,7 +49,7 @@ def load_schema() -> dict[str, Any] | None:
|
|
|
40
49
|
return yaml.safe_load(f)
|
|
41
50
|
|
|
42
51
|
|
|
43
|
-
def load_model_json_data() -> dict[str, Any]
|
|
52
|
+
def load_model_json_data() -> Optional[dict[str, Any]]:
|
|
44
53
|
"""Load the JSON mapping data for model information"""
|
|
45
54
|
json_file = Path(drf_to_mkdoc_settings.MODEL_DOCS_FILE)
|
|
46
55
|
if not json_file.exists():
|
|
@@ -50,7 +59,7 @@ def load_model_json_data() -> dict[str, Any] | None:
|
|
|
50
59
|
return json.load(f)
|
|
51
60
|
|
|
52
61
|
|
|
53
|
-
def load_doc_config() -> dict[str, Any]
|
|
62
|
+
def load_doc_config() -> Optional[dict[str, Any]]:
|
|
54
63
|
"""Load the documentation configuration file"""
|
|
55
64
|
config_file = Path(drf_to_mkdoc_settings.DOC_CONFIG_FILE)
|
|
56
65
|
if not config_file.exists():
|
|
@@ -60,7 +69,7 @@ def load_doc_config() -> dict[str, Any] | None:
|
|
|
60
69
|
return json.load(f)
|
|
61
70
|
|
|
62
71
|
|
|
63
|
-
def get_model_docstring(class_name: str) -> str
|
|
72
|
+
def get_model_docstring(class_name: str) -> Optional[str]:
|
|
64
73
|
"""Extract docstring from Django model class"""
|
|
65
74
|
try:
|
|
66
75
|
# Check if Django is properly initialized
|
|
@@ -158,6 +167,51 @@ def get_custom_schema():
|
|
|
158
167
|
raise QueryParamTypeError("Invalid queryparam_type")
|
|
159
168
|
return data
|
|
160
169
|
|
|
170
|
+
def convert_to_django_path(path: str, parameters: list[dict[str, Any]]) -> str:
|
|
171
|
+
"""
|
|
172
|
+
Convert a path with {param} to a Django-style path with <type:param>.
|
|
173
|
+
If DRF_TO_MKDOC_PATH_PARAM_SUBSTITUTOR is set, use that function instead.
|
|
174
|
+
"""
|
|
175
|
+
function = None
|
|
176
|
+
func_path = getattr(drf_to_mkdoc_settings, "DRF_TO_MKDOC_PATH_PARAM_SUBSTITUTOR", None)
|
|
177
|
+
|
|
178
|
+
if func_path:
|
|
179
|
+
try:
|
|
180
|
+
function = import_string(func_path)
|
|
181
|
+
except ImportError:
|
|
182
|
+
logger.warning("DRF_TO_MKDOC_PATH_PARAM_SUBSTITUTOR is not a valid import path")
|
|
183
|
+
|
|
184
|
+
# If custom function exists and returns a valid value, use it
|
|
185
|
+
if callable(function):
|
|
186
|
+
try:
|
|
187
|
+
value = function(path, parameters)
|
|
188
|
+
if isinstance(value, str) and value.strip():
|
|
189
|
+
return value
|
|
190
|
+
except Exception as e:
|
|
191
|
+
logger.exception("Error in custom path substitutor: %s", e)
|
|
192
|
+
|
|
193
|
+
# Default Django path conversion
|
|
194
|
+
def replacement(match):
|
|
195
|
+
param_name = match.group(1)
|
|
196
|
+
param_info = next((p for p in parameters if p.get('name') == param_name), {})
|
|
197
|
+
param_type = param_info.get('schema', {}).get('type')
|
|
198
|
+
param_format = param_info.get('schema', {}).get('format')
|
|
199
|
+
|
|
200
|
+
if param_type == 'integer':
|
|
201
|
+
converter = 'int'
|
|
202
|
+
elif param_type == 'string' and param_format == 'uuid':
|
|
203
|
+
converter = 'uuid'
|
|
204
|
+
else:
|
|
205
|
+
converter = 'str'
|
|
206
|
+
|
|
207
|
+
return f'<{converter}:{param_name}>'
|
|
208
|
+
|
|
209
|
+
django_path = re.sub(r'{(\w+)}', replacement, path)
|
|
210
|
+
|
|
211
|
+
if not django_path.endswith('/'):
|
|
212
|
+
django_path += '/'
|
|
213
|
+
|
|
214
|
+
return django_path
|
|
161
215
|
|
|
162
216
|
@lru_cache
|
|
163
217
|
def get_schema():
|
|
@@ -209,7 +263,7 @@ def get_operation_id_path_map() -> dict[str, str]:
|
|
|
209
263
|
for _http_method_name, action_data in actions.items():
|
|
210
264
|
operation_id = action_data.get("operationId")
|
|
211
265
|
if operation_id:
|
|
212
|
-
mapping[operation_id] = path
|
|
266
|
+
mapping[operation_id] = path, action_data.get("parameters", [])
|
|
213
267
|
|
|
214
268
|
return mapping
|
|
215
269
|
|
|
@@ -217,13 +271,13 @@ def get_operation_id_path_map() -> dict[str, str]:
|
|
|
217
271
|
def extract_viewset_from_operation_id(operation_id: str):
|
|
218
272
|
"""Extract the ViewSet class from an OpenAPI operation ID."""
|
|
219
273
|
operation_map = get_operation_id_path_map()
|
|
220
|
-
|
|
274
|
+
path, parameters = operation_map.get(operation_id)
|
|
221
275
|
|
|
222
|
-
if not
|
|
276
|
+
if not path:
|
|
223
277
|
raise ValueError(f"Path not found for operation ID: {operation_id}")
|
|
224
278
|
|
|
225
279
|
try:
|
|
226
|
-
resolved_path = substitute_path_params(
|
|
280
|
+
resolved_path = substitute_path_params(path, parameters)
|
|
227
281
|
match = resolve(resolved_path)
|
|
228
282
|
view_func = match.func
|
|
229
283
|
if hasattr(view_func, "view_class"):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: drf-to-mkdoc
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: Generate Markdown API docs from Django/DRF OpenAPI schema for MkDocs
|
|
5
5
|
Author-email: Hossein Shayesteh <shayestehhs1@gmail.com>
|
|
6
6
|
Maintainer-email: Hossein Shayesteh <shayestehhs1@gmail.com>
|
|
@@ -5,21 +5,21 @@ drf_to_mkdoc/conf/defaults.py,sha256=9OK65SeP4aLZbuRJBAE_QeC-OhXkh0cACBqax6wYXnM
|
|
|
5
5
|
drf_to_mkdoc/conf/settings.py,sha256=OgB3MCn4Z5F4xqWP34kwzMs50kRn3qF0gE1zS2SHS2M,1550
|
|
6
6
|
drf_to_mkdoc/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
7
|
drf_to_mkdoc/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
drf_to_mkdoc/management/commands/build_docs.py,sha256=
|
|
8
|
+
drf_to_mkdoc/management/commands/build_docs.py,sha256=p--TajkpYU-hmthXp5eVmVtJnXLxZrfQWQmuzwOgl0c,2491
|
|
9
9
|
drf_to_mkdoc/management/commands/generate_doc_json.py,sha256=mWdYgMbSeLP4iQZeUm2DxwYQmdGy8w05XTEfbT_nOJo,19833
|
|
10
10
|
drf_to_mkdoc/management/commands/generate_docs.py,sha256=YGdejd-b1Wn_e5ru9orwp1b9H5PZwVWkuWxAY1JyG88,4897
|
|
11
11
|
drf_to_mkdoc/management/commands/generate_model_docs.py,sha256=tdT9Z0qjZ9KgGAbFfYWBo-FtDI8wTQ2zRA_OvKKnyaA,12195
|
|
12
12
|
drf_to_mkdoc/management/commands/update_doc_schema.py,sha256=TtHVQxnVpB_VELRkVcdsDXDU5zXdguFleB1mXCDMAbg,2009
|
|
13
13
|
drf_to_mkdoc/utils/__init__.py,sha256=6dFTb07S6yIf-INMy0Mlgf5purNir687ZU9WZtITh4k,68
|
|
14
|
-
drf_to_mkdoc/utils/common.py,sha256=
|
|
14
|
+
drf_to_mkdoc/utils/common.py,sha256=BIzN9iDOphIHPGkRGj8iVhY8lAUdvBAyNazP3wDmz9c,11814
|
|
15
15
|
drf_to_mkdoc/utils/endpoint_generator.py,sha256=oGHQXJB5VFlGOq6W8a3q96CwF3conjBe_tkYj6m2mlg,35849
|
|
16
16
|
drf_to_mkdoc/utils/model_generator.py,sha256=O1ibaw7KmL_fQ1OTebuk6Tt2yTjyElpyF7bN8gk5LBE,9588
|
|
17
17
|
drf_to_mkdoc/utils/extractors/__init__.py,sha256=BvC8gKOPVI9gU1Piw0jRhKQ2ER5s1moAxgq7ZYkJWNI,86
|
|
18
18
|
drf_to_mkdoc/utils/extractors/query_parameter_extractors.py,sha256=e7WW0MeLUfBAfksEKFxowDjz9uUvit_EDxYASfnbdc4,8400
|
|
19
19
|
drf_to_mkdoc/utils/md_generators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
20
|
drf_to_mkdoc/utils/md_generators/query_parameters_generators.py,sha256=N-XqZ_FUODSR5V4xM9oEA3aaIiNGNmNwpvrWbQTx6RI,2566
|
|
21
|
-
drf_to_mkdoc-0.1.
|
|
22
|
-
drf_to_mkdoc-0.1.
|
|
23
|
-
drf_to_mkdoc-0.1.
|
|
24
|
-
drf_to_mkdoc-0.1.
|
|
25
|
-
drf_to_mkdoc-0.1.
|
|
21
|
+
drf_to_mkdoc-0.1.5.dist-info/licenses/LICENSE,sha256=3n9_ckIREsH8ogCxWW6dFsw_WfGcluG2mHcgl9i_UU0,1068
|
|
22
|
+
drf_to_mkdoc-0.1.5.dist-info/METADATA,sha256=qFgbvjEKTuW_DP6VlK3YsJZGIg1NYY1b35j5TdQ4YZU,7304
|
|
23
|
+
drf_to_mkdoc-0.1.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
24
|
+
drf_to_mkdoc-0.1.5.dist-info/top_level.txt,sha256=ZzJecR6j_tvLZiubUBEgawHflozC4DQy9ooNf1yDJ3Q,13
|
|
25
|
+
drf_to_mkdoc-0.1.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|