Skip to content

Commit 1cf36a4

Browse files
committed
Revert "Replace building of python client documentation with pydoc to mkdocstrings"
This reverts commit 6114f2c.
1 parent 36c55a7 commit 1cf36a4

File tree

8 files changed

+283
-81
lines changed

8 files changed

+283
-81
lines changed

tools/build_documentation.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@ mkdir -p "$SCRIPT_DIR"/../website/static/html/
3535

3636
cp -r "$AUTO_GENERATED"/rest_api_doc_spec.yaml "$SCRIPT_DIR"/../website/static/yaml/
3737
cp -r "$AUTO_GENERATED"/rest_api_doc.html "$SCRIPT_DIR"/../website/static/html/
38-
cp -r "$AUTO_GENERATED"/site "$SCRIPT_DIR"/../website/static/html/
38+
cp -r "$AUTO_GENERATED"/client_api "$DOCS"
3939
cp -r "$AUTO_GENERATED"/bin "$DOCS"
4040

41+
4142
echo "Generating Release Notes..."
4243
"$SCRIPT_DIR"/generate_release_notes.py
4344
echo "Generating Release Notes Index..."

tools/run_in_docker/generate_client_api_docs.sh

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,22 @@ set -e
44

55
echo "Generating the Client Api Docs..."
66

7-
#pip install --upgrade "pydoc-markdown>3" &> /dev/null
8-
pip install --upgrade mkdocs mkdocs-gen-files mkdocstrings-python mkdocs-material
7+
pip install --upgrade "pydoc-markdown>3" &> /dev/null
98

