clear-skies-doc-builder 2.0.0__py3-none-any.whl → 2.0.2__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.
- {clear_skies_doc_builder-2.0.0.dist-info → clear_skies_doc_builder-2.0.2.dist-info}/METADATA +12 -12
- clear_skies_doc_builder-2.0.2.dist-info/RECORD +4 -0
- {clear_skies_doc_builder-2.0.0.dist-info → clear_skies_doc_builder-2.0.2.dist-info}/WHEEL +1 -1
- {clear_skies_doc_builder-2.0.0.dist-info → clear_skies_doc_builder-2.0.2.dist-info/licenses}/LICENSE +1 -1
- clear_skies_doc_builder-2.0.0.dist-info/RECORD +0 -37
- clearskies_doc_builder/__init__.py +0 -34
- clearskies_doc_builder/backends/__init__.py +0 -9
- clearskies_doc_builder/backends/attribute_backend.py +0 -90
- clearskies_doc_builder/backends/class_backend.py +0 -98
- clearskies_doc_builder/backends/module_backend.py +0 -139
- clearskies_doc_builder/backends/python.py +0 -0
- clearskies_doc_builder/build_callable.py +0 -24
- clearskies_doc_builder/builders/__init__.py +0 -9
- clearskies_doc_builder/builders/builder.py +0 -136
- clearskies_doc_builder/builders/module.py +0 -68
- clearskies_doc_builder/builders/single_class.py +0 -78
- clearskies_doc_builder/builders/single_class_to_section.py +0 -38
- clearskies_doc_builder/columns/__init__.py +0 -17
- clearskies_doc_builder/columns/any.py +0 -38
- clearskies_doc_builder/columns/attribute.py +0 -34
- clearskies_doc_builder/columns/attributes.py +0 -34
- clearskies_doc_builder/columns/base_classes.py +0 -31
- clearskies_doc_builder/columns/class_column.py +0 -37
- clearskies_doc_builder/columns/method.py +0 -0
- clearskies_doc_builder/columns/module.py +0 -38
- clearskies_doc_builder/columns/module_classes.py +0 -27
- clearskies_doc_builder/models/__init__.py +0 -13
- clearskies_doc_builder/models/arg.py +0 -0
- clearskies_doc_builder/models/attribute.py +0 -14
- clearskies_doc_builder/models/attribute_reference.py +0 -7
- clearskies_doc_builder/models/class_model.py +0 -24
- clearskies_doc_builder/models/class_reference.py +0 -7
- clearskies_doc_builder/models/method.py +0 -19
- clearskies_doc_builder/models/method_reference.py +0 -7
- clearskies_doc_builder/models/module.py +0 -17
- clearskies_doc_builder/models/module_reference.py +0 -7
- clearskies_doc_builder/models/property.py +0 -0
- clearskies_doc_builder/prepare_doc_space.py +0 -22
{clear_skies_doc_builder-2.0.0.dist-info → clear_skies_doc_builder-2.0.2.dist-info}/METADATA
RENAMED
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: clear-skies-doc-builder
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.2
|
|
4
4
|
Summary: The docbuilder for all 'official' clearskies plugins (as well as the main clearskies docs)
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
Project-URL: repository, https://github.com/clearskies-py/docs
|
|
6
|
+
Project-URL: issues, https://github.com/clearskies-py/docs/issues
|
|
7
|
+
Project-URL: changelog, https://github.com/clearskies-py/docs/blob/main/CHANGELOG.md
|
|
8
|
+
Author-email: Conor Mancone <cmancone@gmail.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
9
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
10
12
|
Classifier: Intended Audience :: Developers
|
|
11
13
|
Classifier: License :: OSI Approved :: MIT License
|
|
12
14
|
Classifier: Programming Language :: Python :: 3
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Requires-Dist:
|
|
17
|
-
Project-URL: Repository, https://github.com/clearskies-py/docs
|
|
15
|
+
Requires-Python: <4.0,>=3.11
|
|
16
|
+
Requires-Dist: clear-skies<3.0.0,>=2.0.0
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: types-requests>=2.32.4; extra == 'dev'
|
|
18
19
|
Description-Content-Type: text/markdown
|
|
19
20
|
|
|
20
21
|
# docs
|
|
@@ -37,4 +38,3 @@ The docs themselves (in the source code) are all written with markdown. This do
|
|
|
37
38
|
8. (Only once) Update the main clearskies doc site to know about the new subfolder for this plugin.
|
|
38
39
|
|
|
39
40
|
Of course, we want the Jekyll sites to be consistent with eachother in terms of style/look. In the long run we'll probably have this doc builder also bootstrap the Jekyll site, but for now you just have to manually setup the Jekyll build using the main clearskies repo as a template.
|
|
40
|
-
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
clear_skies_doc_builder-2.0.2.dist-info/METADATA,sha256=CAdYDFZPPTbjQCXYL50a2ksFoycdU4C8W60zfhT67-c,3075
|
|
2
|
+
clear_skies_doc_builder-2.0.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
3
|
+
clear_skies_doc_builder-2.0.2.dist-info/licenses/LICENSE,sha256=pgH32-xrgpIDf6WIBUA1X19o-sIHIXgToCxo-tBWDdw,1071
|
|
4
|
+
clear_skies_doc_builder-2.0.2.dist-info/RECORD,,
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
clearskies_doc_builder/__init__.py,sha256=8Z9XH0MX0B1WTMwTEypOuZopePhZ5coLO6tTaVYvsmQ,1299
|
|
2
|
-
clearskies_doc_builder/backends/__init__.py,sha256=q5jpy8xfZ4SbGQ1T30q4mp4h346HaMivvmNqtgTMQxw,303
|
|
3
|
-
clearskies_doc_builder/backends/attribute_backend.py,sha256=cMj5XdMN550nUHkmc4-KPKn5PwMg3aFgQr3Hx7Cvibw,3580
|
|
4
|
-
clearskies_doc_builder/backends/class_backend.py,sha256=XNN3svPEMNOP81IRVWzsV206Q8U70ZE8ilVoqYq4SoU,4030
|
|
5
|
-
clearskies_doc_builder/backends/module_backend.py,sha256=JCJZCxD8NlWvN4VtoQNDCqLHU1PYglM5GYvsV8qTLjw,5790
|
|
6
|
-
clearskies_doc_builder/backends/python.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
clearskies_doc_builder/build_callable.py,sha256=9_HrytRA5MeGpuUKRFgmlK5Mb1WKt6TfWlCOi1aS-Pc,983
|
|
8
|
-
clearskies_doc_builder/builders/__init__.py,sha256=ndAK4-imR9vhl7CS425ASYi46FN5-eR9Dr6bKxCn5lA,292
|
|
9
|
-
clearskies_doc_builder/builders/builder.py,sha256=PkO6h-W-qLbQt2iUeb4c0WEir4oa_9ozJYLqOgYoXGM,6133
|
|
10
|
-
clearskies_doc_builder/builders/module.py,sha256=LZY2wx9UUe257usrkCUkuuyZNDa3flcgxRiTBJH8luI,3283
|
|
11
|
-
clearskies_doc_builder/builders/single_class.py,sha256=8KGLewKwD1-MeFVyfUjz7gWbSLTngkfTPebIO2Vu3QY,3451
|
|
12
|
-
clearskies_doc_builder/builders/single_class_to_section.py,sha256=kYokUx-JMR66nzJLQ_hECzptHz6ZWAQyDoM39B0uX08,1751
|
|
13
|
-
clearskies_doc_builder/columns/__init__.py,sha256=--cof8kFPwlZncM3MBQYkvfq8TejEeI0B99vTlEVCkU,566
|
|
14
|
-
clearskies_doc_builder/columns/any.py,sha256=j9WW4IRkbO6AQci8OmJUUpoFPR1y9SzbR2fR_HnSpDo,997
|
|
15
|
-
clearskies_doc_builder/columns/attribute.py,sha256=5QwFUZ93me1dF6_W2U-d3KOHoet9w01i-u4U5LKaa24,970
|
|
16
|
-
clearskies_doc_builder/columns/attributes.py,sha256=Y6ieOcPkd9orAa2OQHRD2w8GTtLFwAG3-CAqIaHb6e0,965
|
|
17
|
-
clearskies_doc_builder/columns/base_classes.py,sha256=GmVrAyfaxX9tT2WUYvz9kCkzHDdVxE2eSatzUYCsmRQ,893
|
|
18
|
-
clearskies_doc_builder/columns/class_column.py,sha256=zGFcu4ch8EhF9wglBvxHZbjbudwSWKrm22aRXsXZoAE,964
|
|
19
|
-
clearskies_doc_builder/columns/method.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
-
clearskies_doc_builder/columns/module.py,sha256=OrbxVARbwpeHa4DRJhG4ScPlicIvwlEkbp3okd9OZJQ,1006
|
|
21
|
-
clearskies_doc_builder/columns/module_classes.py,sha256=4CsXcku0hHVZ0V26u1wzfHYXcHgntFo9hmj91Wn5QlU,728
|
|
22
|
-
clearskies_doc_builder/models/__init__.py,sha256=WY9giZKKl81NNSJtLHPW6iZDu12R0r1MK3zgiYUOJ4s,402
|
|
23
|
-
clearskies_doc_builder/models/arg.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
-
clearskies_doc_builder/models/attribute.py,sha256=1YpL6JGoZNu3ss3Zh_hpByFLqj8s9co8Meo_uuSqx5c,384
|
|
25
|
-
clearskies_doc_builder/models/attribute_reference.py,sha256=u_H7uYGgEYFAZHhPodBg6HJuTUxNkw5kG3NuEYXIjME,188
|
|
26
|
-
clearskies_doc_builder/models/class_model.py,sha256=jOwbp1D69i8YEUngd6-u_RAhrGTxekW4wzTTpaaNzug,897
|
|
27
|
-
clearskies_doc_builder/models/class_reference.py,sha256=CnbJubWq2Px0w_ECJBMXuJ3kGrG7yPzHa2U63EoDzxA,184
|
|
28
|
-
clearskies_doc_builder/models/method.py,sha256=ijJffYVQrRh9t5COEw9DhveihTraC1zNlTl1P_ktWqU,492
|
|
29
|
-
clearskies_doc_builder/models/method_reference.py,sha256=PiH7y7iLJDY_nJ0V-RrdAFHozGr5mmKbZBi-KP56bEA,176
|
|
30
|
-
clearskies_doc_builder/models/module.py,sha256=oDTbuCJBix3wcV_FPECp4ZtBDVbx324w3zIaLuY_aqY,571
|
|
31
|
-
clearskies_doc_builder/models/module_reference.py,sha256=Jz_3iXovaNs65twrFKX95l5jJpkVIi9L-w1XpjhrX7U,176
|
|
32
|
-
clearskies_doc_builder/models/property.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
33
|
-
clearskies_doc_builder/prepare_doc_space.py,sha256=i1bDcorrLTJ12os2ugupUYlRGLwoPlLnly70u-BnjoI,593
|
|
34
|
-
clear_skies_doc_builder-2.0.0.dist-info/LICENSE,sha256=Vfhf-H2GuXDYHeEFF3QIkgjANSbOvflU_Gx7j9WX3sI,1070
|
|
35
|
-
clear_skies_doc_builder-2.0.0.dist-info/METADATA,sha256=DC8RAUktXXDa16Ha5EIPKZkULRlV9fVOOLqz5uXTczo,2980
|
|
36
|
-
clear_skies_doc_builder-2.0.0.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
|
37
|
-
clear_skies_doc_builder-2.0.0.dist-info/RECORD,,
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import pathlib
|
|
2
|
-
import sys
|
|
3
|
-
import json
|
|
4
|
-
|
|
5
|
-
import clearskies
|
|
6
|
-
from clearskies_doc_builder import models, backends
|
|
7
|
-
from clearskies_doc_builder.build_callable import build_callable
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def build(build_file_string: str) -> None:
|
|
11
|
-
# We assume a folder structure here where the repo root contains a `src/` folder and a `docs/python` folder.
|
|
12
|
-
# `build_file_string` should contain the absolute path to the file that kicked this off, which should
|
|
13
|
-
# live in the `docs/python` folder. This comes in as a string, which we convert to a path.
|
|
14
|
-
# We then also need to calculate the path to the `src/` folder and add that to our
|
|
15
|
-
# python path. We do this because we want to import the clearskies module in question, since the python
|
|
16
|
-
# code is where all of our documentation lives.
|
|
17
|
-
|
|
18
|
-
doc_python_path = pathlib.Path(build_file_string).parents[0]
|
|
19
|
-
project_root = doc_python_path.parents[1]
|
|
20
|
-
sys.path.append(str(project_root / "src"))
|
|
21
|
-
|
|
22
|
-
config_file = open(str(doc_python_path / "config.json"), "r")
|
|
23
|
-
config = json.loads(config_file.read())
|
|
24
|
-
config_file.close()
|
|
25
|
-
|
|
26
|
-
cli = clearskies.contexts.Cli(
|
|
27
|
-
build_callable,
|
|
28
|
-
modules=[models, backends],
|
|
29
|
-
bindings={
|
|
30
|
-
"config": config,
|
|
31
|
-
"project_root": project_root / "docs",
|
|
32
|
-
},
|
|
33
|
-
)
|
|
34
|
-
cli()
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
from clearskies_doc_builder.backends.attribute_backend import AttributeBackend
|
|
2
|
-
from clearskies_doc_builder.backends.class_backend import ClassBackend
|
|
3
|
-
from clearskies_doc_builder.backends.module_backend import ModuleBackend
|
|
4
|
-
|
|
5
|
-
__all__ = [
|
|
6
|
-
"AttributeBackend",
|
|
7
|
-
"ClassBackend",
|
|
8
|
-
"ModuleBackend",
|
|
9
|
-
]
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import inspect
|
|
2
|
-
from typing import Any
|
|
3
|
-
|
|
4
|
-
import clearskies
|
|
5
|
-
import clearskies.model
|
|
6
|
-
import clearskies.column
|
|
7
|
-
import clearskies.query
|
|
8
|
-
from clearskies_doc_builder.backends.module_backend import ModuleBackend
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class AttributeBackend(ModuleBackend):
|
|
12
|
-
_search_functions = {
|
|
13
|
-
"id": lambda attribute, name, value: id(attribute) == int(value), # type: ignore
|
|
14
|
-
"name": lambda attribute, name, value: name == value, # type: ignore
|
|
15
|
-
"type": lambda attribute, name, value: attribute.__class__ == value, # type: ignore
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
def records(
|
|
19
|
-
self, query: clearskies.query.Query, next_page_data: dict[str, str | int] | None = None
|
|
20
|
-
) -> list[dict[str, Any]]:
|
|
21
|
-
"""
|
|
22
|
-
Returns a list of records that match the given query configuration
|
|
23
|
-
|
|
24
|
-
next_page_data is used to return data to the caller. Pass in an empty dictionary, and it will be populated
|
|
25
|
-
with the data needed to return the next page of results. If it is still an empty dictionary when returned,
|
|
26
|
-
then there is no additional data.
|
|
27
|
-
"""
|
|
28
|
-
disallowed = ["joins", "selects", "group_by"]
|
|
29
|
-
for attribute_name in disallowed:
|
|
30
|
-
if getattr(query, attribute_name):
|
|
31
|
-
raise ValueError(f"The AttributeBackend received {attribute_name} in a query but doesn't support this.")
|
|
32
|
-
|
|
33
|
-
for condition in query.conditions:
|
|
34
|
-
if condition.operator != "=":
|
|
35
|
-
raise ValueError("The AttributeBackend only supports searching with the equals operator")
|
|
36
|
-
|
|
37
|
-
if "parent_class" not in query.conditions_by_column:
|
|
38
|
-
raise ValueError("When searching for attributes you must include a condition on 'parent_class'")
|
|
39
|
-
|
|
40
|
-
parent_class = query.conditions_by_column["parent_class"][0].values[0]
|
|
41
|
-
matching_attributes = []
|
|
42
|
-
for name in dir(parent_class):
|
|
43
|
-
attribute = getattr(parent_class, name)
|
|
44
|
-
matches = True
|
|
45
|
-
for condition in query.conditions:
|
|
46
|
-
if condition.column_name not in self._search_functions:
|
|
47
|
-
continue
|
|
48
|
-
if not self._search_functions[condition.column_name](attribute, name, condition.values[0]): # type: ignore
|
|
49
|
-
matches = False
|
|
50
|
-
|
|
51
|
-
if not matches:
|
|
52
|
-
continue
|
|
53
|
-
matching_attributes.append(self.unpack(attribute, name, parent_class))
|
|
54
|
-
|
|
55
|
-
return self.paginate(matching_attributes, query)
|
|
56
|
-
|
|
57
|
-
def unpack(self, attribute: Any, name: str, parent_class: type) -> dict[str, Any]: # type: ignore
|
|
58
|
-
all_args = []
|
|
59
|
-
args = []
|
|
60
|
-
kwargs = []
|
|
61
|
-
defaults: dict[str, Any] = {}
|
|
62
|
-
argdata = None
|
|
63
|
-
try:
|
|
64
|
-
argdata = inspect.getfullargspec(attribute)
|
|
65
|
-
except:
|
|
66
|
-
pass
|
|
67
|
-
|
|
68
|
-
if argdata:
|
|
69
|
-
nargs = len(argdata.args)
|
|
70
|
-
nkwargs = len(argdata.defaults) if argdata.defaults else 0
|
|
71
|
-
npargs = nargs - nkwargs
|
|
72
|
-
all_args = argdata.args
|
|
73
|
-
kwargs = all_args[nargs - nkwargs :]
|
|
74
|
-
args = all_args[:nkwargs]
|
|
75
|
-
defaults = {}
|
|
76
|
-
if argdata.defaults:
|
|
77
|
-
defaults = {argdata.args[index + npargs]: default for (index, default) in enumerate(argdata.defaults)}
|
|
78
|
-
|
|
79
|
-
return {
|
|
80
|
-
"id": id(attribute),
|
|
81
|
-
"name": name,
|
|
82
|
-
"type": attribute.__class__,
|
|
83
|
-
"doc": attribute.__doc__,
|
|
84
|
-
"parent_class": parent_class,
|
|
85
|
-
"attribute": attribute,
|
|
86
|
-
"all_args": all_args,
|
|
87
|
-
"args": args,
|
|
88
|
-
"kwargs": kwargs,
|
|
89
|
-
"defaults": defaults,
|
|
90
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
from typing import Any
|
|
2
|
-
from types import ModuleType
|
|
3
|
-
import importlib
|
|
4
|
-
import inspect
|
|
5
|
-
|
|
6
|
-
import clearskies
|
|
7
|
-
import clearskies.model
|
|
8
|
-
import clearskies.column
|
|
9
|
-
import clearskies.query
|
|
10
|
-
from clearskies_doc_builder.backends.module_backend import ModuleBackend
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class ClassBackend(ModuleBackend):
|
|
14
|
-
_search_functions = {
|
|
15
|
-
"id": lambda module, value: id(module) == int(value),
|
|
16
|
-
"source_file": lambda module, value: (module.__file__ if hasattr(module, "__file__") else "") == value,
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
def records(
|
|
20
|
-
self, query: clearskies.query.Query, next_page_data: dict[str, str | int] | None = None
|
|
21
|
-
) -> list[dict[str, Any]]:
|
|
22
|
-
"""
|
|
23
|
-
Returns a list of records that match the given query configuration
|
|
24
|
-
|
|
25
|
-
next_page_data is used to return data to the caller. Pass in an empty dictionary, and it will be populated
|
|
26
|
-
with the data needed to return the next page of results. If it is still an empty dictionary when returned,
|
|
27
|
-
then there is no additional data.
|
|
28
|
-
"""
|
|
29
|
-
disallowed = ["joins", "selects", "group_by"]
|
|
30
|
-
for attribute_name in disallowed:
|
|
31
|
-
if getattr(query, attribute_name):
|
|
32
|
-
raise ValueError(f"The ClassBackend received {attribute_name} in a query but doesn't support this.")
|
|
33
|
-
|
|
34
|
-
for condition in query.conditions:
|
|
35
|
-
if condition.operator != "=":
|
|
36
|
-
raise ValueError("The ClassBackend only supports searching with the equals operator")
|
|
37
|
-
|
|
38
|
-
if "import_path" in query.conditions_by_column:
|
|
39
|
-
import_path = query.conditions_by_column["import_path"][0].values[0]
|
|
40
|
-
path_parts = import_path.split(".")
|
|
41
|
-
if len(path_parts) < 2:
|
|
42
|
-
raise ValueError(
|
|
43
|
-
'In order to search for classes by import path you must provide the module and class name, e.g. `classes.find("import_path=clearskies.Endpoint")`'
|
|
44
|
-
)
|
|
45
|
-
class_name = path_parts[-1]
|
|
46
|
-
module_path = ".".join(path_parts[0:-1])
|
|
47
|
-
module = importlib.import_module(module_path)
|
|
48
|
-
if not hasattr(module, class_name):
|
|
49
|
-
raise ValueError(f"Module {import_path} has no class named {class_name}")
|
|
50
|
-
Class = getattr(module, class_name)
|
|
51
|
-
if not inspect.isclass(Class):
|
|
52
|
-
raise ValueError(
|
|
53
|
-
f"I was asked to import the class named '{import_path}' but this doesn't actually reference a class"
|
|
54
|
-
)
|
|
55
|
-
return [self.unpack(Class, module)]
|
|
56
|
-
|
|
57
|
-
if "module" not in query.conditions_by_column:
|
|
58
|
-
raise ValueError(
|
|
59
|
-
"When searching for classes you must include a condition on either 'module' or 'import_path'"
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
parent_module = query.conditions_by_column["module"][0].values[0]
|
|
63
|
-
matching_classes = []
|
|
64
|
-
for name in dir(parent_module):
|
|
65
|
-
attribute = getattr(parent_module, name)
|
|
66
|
-
if not inspect.isclass(attribute):
|
|
67
|
-
continue
|
|
68
|
-
|
|
69
|
-
matches = True
|
|
70
|
-
for condition in query.conditions:
|
|
71
|
-
if condition.column_name not in self._search_functions:
|
|
72
|
-
continue
|
|
73
|
-
if not self._search_functions[condition.column_name](attribute, condition.values[0]):
|
|
74
|
-
matches = False
|
|
75
|
-
|
|
76
|
-
if not matches:
|
|
77
|
-
continue
|
|
78
|
-
matching_classes.append(self.unpack(attribute, parent_module))
|
|
79
|
-
|
|
80
|
-
return self.paginate(matching_classes, query)
|
|
81
|
-
|
|
82
|
-
def unpack(self, Class: type, module: ModuleType) -> dict[str, Any]: # type: ignore
|
|
83
|
-
source_file = ""
|
|
84
|
-
try:
|
|
85
|
-
# this fails for built ins
|
|
86
|
-
source_file = inspect.getfile(Class)
|
|
87
|
-
except TypeError:
|
|
88
|
-
pass
|
|
89
|
-
|
|
90
|
-
return {
|
|
91
|
-
"id": id(Class),
|
|
92
|
-
"import_path": Class.__module__ + "." + Class.__name__,
|
|
93
|
-
"name": Class.__name__,
|
|
94
|
-
"source_file": source_file,
|
|
95
|
-
"doc": Class.__doc__,
|
|
96
|
-
"module": module,
|
|
97
|
-
"type": Class,
|
|
98
|
-
}
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
from typing import Any, Callable
|
|
2
|
-
from types import ModuleType
|
|
3
|
-
import sys
|
|
4
|
-
import importlib
|
|
5
|
-
|
|
6
|
-
import clearskies
|
|
7
|
-
import clearskies.model
|
|
8
|
-
import clearskies.column
|
|
9
|
-
import clearskies.query
|
|
10
|
-
from clearskies.autodoc.schema import Schema as AutoDocSchema
|
|
11
|
-
from clearskies.autodoc.schema import Integer as AutoDocInteger
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class ModuleBackend(clearskies.backends.Backend):
|
|
15
|
-
_search_functions = {
|
|
16
|
-
"id": lambda module, value: id(module) == int(value),
|
|
17
|
-
"is_builtin": lambda module, value: (0 if hasattr(module, "__file__") else 1) == int(value),
|
|
18
|
-
"source_file": lambda module, value: (module.__file__ if hasattr(module, "__file__") else "") == value,
|
|
19
|
-
"module": lambda module, value: id(module) == id(value),
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
def update(self, id: int | str, data: dict[str, Any], model: clearskies.model.Model) -> dict[str, Any]:
|
|
23
|
-
"""
|
|
24
|
-
Updates the record with the given id with the information from the data dictionary
|
|
25
|
-
"""
|
|
26
|
-
raise Exception(f"The {self.__class__.__name__} only supports read operations: update is not allowed")
|
|
27
|
-
|
|
28
|
-
def create(self, data: dict[str, Any], model: clearskies.model.Model) -> dict[str, Any]:
|
|
29
|
-
"""
|
|
30
|
-
Creates a record with the information from the data dictionary
|
|
31
|
-
"""
|
|
32
|
-
raise Exception(f"The {self.__class__.__name__} only supports read operations: create is not allowed")
|
|
33
|
-
|
|
34
|
-
def delete(self, id: int | str, model: clearskies.model.Model) -> bool:
|
|
35
|
-
"""
|
|
36
|
-
Deletes the record with the given id
|
|
37
|
-
"""
|
|
38
|
-
raise Exception(f"The {self.__class__.__name__} only supports read operations: delete is not allowed")
|
|
39
|
-
|
|
40
|
-
def count(self, query: clearskies.query.Query) -> int:
|
|
41
|
-
"""
|
|
42
|
-
Returns the number of records which match the given query configuration
|
|
43
|
-
"""
|
|
44
|
-
return len(self.records(query))
|
|
45
|
-
|
|
46
|
-
def records(
|
|
47
|
-
self, query: clearskies.query.Query, next_page_data: dict[str, str | int] | None = None
|
|
48
|
-
) -> list[dict[str, Any]]:
|
|
49
|
-
"""
|
|
50
|
-
Returns a list of records that match the given query configuration
|
|
51
|
-
|
|
52
|
-
next_page_data is used to return data to the caller. Pass in an empty dictionary, and it will be populated
|
|
53
|
-
with the data needed to return the next page of results. If it is still an empty dictionary when returned,
|
|
54
|
-
then there is no additional data.
|
|
55
|
-
"""
|
|
56
|
-
disallowed = ["joins", "selects", "group_by"]
|
|
57
|
-
for attribute_name in disallowed:
|
|
58
|
-
if getattr(query, attribute_name):
|
|
59
|
-
raise ValueError(f"The ModuleBackend received {attribute_name} in a query but doesn't support this.")
|
|
60
|
-
|
|
61
|
-
for condition in query.conditions:
|
|
62
|
-
if condition.operator != "=":
|
|
63
|
-
raise ValueError("The ModuleBackend only supports searching with the equals operator")
|
|
64
|
-
|
|
65
|
-
module_name_condition = query.conditions_by_column.get("import_path", query.conditions_by_column.get("name"))
|
|
66
|
-
if module_name_condition:
|
|
67
|
-
module_name = module_name_condition[0].values[0]
|
|
68
|
-
module = importlib.import_module(module_name)
|
|
69
|
-
return [self.unpack(module)]
|
|
70
|
-
|
|
71
|
-
matching_modules = []
|
|
72
|
-
module_names = set(sys.modules) & set(globals())
|
|
73
|
-
for module_name in module_names:
|
|
74
|
-
if module_name not in sys.modules:
|
|
75
|
-
continue
|
|
76
|
-
|
|
77
|
-
module = sys.modules[module_name]
|
|
78
|
-
matches = True
|
|
79
|
-
for condition in query.conditions:
|
|
80
|
-
if condition.column_name not in self._search_functions:
|
|
81
|
-
continue
|
|
82
|
-
if not self._search_functions[condition.column_name](module, condition.values[0]):
|
|
83
|
-
matches = False
|
|
84
|
-
|
|
85
|
-
if not matches:
|
|
86
|
-
continue
|
|
87
|
-
|
|
88
|
-
matching_modules.append(self.unpack(module))
|
|
89
|
-
|
|
90
|
-
return self.paginate(matching_modules, query)
|
|
91
|
-
|
|
92
|
-
def unpack(self, module: ModuleType) -> dict[str, Any]:
|
|
93
|
-
return {
|
|
94
|
-
"id": id(module),
|
|
95
|
-
"import_path": module.__name__,
|
|
96
|
-
"name": module.__name__,
|
|
97
|
-
"is_builtin": not hasattr(module, "__file__"),
|
|
98
|
-
"source_file": module.__file__ if hasattr(module, "__file__") else "",
|
|
99
|
-
"doc": module.__doc__,
|
|
100
|
-
"module": module,
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
def paginate(self, records, query):
|
|
104
|
-
return records
|
|
105
|
-
|
|
106
|
-
def validate_pagination_data(self, data: dict[str, Any], case_mapping: Callable) -> str:
|
|
107
|
-
extra_keys = set(data.keys()) - set(self.allowed_pagination_keys())
|
|
108
|
-
if len(extra_keys):
|
|
109
|
-
key_name = case_mapping("start")
|
|
110
|
-
return "Invalid pagination key(s): '" + "','".join(extra_keys) + f"'. Only '{key_name}' is allowed"
|
|
111
|
-
if "start" not in data:
|
|
112
|
-
key_name = case_mapping("start")
|
|
113
|
-
return f"You must specify '{key_name}' when setting pagination"
|
|
114
|
-
start = data["start"]
|
|
115
|
-
try:
|
|
116
|
-
start = int(start)
|
|
117
|
-
except:
|
|
118
|
-
key_name = case_mapping("start")
|
|
119
|
-
return f"Invalid pagination data: '{key_name}' must be a number"
|
|
120
|
-
return ""
|
|
121
|
-
|
|
122
|
-
def allowed_pagination_keys(self) -> list[str]:
|
|
123
|
-
return ["start"]
|
|
124
|
-
|
|
125
|
-
def documentation_pagination_next_page_response(self, case_mapping: Callable[[str], str]) -> list[Any]:
|
|
126
|
-
return [AutoDocInteger(case_mapping("start"), example=0)]
|
|
127
|
-
|
|
128
|
-
def documentation_pagination_next_page_example(self, case_mapping: Callable[[str], str]) -> dict[str, Any]:
|
|
129
|
-
return {case_mapping("start"): 0}
|
|
130
|
-
|
|
131
|
-
def documentation_pagination_parameters(
|
|
132
|
-
self, case_mapping: Callable[[str], str]
|
|
133
|
-
) -> list[tuple[AutoDocSchema, str]]:
|
|
134
|
-
return [
|
|
135
|
-
(
|
|
136
|
-
AutoDocInteger(case_mapping("start"), example=0),
|
|
137
|
-
"The zero-indexed record number to start listing results from",
|
|
138
|
-
)
|
|
139
|
-
]
|
|
File without changes
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
from typing import Any
|
|
2
|
-
|
|
3
|
-
from clearskies_doc_builder import models
|
|
4
|
-
from clearskies_doc_builder.prepare_doc_space import prepare_doc_space
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def build_callable(modules: models.Module, classes: models.Class, config: dict[str, Any], project_root: str):
|
|
8
|
-
doc_root = prepare_doc_space(project_root)
|
|
9
|
-
nav_order_parent_count = {}
|
|
10
|
-
|
|
11
|
-
for index, branch in enumerate(config["tree"]):
|
|
12
|
-
nav_order_title_tracker = branch.get("parent", branch["title"])
|
|
13
|
-
if nav_order_title_tracker not in nav_order_parent_count:
|
|
14
|
-
nav_order_parent_count[nav_order_title_tracker] = 0
|
|
15
|
-
nav_order_parent_count[nav_order_title_tracker] += 1
|
|
16
|
-
builder_class = classes.find("import_path=" + branch["builder"]).type
|
|
17
|
-
builder = builder_class(
|
|
18
|
-
branch,
|
|
19
|
-
modules,
|
|
20
|
-
classes,
|
|
21
|
-
doc_root,
|
|
22
|
-
nav_order=nav_order_parent_count[nav_order_title_tracker] if branch.get("parent") else index + 2,
|
|
23
|
-
)
|
|
24
|
-
builder.build()
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
from clearskies_doc_builder.builders.module import Module
|
|
2
|
-
from clearskies_doc_builder.builders.single_class import SingleClass
|
|
3
|
-
from clearskies_doc_builder.builders.single_class_to_section import SingleClassToSection
|
|
4
|
-
|
|
5
|
-
__all__ = [
|
|
6
|
-
"Module",
|
|
7
|
-
"SingleClass",
|
|
8
|
-
"SingleClassToSection",
|
|
9
|
-
]
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import re
|
|
2
|
-
import tokenize
|
|
3
|
-
import pathlib
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class Builder:
|
|
7
|
-
_attribute_cache: dict[str, dict[str, str]] = {}
|
|
8
|
-
|
|
9
|
-
def __init__(self, branch, modules, classes, doc_root, nav_order):
|
|
10
|
-
self.modules = modules
|
|
11
|
-
self.classes = classes
|
|
12
|
-
self.doc_root = pathlib.Path(doc_root)
|
|
13
|
-
self.title = branch["title"]
|
|
14
|
-
self.source = branch["source"]
|
|
15
|
-
self.nav_order = nav_order
|
|
16
|
-
self._attribute_cache = {}
|
|
17
|
-
self.args_to_additional_attributes_map = {}
|
|
18
|
-
|
|
19
|
-
def make_index_from_class_overview(self, title_snake_case, source_class, section_folder_path):
|
|
20
|
-
filename = "index"
|
|
21
|
-
section_folder_path.mkdir(exist_ok=True)
|
|
22
|
-
|
|
23
|
-
doc = self.build_header(self.title, filename, title_snake_case, None, self.nav_order, True)
|
|
24
|
-
(elevator_pitch, overview) = self.parse_overview_doc(
|
|
25
|
-
self.raw_docblock_to_md(source_class.doc).lstrip("\n").lstrip(" ")
|
|
26
|
-
)
|
|
27
|
-
doc += f"\n\n# {self.title}\n\n{elevator_pitch}\n\n## Overview\n\n{overview}"
|
|
28
|
-
|
|
29
|
-
output_file = section_folder_path / f"{filename}.md"
|
|
30
|
-
with output_file.open(mode="w") as doc_file:
|
|
31
|
-
doc_file.write(doc)
|
|
32
|
-
|
|
33
|
-
def parse_overview_doc(self, overview_doc):
|
|
34
|
-
parts = overview_doc.lstrip("\n").split("\n", 1)
|
|
35
|
-
if len(parts) < 2:
|
|
36
|
-
return (parts[0], "")
|
|
37
|
-
return (parts[0], parts[1].lstrip(" "))
|
|
38
|
-
|
|
39
|
-
def extract_attribute_docs(self, source_class, argument_names, additional_attribute_sources=[]):
|
|
40
|
-
"""
|
|
41
|
-
Fetch the docblocks for class arguments.
|
|
42
|
-
|
|
43
|
-
Sadly, python doesn't support docblocks on class arguments. I only discovered this after writing all
|
|
44
|
-
the docblocs this way. Still, I don't want to move my docblocs, because puttig them on arguments is
|
|
45
|
-
legitimately the place where they make the most sense. So, we have to use the python parsing capabilities
|
|
46
|
-
built into python in order to extract them ourselves. Very exciting... :cry:
|
|
47
|
-
|
|
48
|
-
We substantially simplify this process (in a way that hopefully works) by setting stringent requirements
|
|
49
|
-
for how our docblocks need to be defined. The docblock must come before the argument and they must be
|
|
50
|
-
at the top of the class. In addition, when we are called, we are provided with a list of all argument
|
|
51
|
-
names so that we are looking for a specific list of things rather than searching more generically for
|
|
52
|
-
a series of documented arguments. So, we're looking for a pattern of:
|
|
53
|
-
|
|
54
|
-
1. tokenize.STRING
|
|
55
|
-
2. tokenize.NEWLINE
|
|
56
|
-
3. tokenize.NAME
|
|
57
|
-
|
|
58
|
-
This will probably match a bunch of things, which is where our list of argument names comes in.
|
|
59
|
-
Also, we'll only use the first combination of these things we find, which menas that attribute definitions
|
|
60
|
-
must be at the top of the file. This will help us avoid getting confused by variable definitions with
|
|
61
|
-
matching names later in the class.
|
|
62
|
-
"""
|
|
63
|
-
# built in classes (which we will reach with our iterative approach) don't have a source file.
|
|
64
|
-
if not source_class.source_file:
|
|
65
|
-
return {}
|
|
66
|
-
|
|
67
|
-
# we will iterate over base classes, and these often get re-used, so let's keep a cache
|
|
68
|
-
if source_class.source_file in self._attribute_cache:
|
|
69
|
-
return self._attribute_cache[source_class.source_file]
|
|
70
|
-
|
|
71
|
-
doc_strings = {}
|
|
72
|
-
with open(source_class.source_file, "r") as fp:
|
|
73
|
-
# so this is both very simple and, hopefully, not prone to failure. The tokenization information that comes back from the
|
|
74
|
-
# parser is surprisingly generic and vague. However, we are looking for something
|
|
75
|
-
last_string = ""
|
|
76
|
-
for token_type, token_string, (srow, scol), (erow, ecol), line_content in tokenize.generate_tokens(
|
|
77
|
-
fp.readline
|
|
78
|
-
):
|
|
79
|
-
if token_type == tokenize.STRING:
|
|
80
|
-
last_string = token_string
|
|
81
|
-
continue
|
|
82
|
-
if token_type == tokenize.NEWLINE:
|
|
83
|
-
continue
|
|
84
|
-
if token_type != tokenize.NAME:
|
|
85
|
-
last_string = ""
|
|
86
|
-
continue
|
|
87
|
-
if not last_string or token_string not in argument_names:
|
|
88
|
-
continue
|
|
89
|
-
doc_strings[token_string] = last_string
|
|
90
|
-
|
|
91
|
-
# and let's repeat this for any base classes just to make sure we don't miss anything. Often attributes are defined in
|
|
92
|
-
# bases and we want to use those docs if we don't have them.
|
|
93
|
-
for base_class in source_class.base_classes:
|
|
94
|
-
doc_strings = {
|
|
95
|
-
**self.extract_attribute_docs(base_class, argument_names),
|
|
96
|
-
**doc_strings,
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
for additional_source_class in additional_attribute_sources:
|
|
100
|
-
doc_strings = {
|
|
101
|
-
**self.extract_attribute_docs(additional_source_class, argument_names),
|
|
102
|
-
**doc_strings,
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
self._attribute_cache[source_class.source_file] = doc_strings
|
|
106
|
-
return doc_strings
|
|
107
|
-
|
|
108
|
-
def build_header(self, title, filename, section_name, parent, nav_order, has_children):
|
|
109
|
-
permalink = "/docs/" + (f"{section_name}/" if section_name else "") + f"{filename}.html"
|
|
110
|
-
header = f"""---
|
|
111
|
-
layout: default
|
|
112
|
-
title: {title}
|
|
113
|
-
permalink: {permalink}
|
|
114
|
-
nav_order: {nav_order}
|
|
115
|
-
"""
|
|
116
|
-
if parent:
|
|
117
|
-
header += f"parent: {parent}\n"
|
|
118
|
-
if has_children:
|
|
119
|
-
header += "has_children: true\n"
|
|
120
|
-
header += "---"
|
|
121
|
-
return header
|
|
122
|
-
|
|
123
|
-
def raw_docblock_to_md(self, docblock):
|
|
124
|
-
return re.sub(r"\n ", "\n", docblock)
|
|
125
|
-
|
|
126
|
-
def default_args(self):
|
|
127
|
-
default_args = {}
|
|
128
|
-
for key, value in self.args_to_additional_attributes_map.items():
|
|
129
|
-
parts = value.split(".")
|
|
130
|
-
import_path = ".".join(parts[:-1])
|
|
131
|
-
attribute_name = parts[-1]
|
|
132
|
-
source_class = self.classes.find(f"import_path={import_path}")
|
|
133
|
-
doc = source_class.attributes.where(f"name={attribute_name}").first().doc
|
|
134
|
-
if doc:
|
|
135
|
-
default_args[key] = self.raw_docblock_to_md(doc)
|
|
136
|
-
return default_args
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
from typing import Any
|
|
2
|
-
from collections import OrderedDict
|
|
3
|
-
|
|
4
|
-
import clearskies
|
|
5
|
-
from .builder import Builder
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class Module(Builder):
|
|
9
|
-
def __init__(self, branch, modules, classes, doc_root, nav_order):
|
|
10
|
-
super().__init__(branch, modules, classes, doc_root, nav_order)
|
|
11
|
-
self.class_list = branch["classes"]
|
|
12
|
-
self.args_to_additional_attributes_map = branch.get("args_to_additional_attributes_map", {})
|
|
13
|
-
|
|
14
|
-
def build(self):
|
|
15
|
-
title_snake_case = clearskies.functional.string.title_case_to_snake_case(self.title.replace(" ", "")).replace(
|
|
16
|
-
"_", "-"
|
|
17
|
-
)
|
|
18
|
-
section_folder_path = self.doc_root / title_snake_case
|
|
19
|
-
source_class = self.classes.find(f"import_path={self.source}")
|
|
20
|
-
self.make_index_from_class_overview(title_snake_case, source_class, section_folder_path)
|
|
21
|
-
|
|
22
|
-
default_args = self.default_args()
|
|
23
|
-
|
|
24
|
-
nav_order = 0
|
|
25
|
-
for class_name in self.class_list:
|
|
26
|
-
nav_order += 1
|
|
27
|
-
source_class = self.classes.find(f"import_path={class_name}")
|
|
28
|
-
title = source_class.name
|
|
29
|
-
filename = clearskies.functional.string.title_case_to_snake_case(source_class.name).replace("_", "-")
|
|
30
|
-
class_doc = self.build_header(source_class.name, filename, title_snake_case, self.title, nav_order, False)
|
|
31
|
-
(elevator_pitch, overview) = self.parse_overview_doc(
|
|
32
|
-
self.raw_docblock_to_md(source_class.doc).lstrip("\n").lstrip(" ")
|
|
33
|
-
)
|
|
34
|
-
class_doc += f"\n\n# {title}\n\n{elevator_pitch}\n\n"
|
|
35
|
-
main_doc = "## Overview\n\n{overview}\n\n"
|
|
36
|
-
table_of_contents = " 1. [Overview](#overview)\n"
|
|
37
|
-
|
|
38
|
-
# Find the documentation for all of our init args.
|
|
39
|
-
arguments: dict[str, Any] = OrderedDict()
|
|
40
|
-
for arg in source_class.init.all_args:
|
|
41
|
-
if arg == "self":
|
|
42
|
-
continue
|
|
43
|
-
arguments[arg] = {
|
|
44
|
-
"required": arg not in source_class.init.kwargs,
|
|
45
|
-
"doc": default_args.get(arg, ""),
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
# for various reasons, it's easier to extract docs for all the arguments at once:
|
|
49
|
-
docs = self.extract_attribute_docs(source_class, list(arguments.keys()))
|
|
50
|
-
for arg, doc in docs.items():
|
|
51
|
-
# you would think that we would only get arguments that belong to our class, but this isn't the case
|
|
52
|
-
# because the processing caches results from parent classes, and we don't always use all attributes
|
|
53
|
-
# available from all our parents.
|
|
54
|
-
if arg not in arguments:
|
|
55
|
-
continue
|
|
56
|
-
arguments[arg]["doc"] = doc
|
|
57
|
-
|
|
58
|
-
for index, arg in enumerate(arguments.keys()):
|
|
59
|
-
arg_data = arguments[arg]
|
|
60
|
-
table_of_contents += f" {index+2}. [{arg}](#{arg})\n"
|
|
61
|
-
main_doc += f"## {arg}\n**" + ("Required" if arg_data["required"] else "Optional") + "**\n\n"
|
|
62
|
-
main_doc += self.raw_docblock_to_md(arg_data["doc"].replace('"""', "")) + "\n\n"
|
|
63
|
-
|
|
64
|
-
class_doc += f"{table_of_contents}\n{main_doc}"
|
|
65
|
-
|
|
66
|
-
output_file = section_folder_path / f"{filename}.md"
|
|
67
|
-
with output_file.open(mode="w") as doc_file:
|
|
68
|
-
doc_file.write(class_doc)
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
from typing import Any
|
|
2
|
-
from collections import OrderedDict
|
|
3
|
-
|
|
4
|
-
import clearskies
|
|
5
|
-
from .builder import Builder
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class SingleClass(Builder):
|
|
9
|
-
def __init__(self, branch, modules, classes, doc_root, nav_order):
|
|
10
|
-
super().__init__(branch, modules, classes, doc_root, nav_order)
|
|
11
|
-
self.additional_attribute_sources = branch.get("additional_attribute_sources", [])
|
|
12
|
-
self.args_to_additional_attributes_map = branch.get("args_to_additional_attributes_map", {})
|
|
13
|
-
self.parent = branch.get("parent", False)
|
|
14
|
-
|
|
15
|
-
def build(self):
|
|
16
|
-
section_name = (
|
|
17
|
-
clearskies.functional.string.title_case_to_snake_case(self.parent if self.parent else self.title)
|
|
18
|
-
.replace("_", "-")
|
|
19
|
-
.replace(" ", "")
|
|
20
|
-
)
|
|
21
|
-
section_folder_path = self.doc_root / section_name
|
|
22
|
-
section_folder_path.mkdir(exist_ok=True)
|
|
23
|
-
source_class = self.classes.find(f"import_path={self.source}")
|
|
24
|
-
|
|
25
|
-
title_snake_case = clearskies.functional.string.title_case_to_snake_case(self.title.replace(" ", "")).replace(
|
|
26
|
-
"_", "-"
|
|
27
|
-
)
|
|
28
|
-
class_doc = self.build_header(self.title, title_snake_case, section_name, self.parent, self.nav_order, False)
|
|
29
|
-
(elevator_pitch, overview) = self.parse_overview_doc(
|
|
30
|
-
self.raw_docblock_to_md(source_class.doc).lstrip("\n").lstrip(" ")
|
|
31
|
-
)
|
|
32
|
-
class_doc += f"\n\n# {self.title}\n\n{elevator_pitch}\n\n"
|
|
33
|
-
main_doc = f"## Overview\n\n{overview}\n\n"
|
|
34
|
-
table_of_contents = " 1. [Overview](#overview)\n"
|
|
35
|
-
|
|
36
|
-
default_args = self.default_args()
|
|
37
|
-
|
|
38
|
-
# Find the documentation for all of our init args.
|
|
39
|
-
arguments: dict[str, Any] = OrderedDict()
|
|
40
|
-
for arg in source_class.init.all_args:
|
|
41
|
-
if arg == "self":
|
|
42
|
-
continue
|
|
43
|
-
arguments[arg] = {
|
|
44
|
-
"required": arg not in source_class.init.kwargs,
|
|
45
|
-
"doc": default_args.get(arg, ""),
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
# for various reasons, it's easier to extract docs for all the arguments at once:
|
|
49
|
-
docs = self.extract_attribute_docs(
|
|
50
|
-
source_class,
|
|
51
|
-
list(arguments.keys()),
|
|
52
|
-
additional_attribute_sources=[
|
|
53
|
-
self.classes.find(f"import_path={source}") for source in self.additional_attribute_sources
|
|
54
|
-
],
|
|
55
|
-
)
|
|
56
|
-
for arg, doc in docs.items():
|
|
57
|
-
# you would think that we would only get arguments that belong to our class, but this isn't the case
|
|
58
|
-
# because the processing caches results from parent classes, and we don't always use all attributes
|
|
59
|
-
# available from all our parents.
|
|
60
|
-
if arg not in arguments:
|
|
61
|
-
continue
|
|
62
|
-
arguments[arg]["doc"] = doc
|
|
63
|
-
|
|
64
|
-
for index, arg in enumerate(arguments.keys()):
|
|
65
|
-
arg_data = arguments[arg]
|
|
66
|
-
table_of_contents += f" {index+2}. [{arg}](#{arg})\n"
|
|
67
|
-
main_doc += f"## {arg}\n**" + ("Required" if arg_data["required"] else "Optional") + "**\n\n"
|
|
68
|
-
main_doc += self.raw_docblock_to_md(arg_data["doc"].replace('"""', "")) + "\n\n"
|
|
69
|
-
|
|
70
|
-
class_doc += f"{table_of_contents}\n{main_doc}"
|
|
71
|
-
|
|
72
|
-
output_file = section_folder_path / (
|
|
73
|
-
"index.md"
|
|
74
|
-
if not self.parent
|
|
75
|
-
else clearskies.functional.string.title_case_to_snake_case(self.title.replace(" ", "")) + ".md"
|
|
76
|
-
)
|
|
77
|
-
with output_file.open(mode="w") as doc_file:
|
|
78
|
-
doc_file.write(class_doc)
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import re
|
|
2
|
-
|
|
3
|
-
import clearskies
|
|
4
|
-
from .builder import Builder
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class SingleClassToSection(Builder):
|
|
8
|
-
def __init__(self, branch, modules, classes, doc_root, nav_order):
|
|
9
|
-
super().__init__(branch, modules, classes, doc_root, nav_order)
|
|
10
|
-
self.docs = branch["docs"]
|
|
11
|
-
|
|
12
|
-
def build(self):
|
|
13
|
-
section_name = clearskies.functional.string.title_case_to_snake_case(self.title).replace("_", "-")
|
|
14
|
-
section_folder_path = self.doc_root / section_name
|
|
15
|
-
source_class = self.classes.find(f"import_path={self.source}")
|
|
16
|
-
self.make_index_from_class_overview(section_name, source_class, section_folder_path)
|
|
17
|
-
|
|
18
|
-
for index, doc_data in enumerate(self.docs):
|
|
19
|
-
title = doc_data["title"]
|
|
20
|
-
title_snake_case = clearskies.functional.string.title_case_to_snake_case(title.replace(" ", "")).replace(
|
|
21
|
-
"_", "-"
|
|
22
|
-
)
|
|
23
|
-
doc = self.build_header(title, title_snake_case, section_name, self.title, index + 1, False)
|
|
24
|
-
doc += f"\n\n# {title}\n\n"
|
|
25
|
-
table_of_contents = ""
|
|
26
|
-
attribute_docs = ""
|
|
27
|
-
|
|
28
|
-
for index, attribute_name in enumerate(doc_data["attributes"]):
|
|
29
|
-
attribute = source_class.attributes.find(f"name={attribute_name}")
|
|
30
|
-
table_of_contents += f" {index+1}. [{attribute_name}]({title_snake_case}.html#{attribute_name})\n"
|
|
31
|
-
attribute_docs += f"\n\n## {attribute_name}\n\n"
|
|
32
|
-
attribute_docs += re.sub("\n ", "\n", self.raw_docblock_to_md(attribute.doc))
|
|
33
|
-
|
|
34
|
-
doc += f"{table_of_contents}{attribute_docs}"
|
|
35
|
-
|
|
36
|
-
output_file = section_folder_path / f"{title_snake_case}.md"
|
|
37
|
-
with output_file.open(mode="w") as doc_file:
|
|
38
|
-
doc_file.write(doc)
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
from clearskies_doc_builder.columns.any import Any
|
|
2
|
-
from clearskies_doc_builder.columns.attribute import Attribute
|
|
3
|
-
from clearskies_doc_builder.columns.attributes import Attributes
|
|
4
|
-
from clearskies_doc_builder.columns.base_classes import BaseClasses
|
|
5
|
-
from clearskies_doc_builder.columns.class_column import Class
|
|
6
|
-
from clearskies_doc_builder.columns.module import Module
|
|
7
|
-
from clearskies_doc_builder.columns.module_classes import ModuleClasses
|
|
8
|
-
|
|
9
|
-
__all__ = [
|
|
10
|
-
"Any",
|
|
11
|
-
"Attribute",
|
|
12
|
-
"Attributes",
|
|
13
|
-
"BaseClasses",
|
|
14
|
-
"Class",
|
|
15
|
-
"Module",
|
|
16
|
-
"ModuleClasses",
|
|
17
|
-
]
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
from typing import overload, Self, TYPE_CHECKING
|
|
3
|
-
from typing import Any as AnyType
|
|
4
|
-
|
|
5
|
-
import clearskies
|
|
6
|
-
|
|
7
|
-
if TYPE_CHECKING:
|
|
8
|
-
from clearskies import Model
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class Any(clearskies.Column):
|
|
12
|
-
is_writeable = clearskies.configs.boolean.Boolean(default=False)
|
|
13
|
-
_descriptor_config_map = None
|
|
14
|
-
|
|
15
|
-
def __init__(self):
|
|
16
|
-
super().__init__()
|
|
17
|
-
|
|
18
|
-
@overload
|
|
19
|
-
def __get__(self, instance: None, cls: type[Model]) -> Self:
|
|
20
|
-
pass
|
|
21
|
-
|
|
22
|
-
@overload
|
|
23
|
-
def __get__(self, instance: Model, cls: type[Model]) -> AnyType:
|
|
24
|
-
pass
|
|
25
|
-
|
|
26
|
-
def __get__(self, instance, cls):
|
|
27
|
-
return super().__get__(instance, cls)
|
|
28
|
-
|
|
29
|
-
def __set__(self, instance: Model, value: AnyType) -> None:
|
|
30
|
-
instance._next_data[self.name] = value
|
|
31
|
-
|
|
32
|
-
def from_backend(self, value):
|
|
33
|
-
return value
|
|
34
|
-
|
|
35
|
-
def to_backend(self, data: dict[str, Any]) -> dict[str, Any]:
|
|
36
|
-
if self.name not in data:
|
|
37
|
-
return data
|
|
38
|
-
return {**data, self.name: data[self.name]}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
from typing import Callable
|
|
3
|
-
|
|
4
|
-
import clearskies
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class Attribute(clearskies.columns.HasMany):
|
|
8
|
-
def __init__(
|
|
9
|
-
self,
|
|
10
|
-
child_model_class,
|
|
11
|
-
readable_child_column_names: list[str] = [],
|
|
12
|
-
filter: Callable | None = None,
|
|
13
|
-
):
|
|
14
|
-
self.filter = filter
|
|
15
|
-
super().__init__(
|
|
16
|
-
child_model_class,
|
|
17
|
-
foreign_column_name="parent_class",
|
|
18
|
-
readable_child_column_names=readable_child_column_names,
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
def __get__(self, model, cls):
|
|
22
|
-
if model is None:
|
|
23
|
-
self.model_class = cls
|
|
24
|
-
return self # type: ignore
|
|
25
|
-
|
|
26
|
-
# this makes sure we're initialized
|
|
27
|
-
if "name" not in self._config:
|
|
28
|
-
model.get_columns()
|
|
29
|
-
|
|
30
|
-
attributes = self.child_model.where(self.child_model_class.parent_class.equals(model.type))
|
|
31
|
-
if self.filter:
|
|
32
|
-
return list(filter(self.filter, attributes))[0]
|
|
33
|
-
|
|
34
|
-
return attributes[0]
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
from typing import Callable
|
|
3
|
-
|
|
4
|
-
import clearskies
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class Attributes(clearskies.columns.HasMany):
|
|
8
|
-
def __init__(
|
|
9
|
-
self,
|
|
10
|
-
child_model_class,
|
|
11
|
-
readable_child_column_names: list[str] = [],
|
|
12
|
-
filter: Callable | None = None,
|
|
13
|
-
):
|
|
14
|
-
self.filter = filter
|
|
15
|
-
super().__init__(
|
|
16
|
-
child_model_class,
|
|
17
|
-
foreign_column_name="parent_class",
|
|
18
|
-
readable_child_column_names=readable_child_column_names,
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
def __get__(self, model, cls):
|
|
22
|
-
if model is None:
|
|
23
|
-
self.model_class = cls
|
|
24
|
-
return self # type: ignore
|
|
25
|
-
|
|
26
|
-
# this makes sure we're initialized
|
|
27
|
-
if "name" not in self._config:
|
|
28
|
-
model.get_columns()
|
|
29
|
-
|
|
30
|
-
attributes = self.child_model.where(self.child_model_class.parent_class.equals(model.type))
|
|
31
|
-
if self.filter:
|
|
32
|
-
return list(filter(self.filter, attributes))
|
|
33
|
-
|
|
34
|
-
return attributes
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import clearskies
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class BaseClasses(clearskies.columns.HasMany):
|
|
7
|
-
def __init__(
|
|
8
|
-
self,
|
|
9
|
-
readable_child_column_names: list[str] = [],
|
|
10
|
-
):
|
|
11
|
-
self.foreign_column_name = "type"
|
|
12
|
-
self.readable_child_column_names = readable_child_column_names
|
|
13
|
-
|
|
14
|
-
def finalize_configuration(self, model_class, name) -> None:
|
|
15
|
-
self.child_model_class = model_class
|
|
16
|
-
super().finalize_configuration(model_class, name)
|
|
17
|
-
|
|
18
|
-
def __get__(self, model, cls):
|
|
19
|
-
if model is None:
|
|
20
|
-
self.model_class = cls
|
|
21
|
-
return self # type: ignore
|
|
22
|
-
|
|
23
|
-
# this makes sure we're initialized
|
|
24
|
-
if "name" not in self._config:
|
|
25
|
-
model.get_columns()
|
|
26
|
-
|
|
27
|
-
bases = []
|
|
28
|
-
for cls in model.type.__bases__:
|
|
29
|
-
bases.append(model.model(model.backend.unpack(cls, model.module)))
|
|
30
|
-
|
|
31
|
-
return bases
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
from typing import Any, overload, Self, TYPE_CHECKING
|
|
3
|
-
|
|
4
|
-
import clearskies
|
|
5
|
-
|
|
6
|
-
if TYPE_CHECKING:
|
|
7
|
-
from clearskies import Model
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class Class(clearskies.Column):
|
|
11
|
-
is_writeable = clearskies.configs.boolean.Boolean(default=False)
|
|
12
|
-
_descriptor_config_map = None
|
|
13
|
-
|
|
14
|
-
def __init__(self):
|
|
15
|
-
super().__init__()
|
|
16
|
-
|
|
17
|
-
@overload
|
|
18
|
-
def __get__(self, instance: None, cls: type[Model]) -> Self:
|
|
19
|
-
pass
|
|
20
|
-
|
|
21
|
-
@overload
|
|
22
|
-
def __get__(self, instance: Model, cls: type[Model]) -> type:
|
|
23
|
-
pass
|
|
24
|
-
|
|
25
|
-
def __get__(self, instance, cls):
|
|
26
|
-
return super().__get__(instance, cls)
|
|
27
|
-
|
|
28
|
-
def __set__(self, instance: Model, value: type) -> None:
|
|
29
|
-
instance._next_data[self.name] = value
|
|
30
|
-
|
|
31
|
-
def from_backend(self, value):
|
|
32
|
-
return value
|
|
33
|
-
|
|
34
|
-
def to_backend(self, data: dict[str, Any]) -> dict[str, Any]:
|
|
35
|
-
if self.name not in data:
|
|
36
|
-
return data
|
|
37
|
-
return {**data, self.name: data[self.name]}
|
|
File without changes
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
from typing import Any, overload, Self, TYPE_CHECKING
|
|
3
|
-
from types import ModuleType
|
|
4
|
-
|
|
5
|
-
import clearskies
|
|
6
|
-
|
|
7
|
-
if TYPE_CHECKING:
|
|
8
|
-
from clearskies import Model
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class Module(clearskies.Column):
|
|
12
|
-
is_writeable = clearskies.configs.boolean.Boolean(default=False)
|
|
13
|
-
_descriptor_config_map = None
|
|
14
|
-
|
|
15
|
-
def __init__(self):
|
|
16
|
-
super().__init__()
|
|
17
|
-
|
|
18
|
-
@overload
|
|
19
|
-
def __get__(self, instance: None, cls: type[Model]) -> Self:
|
|
20
|
-
pass
|
|
21
|
-
|
|
22
|
-
@overload
|
|
23
|
-
def __get__(self, instance: Model, cls: type[Model]) -> ModuleType:
|
|
24
|
-
pass
|
|
25
|
-
|
|
26
|
-
def __get__(self, instance, cls):
|
|
27
|
-
return super().__get__(instance, cls)
|
|
28
|
-
|
|
29
|
-
def __set__(self, instance: Model, value: ModuleType) -> None:
|
|
30
|
-
instance._next_data[self.name] = value
|
|
31
|
-
|
|
32
|
-
def from_backend(self, value):
|
|
33
|
-
return value
|
|
34
|
-
|
|
35
|
-
def to_backend(self, data: dict[str, Any]) -> dict[str, Any]:
|
|
36
|
-
if self.name not in data:
|
|
37
|
-
return data
|
|
38
|
-
return {**data, self.name: data[self.name]}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import clearskies
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class ModuleClasses(clearskies.columns.HasMany):
|
|
7
|
-
def __init__(
|
|
8
|
-
self,
|
|
9
|
-
child_model_class,
|
|
10
|
-
readable_child_column_names: list[str] = [],
|
|
11
|
-
):
|
|
12
|
-
super().__init__(
|
|
13
|
-
child_model_class,
|
|
14
|
-
foreign_column_name="module",
|
|
15
|
-
readable_child_column_names=readable_child_column_names,
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
def __get__(self, model, cls):
|
|
19
|
-
if model is None:
|
|
20
|
-
self.model_class = cls
|
|
21
|
-
return self # type: ignore
|
|
22
|
-
|
|
23
|
-
# this makes sure we're initialized
|
|
24
|
-
if "name" not in self._config:
|
|
25
|
-
model.get_columns()
|
|
26
|
-
|
|
27
|
-
return self.child_model.where(cls.module.equals(model.module))
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
from clearskies_doc_builder.models.attribute import Attribute
|
|
2
|
-
from clearskies_doc_builder.models.method import Method
|
|
3
|
-
from clearskies_doc_builder.models.module import Module
|
|
4
|
-
from clearskies_doc_builder.models.class_model import Class
|
|
5
|
-
from clearskies_doc_builder.models.class_reference import ClassReference
|
|
6
|
-
|
|
7
|
-
__all__ = [
|
|
8
|
-
"Attribute",
|
|
9
|
-
"Class",
|
|
10
|
-
"ClassReference",
|
|
11
|
-
"Method",
|
|
12
|
-
"Module",
|
|
13
|
-
]
|
|
File without changes
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import clearskies
|
|
2
|
-
from clearskies_doc_builder import backends, columns
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class Attribute(clearskies.Model):
|
|
6
|
-
id_column_name = "id"
|
|
7
|
-
backend = backends.AttributeBackend()
|
|
8
|
-
|
|
9
|
-
id = clearskies.columns.Integer()
|
|
10
|
-
name = clearskies.columns.String()
|
|
11
|
-
type = columns.Class()
|
|
12
|
-
doc = clearskies.columns.String()
|
|
13
|
-
attribute = columns.Any()
|
|
14
|
-
parent_class = columns.Class()
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import clearskies
|
|
2
|
-
from clearskies_doc_builder import backends, columns
|
|
3
|
-
from .attribute_reference import AttributeReference
|
|
4
|
-
from .method_reference import MethodReference
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class Class(clearskies.Model):
|
|
8
|
-
id_column_name = "id"
|
|
9
|
-
backend = backends.ClassBackend()
|
|
10
|
-
|
|
11
|
-
id = clearskies.columns.Integer()
|
|
12
|
-
type = columns.Any()
|
|
13
|
-
source_file = clearskies.columns.String()
|
|
14
|
-
import_path = clearskies.columns.String()
|
|
15
|
-
name = clearskies.columns.String(is_searchable=False)
|
|
16
|
-
doc = clearskies.columns.String(is_searchable=False)
|
|
17
|
-
module = columns.Module()
|
|
18
|
-
base_classes = columns.BaseClasses()
|
|
19
|
-
attributes = columns.Attributes(AttributeReference)
|
|
20
|
-
methods = columns.Attributes(AttributeReference, filter=lambda attribute: callable(attribute.attribute))
|
|
21
|
-
init = columns.Attribute(
|
|
22
|
-
MethodReference,
|
|
23
|
-
filter=lambda attribute: attribute.name == "__init__",
|
|
24
|
-
)
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import clearskies
|
|
2
|
-
from clearskies_doc_builder import backends, columns
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class Method(clearskies.Model):
|
|
6
|
-
id_column_name = "id"
|
|
7
|
-
backend = backends.AttributeBackend()
|
|
8
|
-
|
|
9
|
-
id = clearskies.columns.Integer()
|
|
10
|
-
name = clearskies.columns.String()
|
|
11
|
-
type = columns.Class()
|
|
12
|
-
doc = clearskies.columns.String()
|
|
13
|
-
attribute = columns.Any()
|
|
14
|
-
parent_class = columns.Class()
|
|
15
|
-
|
|
16
|
-
args = columns.Any()
|
|
17
|
-
kwargs = columns.Any()
|
|
18
|
-
all_args = columns.Any()
|
|
19
|
-
defaults = columns.Any()
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import clearskies
|
|
2
|
-
from clearskies_doc_builder import backends, columns
|
|
3
|
-
from . import class_reference
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class Module(clearskies.Model):
|
|
7
|
-
id_column_name = "id"
|
|
8
|
-
backend = backends.ModuleBackend()
|
|
9
|
-
|
|
10
|
-
id = clearskies.columns.Integer()
|
|
11
|
-
import_path = clearskies.columns.String()
|
|
12
|
-
source_file = clearskies.columns.String()
|
|
13
|
-
is_builtin = clearskies.columns.Boolean()
|
|
14
|
-
name = clearskies.columns.String()
|
|
15
|
-
doc = clearskies.columns.String(is_searchable=False)
|
|
16
|
-
module = columns.Module()
|
|
17
|
-
classes = columns.ModuleClasses(class_reference.ClassReference)
|
|
File without changes
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import shutil
|
|
2
|
-
import pathlib
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def prepare_doc_space(project_root):
|
|
6
|
-
project_path = pathlib.Path(project_root)
|
|
7
|
-
build_path = project_path / "build"
|
|
8
|
-
doc_path = build_path / "docs"
|
|
9
|
-
jekyll_path = project_path / "jekyll"
|
|
10
|
-
|
|
11
|
-
if doc_path.is_dir():
|
|
12
|
-
shutil.rmtree(doc_path)
|
|
13
|
-
build_path.mkdir(parents=True, exist_ok=True)
|
|
14
|
-
|
|
15
|
-
for file in jekyll_path.glob("*"):
|
|
16
|
-
if not file.is_file():
|
|
17
|
-
continue
|
|
18
|
-
shutil.copy2(str(file), str(build_path / file.name))
|
|
19
|
-
|
|
20
|
-
shutil.copytree(str(jekyll_path / "docs"), str(build_path / "docs"))
|
|
21
|
-
|
|
22
|
-
return str(doc_path)
|