clear-skies-doc-builder 2.0.1__py3-none-any.whl → 2.0.3__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.
Files changed (39) hide show
  1. {clear_skies_doc_builder-2.0.1.dist-info → clear_skies_doc_builder-2.0.3.dist-info}/METADATA +1 -1
  2. clear_skies_doc_builder-2.0.3.dist-info/RECORD +38 -0
  3. clearskies_doc_builder/__init__.py +35 -0
  4. clearskies_doc_builder/backends/__init__.py +9 -0
  5. clearskies_doc_builder/backends/attribute_backend.py +91 -0
  6. clearskies_doc_builder/backends/class_backend.py +99 -0
  7. clearskies_doc_builder/backends/module_backend.py +131 -0
  8. clearskies_doc_builder/backends/python.py +0 -0
  9. clearskies_doc_builder/build_callable.py +24 -0
  10. clearskies_doc_builder/builders/__init__.py +9 -0
  11. clearskies_doc_builder/builders/builder.py +136 -0
  12. clearskies_doc_builder/builders/module.py +69 -0
  13. clearskies_doc_builder/builders/single_class.py +79 -0
  14. clearskies_doc_builder/builders/single_class_to_section.py +39 -0
  15. clearskies_doc_builder/columns/__init__.py +17 -0
  16. clearskies_doc_builder/columns/any.py +39 -0
  17. clearskies_doc_builder/columns/attribute.py +35 -0
  18. clearskies_doc_builder/columns/attributes.py +35 -0
  19. clearskies_doc_builder/columns/base_classes.py +31 -0
  20. clearskies_doc_builder/columns/class_column.py +38 -0
  21. clearskies_doc_builder/columns/method.py +0 -0
  22. clearskies_doc_builder/columns/module.py +39 -0
  23. clearskies_doc_builder/columns/module_classes.py +27 -0
  24. clearskies_doc_builder/configs/__init__.py +0 -0
  25. clearskies_doc_builder/models/__init__.py +13 -0
  26. clearskies_doc_builder/models/arg.py +0 -0
  27. clearskies_doc_builder/models/attribute.py +15 -0
  28. clearskies_doc_builder/models/attribute_reference.py +8 -0
  29. clearskies_doc_builder/models/class_model.py +26 -0
  30. clearskies_doc_builder/models/class_reference.py +8 -0
  31. clearskies_doc_builder/models/method.py +20 -0
  32. clearskies_doc_builder/models/method_reference.py +8 -0
  33. clearskies_doc_builder/models/module.py +19 -0
  34. clearskies_doc_builder/models/module_reference.py +8 -0
  35. clearskies_doc_builder/models/property.py +0 -0
  36. clearskies_doc_builder/prepare_doc_space.py +25 -0
  37. clear_skies_doc_builder-2.0.1.dist-info/RECORD +0 -4
  38. {clear_skies_doc_builder-2.0.1.dist-info → clear_skies_doc_builder-2.0.3.dist-info}/WHEEL +0 -0
  39. {clear_skies_doc_builder-2.0.1.dist-info → clear_skies_doc_builder-2.0.3.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clear-skies-doc-builder
3
- Version: 2.0.1
3
+ Version: 2.0.3
4
4
  Summary: The docbuilder for all 'official' clearskies plugins (as well as the main clearskies docs)
5
5
  Project-URL: repository, https://github.com/clearskies-py/docs
6
6
  Project-URL: issues, https://github.com/clearskies-py/docs/issues
@@ -0,0 +1,38 @@
1
+ clearskies_doc_builder/__init__.py,sha256=AFQzZ9HwIxPyLpsUhkSDRh_6hm8-5H4RpAelTShCDTc,1300
2
+ clearskies_doc_builder/build_callable.py,sha256=9_HrytRA5MeGpuUKRFgmlK5Mb1WKt6TfWlCOi1aS-Pc,983
3
+ clearskies_doc_builder/prepare_doc_space.py,sha256=eGDsjWaVx0gwCU9mZkrmZTsUcgeod-4PBNqSuPdfwQw,745
4
+ clearskies_doc_builder/backends/__init__.py,sha256=q5jpy8xfZ4SbGQ1T30q4mp4h346HaMivvmNqtgTMQxw,303
5
+ clearskies_doc_builder/backends/attribute_backend.py,sha256=0vlnREe3TcYjrmBFvAlTFeWzQ-LzT__wVJG7inWQLzM,3581
6
+ clearskies_doc_builder/backends/class_backend.py,sha256=6jOKzUDLYIQofCxno3Y9L_FiKT9x2MsPOe3c1HJ2k2M,4031
7
+ clearskies_doc_builder/backends/module_backend.py,sha256=hJMeJtgInaZfXnFpxtCuRA05jBZTpMhVrR8Se3uul9Y,5718
8
+ clearskies_doc_builder/backends/python.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ clearskies_doc_builder/builders/__init__.py,sha256=ndAK4-imR9vhl7CS425ASYi46FN5-eR9Dr6bKxCn5lA,292
10
+ clearskies_doc_builder/builders/builder.py,sha256=3aQ_UTKJFDDUCNHKhwn1sr74CKR3YCe7xuWRX528eAw,6133
11
+ clearskies_doc_builder/builders/module.py,sha256=IrSb5ZKyuPzsK5UXSX-UFTEx5ENH05VEOeYuZ2rnDxY,3286
12
+ clearskies_doc_builder/builders/single_class.py,sha256=3JZBVtt9GToPFoSIlggWbalRFbfayLpLVb9-2-1ymdY,3454
13
+ clearskies_doc_builder/builders/single_class_to_section.py,sha256=uEyawxW4cIdavBVnK2TBiIqywUgj3IxmUcS16E81cgI,1754
14
+ clearskies_doc_builder/columns/__init__.py,sha256=--cof8kFPwlZncM3MBQYkvfq8TejEeI0B99vTlEVCkU,566
15
+ clearskies_doc_builder/columns/any.py,sha256=5kjwEz4-t7MH9sFyycWxEcDdDmKZsb3wfX1Nmvc6QI4,998
16
+ clearskies_doc_builder/columns/attribute.py,sha256=2KGLIxCCbjc2yPZ8fnXqUjyq9pV2Rg164XA2ZquT0Lo,971
17
+ clearskies_doc_builder/columns/attributes.py,sha256=Qrk7n2rXX-yAItwuap5NKZv13I2wQl6dv9Qym3IJVQo,966
18
+ clearskies_doc_builder/columns/base_classes.py,sha256=GmVrAyfaxX9tT2WUYvz9kCkzHDdVxE2eSatzUYCsmRQ,893
19
+ clearskies_doc_builder/columns/class_column.py,sha256=lLPUONBsLVNrmpsL_gcJ6El-PG8t0Cky21DanGT_Jfs,965
20
+ clearskies_doc_builder/columns/method.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ clearskies_doc_builder/columns/module.py,sha256=LJ22plawtxJC_M8nhqUSIZVhka7cB0GD_u-35LjvOJA,1007
22
+ clearskies_doc_builder/columns/module_classes.py,sha256=4CsXcku0hHVZ0V26u1wzfHYXcHgntFo9hmj91Wn5QlU,728
23
+ clearskies_doc_builder/configs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
+ clearskies_doc_builder/models/__init__.py,sha256=SxTzmTPpBHrqzO3nef4r3fBZ_C3F7UdyOaxmOoMTIcM,402
25
+ clearskies_doc_builder/models/arg.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
+ clearskies_doc_builder/models/attribute.py,sha256=13SQbGCQ7O1fQAWKeCDIkpYtlVsrrqxb93spiabMjk4,385
27
+ clearskies_doc_builder/models/attribute_reference.py,sha256=U7ISNKfq-45X54ZbvnTMDZVMyai9cQU_gpsPhVzy5JM,189
28
+ clearskies_doc_builder/models/class_model.py,sha256=KqZ3im8OxOAAPj7bgVM8v2-n-RRoDaFCqpYDhBUpn78,899
29
+ clearskies_doc_builder/models/class_reference.py,sha256=GWcPc9OMmDe6nuFx-o3Rt0uQ_PRUe5dHMyFKOUQ4I2M,185
30
+ clearskies_doc_builder/models/method.py,sha256=rdZkG-Dx9_h0aIRLneh_jCixrykbWfCnoDfE1bhHavA,493
31
+ clearskies_doc_builder/models/method_reference.py,sha256=U4YOpRLotyEp6G0Y5OHORdo8mZOnjUzOgqOYCFBPDUw,177
32
+ clearskies_doc_builder/models/module.py,sha256=5JUF2LUKTpVsYlviT7T_qgLdMApGtS4qIUloCEzJ-uo,573
33
+ clearskies_doc_builder/models/module_reference.py,sha256=-zHnrkP6JR7j2XFuW94uGlPENI3ZshxgBu4yY9ayzSg,177
34
+ clearskies_doc_builder/models/property.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
+ clear_skies_doc_builder-2.0.3.dist-info/METADATA,sha256=2dHuWzKFnDWHbevWGNM0utGKiFcssuNQzedJATY9ngQ,3075
36
+ clear_skies_doc_builder-2.0.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
37
+ clear_skies_doc_builder-2.0.3.dist-info/licenses/LICENSE,sha256=pgH32-xrgpIDf6WIBUA1X19o-sIHIXgToCxo-tBWDdw,1071
38
+ clear_skies_doc_builder-2.0.3.dist-info/RECORD,,
@@ -0,0 +1,35 @@
1
+ import json
2
+ import pathlib
3
+ import sys
4
+
5
+ import clearskies
6
+
7
+ from clearskies_doc_builder import backends, models
8
+ from clearskies_doc_builder.build_callable import build_callable
9
+
10
+
11
+ def build(build_file_string: str) -> None:
12
+ # We assume a folder structure here where the repo root contains a `src/` folder and a `docs/python` folder.
13
+ # `build_file_string` should contain the absolute path to the file that kicked this off, which should
14
+ # live in the `docs/python` folder. This comes in as a string, which we convert to a path.
15
+ # We then also need to calculate the path to the `src/` folder and add that to our
16
+ # python path. We do this because we want to import the clearskies module in question, since the python
17
+ # code is where all of our documentation lives.
18
+
19
+ doc_python_path = pathlib.Path(build_file_string).parents[0]
20
+ project_root = doc_python_path.parents[1]
21
+ sys.path.append(str(project_root / "src"))
22
+
23
+ config_file = open(str(doc_python_path / "config.json"), "r")
24
+ config = json.loads(config_file.read())
25
+ config_file.close()
26
+
27
+ cli = clearskies.contexts.Cli(
28
+ build_callable,
29
+ modules=[models, backends],
30
+ bindings={
31
+ "config": config,
32
+ "project_root": project_root / "docs",
33
+ },
34
+ )
35
+ cli()
@@ -0,0 +1,9 @@
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
+ ]
@@ -0,0 +1,91 @@
1
+ import inspect
2
+ from typing import Any
3
+
4
+ import clearskies
5
+ import clearskies.column
6
+ import clearskies.model
7
+ import clearskies.query
8
+
9
+ from clearskies_doc_builder.backends.module_backend import ModuleBackend
10
+
11
+
12
+ class AttributeBackend(ModuleBackend):
13
+ _search_functions = {
14
+ "id": lambda attribute, name, value: id(attribute) == int(value), # type: ignore
15
+ "name": lambda attribute, name, value: name == value, # type: ignore
16
+ "type": lambda attribute, name, value: attribute.__class__ == value, # type: ignore
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
+ Return 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 AttributeBackend 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 AttributeBackend only supports searching with the equals operator")
37
+
38
+ if "parent_class" not in query.conditions_by_column:
39
+ raise ValueError("When searching for attributes you must include a condition on 'parent_class'")
40
+
41
+ parent_class = query.conditions_by_column["parent_class"][0].values[0]
42
+ matching_attributes = []
43
+ for name in dir(parent_class):
44
+ attribute = getattr(parent_class, name)
45
+ matches = True
46
+ for condition in query.conditions:
47
+ if condition.column_name not in self._search_functions:
48
+ continue
49
+ if not self._search_functions[condition.column_name](attribute, name, condition.values[0]): # type: ignore
50
+ matches = False
51
+
52
+ if not matches:
53
+ continue
54
+ matching_attributes.append(self.unpack(attribute, name, parent_class))
55
+
56
+ return self.paginate(matching_attributes, query)
57
+
58
+ def unpack(self, attribute: Any, name: str, parent_class: type) -> dict[str, Any]: # type: ignore
59
+ all_args = []
60
+ args = []
61
+ kwargs = []
62
+ defaults: dict[str, Any] = {}
63
+ argdata = None
64
+ try:
65
+ argdata = inspect.getfullargspec(attribute)
66
+ except:
67
+ pass
68
+
69
+ if argdata:
70
+ nargs = len(argdata.args)
71
+ nkwargs = len(argdata.defaults) if argdata.defaults else 0
72
+ npargs = nargs - nkwargs
73
+ all_args = argdata.args
74
+ kwargs = all_args[nargs - nkwargs :]
75
+ args = all_args[:nkwargs]
76
+ defaults = {}
77
+ if argdata.defaults:
78
+ defaults = {argdata.args[index + npargs]: default for (index, default) in enumerate(argdata.defaults)}
79
+
80
+ return {
81
+ "id": id(attribute),
82
+ "name": name,
83
+ "type": attribute.__class__,
84
+ "doc": attribute.__doc__,
85
+ "parent_class": parent_class,
86
+ "attribute": attribute,
87
+ "all_args": all_args,
88
+ "args": args,
89
+ "kwargs": kwargs,
90
+ "defaults": defaults,
91
+ }
@@ -0,0 +1,99 @@
1
+ import importlib
2
+ import inspect
3
+ from types import ModuleType
4
+ from typing import Any
5
+
6
+ import clearskies
7
+ import clearskies.column
8
+ import clearskies.model
9
+ import clearskies.query
10
+
11
+ from clearskies_doc_builder.backends.module_backend import ModuleBackend
12
+
13
+
14
+ class ClassBackend(ModuleBackend):
15
+ _search_functions = {
16
+ "id": lambda module, value: id(module) == int(value),
17
+ "source_file": lambda module, value: (module.__file__ if hasattr(module, "__file__") else "") == value,
18
+ }
19
+
20
+ def records(
21
+ self, query: clearskies.query.Query, next_page_data: dict[str, str | int] | None = None
22
+ ) -> list[dict[str, Any]]:
23
+ """
24
+ Return a list of records that match the given query configuration.
25
+
26
+ next_page_data is used to return data to the caller. Pass in an empty dictionary, and it will be populated
27
+ with the data needed to return the next page of results. If it is still an empty dictionary when returned,
28
+ then there is no additional data.
29
+ """
30
+ disallowed = ["joins", "selects", "group_by"]
31
+ for attribute_name in disallowed:
32
+ if getattr(query, attribute_name):
33
+ raise ValueError(f"The ClassBackend received {attribute_name} in a query but doesn't support this.")
34
+
35
+ for condition in query.conditions:
36
+ if condition.operator != "=":
37
+ raise ValueError("The ClassBackend only supports searching with the equals operator")
38
+
39
+ if "import_path" in query.conditions_by_column:
40
+ import_path = query.conditions_by_column["import_path"][0].values[0]
41
+ path_parts = import_path.split(".")
42
+ if len(path_parts) < 2:
43
+ raise ValueError(
44
+ '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")`'
45
+ )
46
+ class_name = path_parts[-1]
47
+ module_path = ".".join(path_parts[0:-1])
48
+ module = importlib.import_module(module_path)
49
+ if not hasattr(module, class_name):
50
+ raise ValueError(f"Module {import_path} has no class named {class_name}")
51
+ Class = getattr(module, class_name)
52
+ if not inspect.isclass(Class):
53
+ raise ValueError(
54
+ f"I was asked to import the class named '{import_path}' but this doesn't actually reference a class"
55
+ )
56
+ return [self.unpack(Class, module)]
57
+
58
+ if "module" not in query.conditions_by_column:
59
+ raise ValueError(
60
+ "When searching for classes you must include a condition on either 'module' or 'import_path'"
61
+ )
62
+
63
+ parent_module = query.conditions_by_column["module"][0].values[0]
64
+ matching_classes = []
65
+ for name in dir(parent_module):
66
+ attribute = getattr(parent_module, name)
67
+ if not inspect.isclass(attribute):
68
+ continue
69
+
70
+ matches = True
71
+ for condition in query.conditions:
72
+ if condition.column_name not in self._search_functions:
73
+ continue
74
+ if not self._search_functions[condition.column_name](attribute, condition.values[0]):
75
+ matches = False
76
+
77
+ if not matches:
78
+ continue
79
+ matching_classes.append(self.unpack(attribute, parent_module))
80
+
81
+ return self.paginate(matching_classes, query)
82
+
83
+ def unpack(self, Class: type, module: ModuleType) -> dict[str, Any]: # type: ignore
84
+ source_file = ""
85
+ try:
86
+ # this fails for built ins
87
+ source_file = inspect.getfile(Class)
88
+ except TypeError:
89
+ pass
90
+
91
+ return {
92
+ "id": id(Class),
93
+ "import_path": Class.__module__ + "." + Class.__name__,
94
+ "name": Class.__name__,
95
+ "source_file": source_file,
96
+ "doc": Class.__doc__,
97
+ "module": module,
98
+ "type": Class,
99
+ }
@@ -0,0 +1,131 @@
1
+ import importlib
2
+ import sys
3
+ from types import ModuleType
4
+ from typing import Any, Callable
5
+
6
+ import clearskies
7
+ import clearskies.column
8
+ import clearskies.model
9
+ import clearskies.query
10
+ from clearskies.autodoc.schema import Integer as AutoDocInteger
11
+ from clearskies.autodoc.schema import Schema as AutoDocSchema
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
+ """Update the record with the given id with the information from the data dictionary."""
24
+ raise Exception(f"The {self.__class__.__name__} only supports read operations: update is not allowed")
25
+
26
+ def create(self, data: dict[str, Any], model: clearskies.model.Model) -> dict[str, Any]:
27
+ """Create a record with the information from the data dictionary."""
28
+ raise Exception(f"The {self.__class__.__name__} only supports read operations: create is not allowed")
29
+
30
+ def delete(self, id: int | str, model: clearskies.model.Model) -> bool:
31
+ """Delete the record with the given id."""
32
+ raise Exception(f"The {self.__class__.__name__} only supports read operations: delete is not allowed")
33
+
34
+ def count(self, query: clearskies.query.Query) -> int:
35
+ """Return the number of records which match the given query configuration."""
36
+ return len(self.records(query))
37
+
38
+ def records(
39
+ self, query: clearskies.query.Query, next_page_data: dict[str, str | int] | None = None
40
+ ) -> list[dict[str, Any]]:
41
+ """
42
+ Return a list of records that match the given query configuration.
43
+
44
+ next_page_data is used to return data to the caller. Pass in an empty dictionary, and it will be populated
45
+ with the data needed to return the next page of results. If it is still an empty dictionary when returned,
46
+ then there is no additional data.
47
+ """
48
+ disallowed = ["joins", "selects", "group_by"]
49
+ for attribute_name in disallowed:
50
+ if getattr(query, attribute_name):
51
+ raise ValueError(f"The ModuleBackend received {attribute_name} in a query but doesn't support this.")
52
+
53
+ for condition in query.conditions:
54
+ if condition.operator != "=":
55
+ raise ValueError("The ModuleBackend only supports searching with the equals operator")
56
+
57
+ module_name_condition = query.conditions_by_column.get("import_path", query.conditions_by_column.get("name"))
58
+ if module_name_condition:
59
+ module_name = module_name_condition[0].values[0]
60
+ module = importlib.import_module(module_name)
61
+ return [self.unpack(module)]
62
+
63
+ matching_modules = []
64
+ module_names = set(sys.modules) & set(globals())
65
+ for module_name in module_names:
66
+ if module_name not in sys.modules:
67
+ continue
68
+
69
+ module = sys.modules[module_name]
70
+ matches = True
71
+ for condition in query.conditions:
72
+ if condition.column_name not in self._search_functions:
73
+ continue
74
+ if not self._search_functions[condition.column_name](module, condition.values[0]):
75
+ matches = False
76
+
77
+ if not matches:
78
+ continue
79
+
80
+ matching_modules.append(self.unpack(module))
81
+
82
+ return self.paginate(matching_modules, query)
83
+
84
+ def unpack(self, module: ModuleType) -> dict[str, Any]:
85
+ return {
86
+ "id": id(module),
87
+ "import_path": module.__name__,
88
+ "name": module.__name__,
89
+ "is_builtin": not hasattr(module, "__file__"),
90
+ "source_file": module.__file__ if hasattr(module, "__file__") else "",
91
+ "doc": module.__doc__,
92
+ "module": module,
93
+ }
94
+
95
+ def paginate(self, records, query):
96
+ return records
97
+
98
+ def validate_pagination_data(self, data: dict[str, Any], case_mapping: Callable) -> str:
99
+ extra_keys = set(data.keys()) - set(self.allowed_pagination_keys())
100
+ if len(extra_keys):
101
+ key_name = case_mapping("start")
102
+ return "Invalid pagination key(s): '" + "','".join(extra_keys) + f"'. Only '{key_name}' is allowed"
103
+ if "start" not in data:
104
+ key_name = case_mapping("start")
105
+ return f"You must specify '{key_name}' when setting pagination"
106
+ start = data["start"]
107
+ try:
108
+ start = int(start)
109
+ except:
110
+ key_name = case_mapping("start")
111
+ return f"Invalid pagination data: '{key_name}' must be a number"
112
+ return ""
113
+
114
+ def allowed_pagination_keys(self) -> list[str]:
115
+ return ["start"]
116
+
117
+ def documentation_pagination_next_page_response(self, case_mapping: Callable[[str], str]) -> list[Any]:
118
+ return [AutoDocInteger(case_mapping("start"), example=0)]
119
+
120
+ def documentation_pagination_next_page_example(self, case_mapping: Callable[[str], str]) -> dict[str, Any]:
121
+ return {case_mapping("start"): 0}
122
+
123
+ def documentation_pagination_parameters(
124
+ self, case_mapping: Callable[[str], str]
125
+ ) -> list[tuple[AutoDocSchema, str]]:
126
+ return [
127
+ (
128
+ AutoDocInteger(case_mapping("start"), example=0),
129
+ "The zero-indexed record number to start listing results from",
130
+ )
131
+ ]
File without changes
@@ -0,0 +1,24 @@
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()
@@ -0,0 +1,9 @@
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
+ ]
@@ -0,0 +1,136 @@
1
+ import pathlib
2
+ import re
3
+ import tokenize
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
@@ -0,0 +1,69 @@
1
+ from collections import OrderedDict
2
+ from typing import Any
3
+
4
+ import clearskies
5
+
6
+ from .builder import Builder
7
+
8
+
9
+ class Module(Builder):
10
+ def __init__(self, branch, modules, classes, doc_root, nav_order):
11
+ super().__init__(branch, modules, classes, doc_root, nav_order)
12
+ self.class_list = branch["classes"]
13
+ self.args_to_additional_attributes_map = branch.get("args_to_additional_attributes_map", {})
14
+
15
+ def build(self):
16
+ title_snake_case = clearskies.functional.string.title_case_to_snake_case(self.title.replace(" ", "")).replace(
17
+ "_", "-"
18
+ )
19
+ section_folder_path = self.doc_root / title_snake_case
20
+ source_class = self.classes.find(f"import_path={self.source}")
21
+ self.make_index_from_class_overview(title_snake_case, source_class, section_folder_path)
22
+
23
+ default_args = self.default_args()
24
+
25
+ nav_order = 0
26
+ for class_name in self.class_list:
27
+ nav_order += 1
28
+ source_class = self.classes.find(f"import_path={class_name}")
29
+ title = source_class.name
30
+ filename = clearskies.functional.string.title_case_to_snake_case(source_class.name).replace("_", "-")
31
+ class_doc = self.build_header(source_class.name, filename, title_snake_case, self.title, nav_order, False)
32
+ (elevator_pitch, overview) = self.parse_overview_doc(
33
+ self.raw_docblock_to_md(source_class.doc).lstrip("\n").lstrip(" ")
34
+ )
35
+ class_doc += f"\n\n# {title}\n\n{elevator_pitch}\n\n"
36
+ main_doc = "## Overview\n\n{overview}\n\n"
37
+ table_of_contents = " 1. [Overview](#overview)\n"
38
+
39
+ # Find the documentation for all of our init args.
40
+ arguments: dict[str, Any] = OrderedDict()
41
+ for arg in source_class.init.all_args:
42
+ if arg == "self":
43
+ continue
44
+ arguments[arg] = {
45
+ "required": arg not in source_class.init.kwargs,
46
+ "doc": default_args.get(arg, ""),
47
+ }
48
+
49
+ # for various reasons, it's easier to extract docs for all the arguments at once:
50
+ docs = self.extract_attribute_docs(source_class, list(arguments.keys()))
51
+ for arg, doc in docs.items():
52
+ # you would think that we would only get arguments that belong to our class, but this isn't the case
53
+ # because the processing caches results from parent classes, and we don't always use all attributes
54
+ # available from all our parents.
55
+ if arg not in arguments:
56
+ continue
57
+ arguments[arg]["doc"] = doc
58
+
59
+ for index, arg in enumerate(arguments.keys()):
60
+ arg_data = arguments[arg]
61
+ table_of_contents += f" {index + 2}. [{arg}](#{arg})\n"
62
+ main_doc += f"## {arg}\n**" + ("Required" if arg_data["required"] else "Optional") + "**\n\n"
63
+ main_doc += self.raw_docblock_to_md(arg_data["doc"].replace('"""', "")) + "\n\n"
64
+
65
+ class_doc += f"{table_of_contents}\n{main_doc}"
66
+
67
+ output_file = section_folder_path / f"{filename}.md"
68
+ with output_file.open(mode="w") as doc_file:
69
+ doc_file.write(class_doc)
@@ -0,0 +1,79 @@
1
+ from collections import OrderedDict
2
+ from typing import Any
3
+
4
+ import clearskies
5
+
6
+ from .builder import Builder
7
+
8
+
9
+ class SingleClass(Builder):
10
+ def __init__(self, branch, modules, classes, doc_root, nav_order):
11
+ super().__init__(branch, modules, classes, doc_root, nav_order)
12
+ self.additional_attribute_sources = branch.get("additional_attribute_sources", [])
13
+ self.args_to_additional_attributes_map = branch.get("args_to_additional_attributes_map", {})
14
+ self.parent = branch.get("parent", False)
15
+
16
+ def build(self):
17
+ section_name = (
18
+ clearskies.functional.string.title_case_to_snake_case(self.parent if self.parent else self.title)
19
+ .replace("_", "-")
20
+ .replace(" ", "")
21
+ )
22
+ section_folder_path = self.doc_root / section_name
23
+ section_folder_path.mkdir(exist_ok=True)
24
+ source_class = self.classes.find(f"import_path={self.source}")
25
+
26
+ title_snake_case = clearskies.functional.string.title_case_to_snake_case(self.title.replace(" ", "")).replace(
27
+ "_", "-"
28
+ )
29
+ class_doc = self.build_header(self.title, title_snake_case, section_name, self.parent, self.nav_order, False)
30
+ (elevator_pitch, overview) = self.parse_overview_doc(
31
+ self.raw_docblock_to_md(source_class.doc).lstrip("\n").lstrip(" ")
32
+ )
33
+ class_doc += f"\n\n# {self.title}\n\n{elevator_pitch}\n\n"
34
+ main_doc = f"## Overview\n\n{overview}\n\n"
35
+ table_of_contents = " 1. [Overview](#overview)\n"
36
+
37
+ default_args = self.default_args()
38
+
39
+ # Find the documentation for all of our init args.
40
+ arguments: dict[str, Any] = OrderedDict()
41
+ for arg in source_class.init.all_args:
42
+ if arg == "self":
43
+ continue
44
+ arguments[arg] = {
45
+ "required": arg not in source_class.init.kwargs,
46
+ "doc": default_args.get(arg, ""),
47
+ }
48
+
49
+ # for various reasons, it's easier to extract docs for all the arguments at once:
50
+ docs = self.extract_attribute_docs(
51
+ source_class,
52
+ list(arguments.keys()),
53
+ additional_attribute_sources=[
54
+ self.classes.find(f"import_path={source}") for source in self.additional_attribute_sources
55
+ ],
56
+ )
57
+ for arg, doc in docs.items():
58
+ # you would think that we would only get arguments that belong to our class, but this isn't the case
59
+ # because the processing caches results from parent classes, and we don't always use all attributes
60
+ # available from all our parents.
61
+ if arg not in arguments:
62
+ continue
63
+ arguments[arg]["doc"] = doc
64
+
65
+ for index, arg in enumerate(arguments.keys()):
66
+ arg_data = arguments[arg]
67
+ table_of_contents += f" {index + 2}. [{arg}](#{arg})\n"
68
+ main_doc += f"## {arg}\n**" + ("Required" if arg_data["required"] else "Optional") + "**\n\n"
69
+ main_doc += self.raw_docblock_to_md(arg_data["doc"].replace('"""', "")) + "\n\n"
70
+
71
+ class_doc += f"{table_of_contents}\n{main_doc}"
72
+
73
+ output_file = section_folder_path / (
74
+ "index.md"
75
+ if not self.parent
76
+ else clearskies.functional.string.title_case_to_snake_case(self.title.replace(" ", "")) + ".md"
77
+ )
78
+ with output_file.open(mode="w") as doc_file:
79
+ doc_file.write(class_doc)
@@ -0,0 +1,39 @@
1
+ import re
2
+
3
+ import clearskies
4
+
5
+ from .builder import Builder
6
+
7
+
8
+ class SingleClassToSection(Builder):
9
+ def __init__(self, branch, modules, classes, doc_root, nav_order):
10
+ super().__init__(branch, modules, classes, doc_root, nav_order)
11
+ self.docs = branch["docs"]
12
+
13
+ def build(self):
14
+ section_name = clearskies.functional.string.title_case_to_snake_case(self.title).replace("_", "-")
15
+ section_folder_path = self.doc_root / section_name
16
+ source_class = self.classes.find(f"import_path={self.source}")
17
+ self.make_index_from_class_overview(section_name, source_class, section_folder_path)
18
+
19
+ for index, doc_data in enumerate(self.docs):
20
+ title = doc_data["title"]
21
+ title_snake_case = clearskies.functional.string.title_case_to_snake_case(title.replace(" ", "")).replace(
22
+ "_", "-"
23
+ )
24
+ doc = self.build_header(title, title_snake_case, section_name, self.title, index + 1, False)
25
+ doc += f"\n\n# {title}\n\n"
26
+ table_of_contents = ""
27
+ attribute_docs = ""
28
+
29
+ for index, attribute_name in enumerate(doc_data["attributes"]):
30
+ attribute = source_class.attributes.find(f"name={attribute_name}")
31
+ table_of_contents += f" {index + 1}. [{attribute_name}]({title_snake_case}.html#{attribute_name})\n"
32
+ attribute_docs += f"\n\n## {attribute_name}\n\n"
33
+ attribute_docs += re.sub("\n ", "\n", self.raw_docblock_to_md(attribute.doc))
34
+
35
+ doc += f"{table_of_contents}{attribute_docs}"
36
+
37
+ output_file = section_folder_path / f"{title_snake_case}.md"
38
+ with output_file.open(mode="w") as doc_file:
39
+ doc_file.write(doc)
@@ -0,0 +1,17 @@
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
+ ]
@@ -0,0 +1,39 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Self, overload
4
+ from typing import Any as AnyType
5
+
6
+ import clearskies
7
+
8
+ if TYPE_CHECKING:
9
+ from clearskies import Model
10
+
11
+
12
+ class Any(clearskies.Column):
13
+ is_writeable = clearskies.configs.boolean.Boolean(default=False)
14
+ _descriptor_config_map = None
15
+
16
+ def __init__(self):
17
+ super().__init__()
18
+
19
+ @overload
20
+ def __get__(self, instance: None, cls: type[Model]) -> Self:
21
+ pass
22
+
23
+ @overload
24
+ def __get__(self, instance: Model, cls: type[Model]) -> AnyType:
25
+ pass
26
+
27
+ def __get__(self, instance, cls):
28
+ return super().__get__(instance, cls)
29
+
30
+ def __set__(self, instance: Model, value: AnyType) -> None:
31
+ instance._next_data[self.name] = value
32
+
33
+ def from_backend(self, value):
34
+ return value
35
+
36
+ def to_backend(self, data: dict[str, Any]) -> dict[str, Any]:
37
+ if self.name not in data:
38
+ return data
39
+ return {**data, self.name: data[self.name]}
@@ -0,0 +1,35 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Callable
4
+
5
+ import clearskies
6
+
7
+
8
+ class Attribute(clearskies.columns.HasMany):
9
+ def __init__(
10
+ self,
11
+ child_model_class,
12
+ readable_child_column_names: list[str] = [],
13
+ filter: Callable | None = None,
14
+ ):
15
+ self.filter = filter
16
+ super().__init__(
17
+ child_model_class,
18
+ foreign_column_name="parent_class",
19
+ readable_child_column_names=readable_child_column_names,
20
+ )
21
+
22
+ def __get__(self, model, cls):
23
+ if model is None:
24
+ self.model_class = cls
25
+ return self # type: ignore
26
+
27
+ # this makes sure we're initialized
28
+ if "name" not in self._config:
29
+ model.get_columns()
30
+
31
+ attributes = self.child_model.where(self.child_model_class.parent_class.equals(model.type))
32
+ if self.filter:
33
+ return list(filter(self.filter, attributes))[0]
34
+
35
+ return attributes[0]
@@ -0,0 +1,35 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Callable
4
+
5
+ import clearskies
6
+
7
+
8
+ class Attributes(clearskies.columns.HasMany):
9
+ def __init__(
10
+ self,
11
+ child_model_class,
12
+ readable_child_column_names: list[str] = [],
13
+ filter: Callable | None = None,
14
+ ):
15
+ self.filter = filter
16
+ super().__init__(
17
+ child_model_class,
18
+ foreign_column_name="parent_class",
19
+ readable_child_column_names=readable_child_column_names,
20
+ )
21
+
22
+ def __get__(self, model, cls):
23
+ if model is None:
24
+ self.model_class = cls
25
+ return self # type: ignore
26
+
27
+ # this makes sure we're initialized
28
+ if "name" not in self._config:
29
+ model.get_columns()
30
+
31
+ attributes = self.child_model.where(self.child_model_class.parent_class.equals(model.type))
32
+ if self.filter:
33
+ return list(filter(self.filter, attributes))
34
+
35
+ return attributes
@@ -0,0 +1,31 @@
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
@@ -0,0 +1,38 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any, Self, overload
4
+
5
+ import clearskies
6
+
7
+ if TYPE_CHECKING:
8
+ from clearskies import Model
9
+
10
+
11
+ class Class(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]) -> type:
24
+ pass
25
+
26
+ def __get__(self, instance, cls):
27
+ return super().__get__(instance, cls)
28
+
29
+ def __set__(self, instance: Model, value: type) -> 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]}
File without changes
@@ -0,0 +1,39 @@
1
+ from __future__ import annotations
2
+
3
+ from types import ModuleType
4
+ from typing import TYPE_CHECKING, Any, Self, overload
5
+
6
+ import clearskies
7
+
8
+ if TYPE_CHECKING:
9
+ from clearskies import Model
10
+
11
+
12
+ class Module(clearskies.Column):
13
+ is_writeable = clearskies.configs.boolean.Boolean(default=False)
14
+ _descriptor_config_map = None
15
+
16
+ def __init__(self):
17
+ super().__init__()
18
+
19
+ @overload
20
+ def __get__(self, instance: None, cls: type[Model]) -> Self:
21
+ pass
22
+
23
+ @overload
24
+ def __get__(self, instance: Model, cls: type[Model]) -> ModuleType:
25
+ pass
26
+
27
+ def __get__(self, instance, cls):
28
+ return super().__get__(instance, cls)
29
+
30
+ def __set__(self, instance: Model, value: ModuleType) -> None:
31
+ instance._next_data[self.name] = value
32
+
33
+ def from_backend(self, value):
34
+ return value
35
+
36
+ def to_backend(self, data: dict[str, Any]) -> dict[str, Any]:
37
+ if self.name not in data:
38
+ return data
39
+ return {**data, self.name: data[self.name]}
@@ -0,0 +1,27 @@
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))
File without changes
@@ -0,0 +1,13 @@
1
+ from clearskies_doc_builder.models.attribute import Attribute
2
+ from clearskies_doc_builder.models.class_model import Class
3
+ from clearskies_doc_builder.models.class_reference import ClassReference
4
+ from clearskies_doc_builder.models.method import Method
5
+ from clearskies_doc_builder.models.module import Module
6
+
7
+ __all__ = [
8
+ "Attribute",
9
+ "Class",
10
+ "ClassReference",
11
+ "Method",
12
+ "Module",
13
+ ]
File without changes
@@ -0,0 +1,15 @@
1
+ import clearskies
2
+
3
+ from clearskies_doc_builder import backends, columns
4
+
5
+
6
+ class Attribute(clearskies.Model):
7
+ id_column_name = "id"
8
+ backend = backends.AttributeBackend()
9
+
10
+ id = clearskies.columns.Integer()
11
+ name = clearskies.columns.String()
12
+ type = columns.Class()
13
+ doc = clearskies.columns.String()
14
+ attribute = columns.Any()
15
+ parent_class = columns.Class()
@@ -0,0 +1,8 @@
1
+ from clearskies.model import ModelClassReference
2
+
3
+ from . import attribute
4
+
5
+
6
+ class AttributeReference(ModelClassReference):
7
+ def get_model_class(self):
8
+ return attribute.Attribute
@@ -0,0 +1,26 @@
1
+ import clearskies
2
+
3
+ from clearskies_doc_builder import backends, columns
4
+
5
+ from .attribute_reference import AttributeReference
6
+ from .method_reference import MethodReference
7
+
8
+
9
+ class Class(clearskies.Model):
10
+ id_column_name = "id"
11
+ backend = backends.ClassBackend()
12
+
13
+ id = clearskies.columns.Integer()
14
+ type = columns.Any()
15
+ source_file = clearskies.columns.String()
16
+ import_path = clearskies.columns.String()
17
+ name = clearskies.columns.String(is_searchable=False)
18
+ doc = clearskies.columns.String(is_searchable=False)
19
+ module = columns.Module()
20
+ base_classes = columns.BaseClasses()
21
+ attributes = columns.Attributes(AttributeReference)
22
+ methods = columns.Attributes(AttributeReference, filter=lambda attribute: callable(attribute.attribute))
23
+ init = columns.Attribute(
24
+ MethodReference,
25
+ filter=lambda attribute: attribute.name == "__init__",
26
+ )
@@ -0,0 +1,8 @@
1
+ from clearskies.model import ModelClassReference
2
+
3
+ from . import class_model
4
+
5
+
6
+ class ClassReference(ModelClassReference):
7
+ def get_model_class(self):
8
+ return class_model.Class
@@ -0,0 +1,20 @@
1
+ import clearskies
2
+
3
+ from clearskies_doc_builder import backends, columns
4
+
5
+
6
+ class Method(clearskies.Model):
7
+ id_column_name = "id"
8
+ backend = backends.AttributeBackend()
9
+
10
+ id = clearskies.columns.Integer()
11
+ name = clearskies.columns.String()
12
+ type = columns.Class()
13
+ doc = clearskies.columns.String()
14
+ attribute = columns.Any()
15
+ parent_class = columns.Class()
16
+
17
+ args = columns.Any()
18
+ kwargs = columns.Any()
19
+ all_args = columns.Any()
20
+ defaults = columns.Any()
@@ -0,0 +1,8 @@
1
+ from clearskies.model import ModelClassReference
2
+
3
+ from . import method
4
+
5
+
6
+ class MethodReference(ModelClassReference):
7
+ def get_model_class(self):
8
+ return method.Method
@@ -0,0 +1,19 @@
1
+ import clearskies
2
+
3
+ from clearskies_doc_builder import backends, columns
4
+
5
+ from . import class_reference
6
+
7
+
8
+ class Module(clearskies.Model):
9
+ id_column_name = "id"
10
+ backend = backends.ModuleBackend()
11
+
12
+ id = clearskies.columns.Integer()
13
+ import_path = clearskies.columns.String()
14
+ source_file = clearskies.columns.String()
15
+ is_builtin = clearskies.columns.Boolean()
16
+ name = clearskies.columns.String()
17
+ doc = clearskies.columns.String(is_searchable=False)
18
+ module = columns.Module()
19
+ classes = columns.ModuleClasses(class_reference.ClassReference)
@@ -0,0 +1,8 @@
1
+ from clearskies.model import ModelClassReference
2
+
3
+ from . import module
4
+
5
+
6
+ class ModuleReference(ModelClassReference):
7
+ def get_model_class(self):
8
+ return module.Module
File without changes
@@ -0,0 +1,25 @@
1
+ import pathlib
2
+ import shutil
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
+ includes_path = jekyll_path / "_includes"
11
+
12
+ if doc_path.is_dir():
13
+ shutil.rmtree(doc_path)
14
+ build_path.mkdir(parents=True, exist_ok=True)
15
+
16
+ for file in jekyll_path.glob("*"):
17
+ if not file.is_file():
18
+ continue
19
+ shutil.copy2(str(file), str(build_path / file.name))
20
+
21
+ shutil.copytree(str(jekyll_path / "docs"), str(build_path / "docs"))
22
+ if includes_path.is_dir():
23
+ shutil.copytree(str(includes_path), str(build_path / "_includes"))
24
+
25
+ return str(doc_path)
@@ -1,4 +0,0 @@
1
- clear_skies_doc_builder-2.0.1.dist-info/METADATA,sha256=lulMc1B5pmXTvq2qrhrAAUVhsnBqyNy2wcbpAX4rPDo,3075
2
- clear_skies_doc_builder-2.0.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
3
- clear_skies_doc_builder-2.0.1.dist-info/licenses/LICENSE,sha256=pgH32-xrgpIDf6WIBUA1X19o-sIHIXgToCxo-tBWDdw,1071
4
- clear_skies_doc_builder-2.0.1.dist-info/RECORD,,