109
mkdir -p /auto_generated/client_api
10+
for f in rucio/lib/rucio/client/*.py; do
11+
if [[ $f =~ "__init__" ]]; then
12+
continue
13+
fi
1114

12-
mkdocs build --clean --no-directory-urls
15+
executable_name=$(basename "$f" ".py")
1316

14-
cp -r site /auto_generated/
17+
config="
18+
processors:
19+
- type: rucio_processor.RucioProcessor
20+
renderer:
21+
type: rucio_renderer.RucioRenderer"
22+
content=$(PYTHONPATH=. pydoc-markdown -I rucio/lib/rucio/client -m "$executable_name" "$config")
23+
24+
echo "$content" > /auto_generated/client_api/"$executable_name".md
25+
done

tools/run_in_docker/generate_client_api_pages.py

Lines changed: 0 additions & 24 deletions
This file was deleted.

tools/run_in_docker/mkdocs.yml

Lines changed: 0 additions & 51 deletions
This file was deleted.
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright (c) 2019 Niklas Rosenstein
3+
#
4+
# Permission is hereby granted, free of charge, to any person obtaining a copy
5+
# of this software and associated documentation files (the "Software"), to
6+
# deal in the Software without restriction, including without limitation the
7+
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8+
# sell copies of the Software, and to permit persons to whom the Software is
9+
# furnished to do so, subject to the following conditions:
10+
#
11+
# The above copyright notice and this permission notice shall be included in
12+
# all copies or substantial portions of the Software.
13+
#
14+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20+
# IN THE SOFTWARE.
21+
22+
import dataclasses
23+
import logging
24+
import typing as t
25+
26+
import docspec
27+
import docstring_parser
28+
from pydoc_markdown.interfaces import Processor, Resolver
29+
30+
logger = logging.getLogger(__name__)
31+
32+
33+
def sanitize(s: t.Optional[str]) -> str:
34+
if not s:
35+
return ""
36+
37+
character_map = {r"<": r"\<", r">": r"\>", r"{": r"\{", r"}": r"\}"}
38+
39+
for before, after in character_map.items():
40+
s = s.replace(before, after)
41+
return s
42+
43+
44+
@dataclasses.dataclass
45+
class _ParamLine:
46+
"""
47+
Helper data class for holding details of Sphinx arguments.
48+
"""
49+
50+
name: str
51+
docs: str
52+
type: t.Optional[str] = None
53+
54+
55+
def generate_sections_markdown(sections):
56+
ret = []
57+
58+
ret.append("<table style={{border: 'none'}}><tbody>\n")
59+
for key, section in sections.items():
60+
if section:
61+
ret.append("\n<tr style={{border: 'none'}}>\n")
62+
63+
ret.append(
64+
"\n<td style={{border: 'none', backgroundColor: 'var(--ifm-background-color)', 'verticalAlign': 'top'}}>\n" # noqa: E501
65+
)
66+
ret.append(f"**{key}**:")
67+
ret.append("\n</td>\n")
68+
69+
ret.append(
70+
"\n<td style={{border: 'none', backgroundColor: 'var(--ifm-background-color)', 'verticalAlign': 'top'}}>\n" # noqa: E501
71+
)
72+
ret.extend(section)
73+
ret.append("\n</td>\n")
74+
75+
ret.append("\n</tr>\n")
76+
ret.append("\n</tbody></table>\n")
77+
78+
return ret
79+
80+
81+
@dataclasses.dataclass
82+
class RucioProcessor(Processor):
83+
_KEYWORDS = {
84+
"Arguments": [
85+
"arg",
86+
"argument",
87+
"param",
88+
"parameter",
89+
"type",
90+
],
91+
"Returns": [
92+
"return",
93+
"returns",
94+
"rtype",
95+
],
96+
"Raises": [
97+
"raises",
98+
"raise",
99+
],
100+
}
101+
102+
def check_docstring_format(self, docstring: str) -> bool:
103+
return any(
104+
f":{k}" in docstring for _, value in self._KEYWORDS.items() for k in value
105+
)
106+
107+
def process(
108+
self, modules: t.List[docspec.Module], resolver: t.Optional[Resolver]
109+
) -> None:
110+
docspec.visit(modules, self._process)
111+
112+
def _convert_raises(
113+
self, raises: t.List[docstring_parser.common.DocstringRaises]
114+
) -> list:
115+
"""Convert a list of DocstringRaises from docstring_parser to markdown lines
116+
117+
:return: A list of markdown formatted lines
118+
"""
119+
converted_lines = []
120+
for entry in raises:
121+
converted_lines.append(
122+
"`{}`: {}\n".format(
123+
sanitize(entry.type_name), sanitize(entry.description)
124+
)
125+
)
126+
return converted_lines
127+
128+
def _convert_params(
129+
self, params: t.List[docstring_parser.common.DocstringParam]
130+
) -> list:
131+
"""Convert a list of DocstringParam to markdown lines.
132+
133+
:return: A list of markdown formatted lines
134+
"""
135+
converted = []
136+
for param in params:
137+
if param.type_name is None:
138+
converted.append(
139+
"`{name}`: {description}\n".format(
140+
name=sanitize(param.arg_name),
141+
description=sanitize(param.description),
142+
)
143+
)
144+
else:
145+
converted.append(
146+
"`{name}` (`{type}`): {description}\n".format(
147+
name=sanitize(param.arg_name),
148+
type=param.type_name,
149+
description=sanitize(param.description),
150+
)
151+
)
152+
return converted
153+
154+
def _convert_returns(
155+
self, returns: t.Optional[docstring_parser.common.DocstringReturns]
156+
) -> str:
157+
"""Convert a DocstringReturns object to a markdown string.
158+
159+
:return: A markdown formatted string
160+
"""
161+
if not returns:
162+
return ""
163+
if returns.type_name:
164+
type_data = "`{}`: ".format(returns.type_name)
165+
else:
166+
type_data = ""
167+
return " " + type_data + (sanitize(returns.description) or "") + "\n"
168+
169+
def _process(self, node: docspec.ApiObject) -> None:
170+
if not node.docstring:
171+
return
172+
173+
lines = []
174+
components: t.Dict[str, t.List[str]] = {}
175+
176+
parsed_docstring = docstring_parser.parse(
177+
node.docstring.content, docstring_parser.DocstringStyle.REST
178+
)
179+
components["Arguments"] = self._convert_params(parsed_docstring.params)
180+
components["Raises"] = self._convert_raises(parsed_docstring.raises)
181+
return_doc = self._convert_returns(parsed_docstring.returns)
182+
if return_doc:
183+
components["Returns"] = [return_doc]
184+
185+
if parsed_docstring.short_description:
186+
lines.append(sanitize(parsed_docstring.short_description))
187+
lines.append("")
188+
if parsed_docstring.long_description:
189+
lines.append(sanitize(parsed_docstring.long_description))
190+
lines.append("")
191+
192+
lines.extend(generate_sections_markdown(components))
193+
node.docstring.content = "\n".join(lines)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import dataclasses
2+
import typing as t
3+
4+
import docspec
5+
from pydoc_markdown.contrib.renderers.markdown import MarkdownRenderer
6+
from pydoc_markdown.interfaces import Context, Renderer
7+
8+
9+
def get_first_client_class(
10+
modules: t.List[docspec.Module],
11+
) -> t.Optional[docspec.Class]:
12+
if not modules:
13+
return None
14+
15+
for i in modules:
16+
if isinstance(i, docspec.Class) and getattr(i, "name", "").lower().endswith(
17+
"client"
18+
):
19+
return i
20+
21+
child = get_first_client_class(getattr(i, "members", []))
22+
if child:
23+
return child
24+
25+
return None
26+
27+
28+
def sanitize(s: str) -> str:
29+
character_map = {r"_": r"\_", r"<": r"\<", r">": r"\>", r"{": r"\{", r"}": r"\}"}
30+
31+
for before, after in character_map.items():
32+
s = s.replace(before, after)
33+
return s
34+
35+
36+
@dataclasses.dataclass
37+
class RucioRenderer(Renderer):
38+
markdown: MarkdownRenderer = dataclasses.field(default_factory=MarkdownRenderer)
39+
40+
def init(self, context: Context) -> None:
41+
self.markdown.init(context)
42+
43+
def render_recursive(self, obj: docspec.ApiObject) -> None:
44+
if isinstance(obj, docspec.Function) and (
45+
not obj.name.startswith("_") or obj.name == "__init__"
46+
):
47+
print(f"## {sanitize(obj.name)}\n")
48+
if obj.docstring:
49+
print('<span style={{"white-space": "pre"}}>\n')
50+
print(obj.docstring.content + "\n")
51+
print("</span>\n")
52+
53+
for item in getattr(obj, "members", []):
54+
self.render_recursive(item)
55+
56+
def render(self, modules: t.List[docspec.Module]) -> None:
57+
client_class = get_first_client_class(modules)
58+
assert client_class, "Client Class should not be empty"
59+
60+
print("---")
61+
print(f"title: {client_class.name}")
62+
print("---")
63+
64+
self.render_recursive(client_class)

website/docusaurus.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ module.exports={
5454
},
5555
"items": [
5656
{
57-
"to": "pathname:///html/site/accountclient.html",
57+
"to": "client_api/accountclient",
5858
"label": "Python Client API",
5959
"position": "left"
6060
},

0 commit comments

Comments
 (0)