mkdocstrings-matlab 0.1.0__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. mkdocstrings_matlab-0.1.0/.gitignore +196 -0
  2. mkdocstrings_matlab-0.1.0/LICENSE +21 -0
  3. mkdocstrings_matlab-0.1.0/PKG-INFO +17 -0
  4. mkdocstrings_matlab-0.1.0/README.md +3 -0
  5. mkdocstrings_matlab-0.1.0/debug.py +17 -0
  6. mkdocstrings_matlab-0.1.0/docs/index.md +25 -0
  7. mkdocstrings_matlab-0.1.0/mkdocs.yml +28 -0
  8. mkdocstrings_matlab-0.1.0/pyproject.toml +30 -0
  9. mkdocstrings_matlab-0.1.0/src/mkdocstrings_handlers/matlab/__init__.py +5 -0
  10. mkdocstrings_matlab-0.1.0/src/mkdocstrings_handlers/matlab/handler.py +383 -0
  11. mkdocstrings_matlab-0.1.0/src/mkdocstrings_handlers/matlab/matlab/+docstring/+case/builtin.m +8 -0
  12. mkdocstrings_matlab-0.1.0/src/mkdocstrings_handlers/matlab/matlab/+docstring/+case/class.m +12 -0
  13. mkdocstrings_matlab-0.1.0/src/mkdocstrings_handlers/matlab/matlab/+docstring/+case/func.m +5 -0
  14. mkdocstrings_matlab-0.1.0/src/mkdocstrings_handlers/matlab/matlab/+docstring/+case/method.m +6 -0
  15. mkdocstrings_matlab-0.1.0/src/mkdocstrings_handlers/matlab/matlab/+docstring/+case/namespace.m +28 -0
  16. mkdocstrings_matlab-0.1.0/src/mkdocstrings_handlers/matlab/matlab/+docstring/+metadata/argument.m +58 -0
  17. mkdocstrings_matlab-0.1.0/src/mkdocstrings_handlers/matlab/matlab/+docstring/+metadata/class.m +44 -0
  18. mkdocstrings_matlab-0.1.0/src/mkdocstrings_handlers/matlab/matlab/+docstring/+metadata/func.m +14 -0
  19. mkdocstrings_matlab-0.1.0/src/mkdocstrings_handlers/matlab/matlab/+docstring/+metadata/namespace.m +14 -0
  20. mkdocstrings_matlab-0.1.0/src/mkdocstrings_handlers/matlab/matlab/+docstring/+metadata/property.m +41 -0
  21. mkdocstrings_matlab-0.1.0/src/mkdocstrings_handlers/matlab/matlab/+docstring/+metadata/script.m +15 -0
  22. mkdocstrings_matlab-0.1.0/src/mkdocstrings_handlers/matlab/matlab/+docstring/+utils/dedent.m +34 -0
  23. mkdocstrings_matlab-0.1.0/src/mkdocstrings_handlers/matlab/matlab/+docstring/+utils/get_namespace_path.m +26 -0
  24. mkdocstrings_matlab-0.1.0/src/mkdocstrings_handlers/matlab/matlab/+docstring/+utils/parse_doc.m +21 -0
  25. mkdocstrings_matlab-0.1.0/src/mkdocstrings_handlers/matlab/matlab/+docstring/exception.m +7 -0
  26. mkdocstrings_matlab-0.1.0/src/mkdocstrings_handlers/matlab/matlab/+docstring/resolve.m +94 -0
  27. mkdocstrings_matlab-0.1.0/src/mkdocstrings_handlers/matlab/matlab/matlab_startup.m +21 -0
  28. mkdocstrings_matlab-0.1.0/src/mkdocstrings_handlers/matlab/models.py +127 -0
  29. mkdocstrings_matlab-0.1.0/src/mkdocstrings_handlers/matlab/py.typed +0 -0
@@ -0,0 +1,196 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # poetry
98
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102
+ #poetry.lock
103
+
104
+ # pdm
105
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106
+ #pdm.lock
107
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108
+ # in version control.
109
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
110
+ .pdm.toml
111
+ .pdm-python
112
+ .pdm-build/
113
+
114
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
115
+ __pypackages__/
116
+
117
+ # Celery stuff
118
+ celerybeat-schedule
119
+ celerybeat.pid
120
+
121
+ # SageMath parsed files
122
+ *.sage.py
123
+
124
+ # Environments
125
+ .env
126
+ .venv
127
+ env/
128
+ venv/
129
+ ENV/
130
+ env.bak/
131
+ venv.bak/
132
+
133
+ # Spyder project settings
134
+ .spyderproject
135
+ .spyproject
136
+
137
+ # Rope project settings
138
+ .ropeproject
139
+
140
+ # mkdocs documentation
141
+ /site
142
+
143
+ # mypy
144
+ .mypy_cache/
145
+ .dmypy.json
146
+ dmypy.json
147
+
148
+ # Pyre type checker
149
+ .pyre/
150
+
151
+ # pytype static type analyzer
152
+ .pytype/
153
+
154
+ # Cython debug symbols
155
+ cython_debug/
156
+
157
+ # PyCharm
158
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
159
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
160
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
161
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
162
+ #.idea/
163
+
164
+
165
+ # Windows default autosave extension
166
+ *.asv
167
+
168
+ # OSX / *nix default autosave extension
169
+ *.m~
170
+
171
+ # Compiled MEX binaries (all platforms)
172
+ *.mex*
173
+
174
+ # Packaged app and toolbox files
175
+ *.mlappinstall
176
+ *.mltbx
177
+
178
+ # Generated helpsearch folders
179
+ helpsearch*/
180
+
181
+ # Simulink code generation folders
182
+ slprj/
183
+ sccprj/
184
+
185
+ # Matlab code generation folders
186
+ codegen/
187
+
188
+ # Simulink autosave extension
189
+ *.autosave
190
+
191
+ # Simulink cache files
192
+ *.slxc
193
+
194
+ # Octave session info
195
+ octave-workspace
196
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Mark Shui Hu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,17 @@
1
+ Metadata-Version: 2.3
2
+ Name: mkdocstrings-matlab
3
+ Version: 0.1.0
4
+ Summary: Add your description here
5
+ Author-email: Mark Hu <mark.hu@asml.com>
6
+ License-File: LICENSE
7
+ Requires-Dist: griffe>=1.2.0
8
+ Requires-Dist: markdown>=3.7
9
+ Requires-Dist: mkdocs-material>=9.5.33
10
+ Requires-Dist: mkdocs>=1.6.0
11
+ Requires-Dist: mkdocstrings>=0.25.2
12
+ Requires-Dist: mkdocstrings[python]>=0.18
13
+ Description-Content-Type: text/markdown
14
+
15
+ # mkdocstrings-matlab
16
+
17
+ Describe your project here.
@@ -0,0 +1,3 @@
1
+ # mkdocstrings-matlab
2
+
3
+ Describe your project here.
@@ -0,0 +1,17 @@
1
+ from mkdocs.commands import serve
2
+ from pathlib import Path
3
+
4
+ config_file = str(Path(__file__).parent / "mkdocs.yml")
5
+
6
+ kwargs = {
7
+ "dev_addr": None,
8
+ "open_in_browser": False,
9
+ "livereload": True,
10
+ "build_type": None,
11
+ "watch_theme": True,
12
+ "config_file": config_file,
13
+ "strict": None,
14
+ "theme": None,
15
+ "use_directory_urls": None,
16
+ }
17
+ serve.serve(**kwargs)
@@ -0,0 +1,25 @@
1
+
2
+ # Docs
3
+
4
+ <!-- ::: matlab_startupP455W0rd
5
+
6
+ handler: matlab -->
7
+
8
+ <!-- ::: docstring.resolve
9
+ handler: matlab -->
10
+
11
+ <!-- ::: mkdocstrings_handlers.matlab.handler.MatlabHandler
12
+ handler: python
13
+ options:
14
+ show_source: false
15
+ members:
16
+ - collect -->
17
+
18
+ ::: ghtest.testrunner
19
+ handler: matlab
20
+ options:
21
+ members:
22
+ - run
23
+ - get_suite
24
+ - get_runner
25
+
@@ -0,0 +1,28 @@
1
+ site_name: "mkdocstrings-matlab"
2
+ repo_name: "watermarkhu/mkdocstrings-matlab"
3
+ site_dir: "site"
4
+
5
+ nav:
6
+ - Home: index.md
7
+
8
+ theme:
9
+ name: material
10
+
11
+ watch:
12
+ - src
13
+
14
+ plugins:
15
+ - search
16
+ - autorefs
17
+ - mkdocstrings:
18
+ default_handler: python
19
+ handlers:
20
+ # python:
21
+ # paths: ["src"]
22
+ # options:
23
+ # show_root_heading: true
24
+ # show_docstring_attributes: false
25
+ matlab:
26
+ paths: ["/home/mahu/repositories/github-matlab-test-action/src"]
27
+ options:
28
+ show_root_heading: true
@@ -0,0 +1,30 @@
1
+ [project]
2
+ name = "mkdocstrings-matlab"
3
+ version = "0.1.0"
4
+ description = "Add your description here"
5
+ authors = [
6
+ { name = "Mark Hu", email = "mark.hu@asml.com" }
7
+ ]
8
+ dependencies = [
9
+ "mkdocs>=1.6.0",
10
+ "mkdocstrings>=0.25.2",
11
+ "mkdocstrings[python]>=0.18",
12
+ "griffe>=1.2.0",
13
+ "mkdocs-material>=9.5.33",
14
+ "markdown>=3.7",
15
+ ]
16
+ readme = "README.md"
17
+
18
+ [build-system]
19
+ requires = ["hatchling"]
20
+ build-backend = "hatchling.build"
21
+
22
+ [tool.rye]
23
+ managed = true
24
+ dev-dependencies = []
25
+
26
+ [tool.hatch.metadata]
27
+ allow-direct-references = true
28
+
29
+ [tool.hatch.build.targets.wheel]
30
+ packages = ["src/mkdocstrings_handlers"]
@@ -0,0 +1,5 @@
1
+ """MATLAB handler for mkdocstrings."""
2
+
3
+ from mkdocstrings_handlers.matlab.handler import get_handler
4
+
5
+ __all__ = ["get_handler"]
@@ -0,0 +1,383 @@
1
+ from pathlib import Path
2
+ from collections import ChainMap
3
+ from markdown import Markdown
4
+ from mkdocstrings.handlers.base import BaseHandler, CollectorItem, CollectionError
5
+ from mkdocstrings_handlers.python import rendering
6
+ from typing import Any, ClassVar, Mapping
7
+ from griffe import Docstring, Parameters, Parameter, ParameterKind
8
+ from pprint import pprint
9
+
10
+
11
+ import json
12
+
13
+
14
+ from mkdocstrings_handlers.matlab_engine import MatlabEngine, MatlabExecutionError
15
+ from mkdocstrings_handlers.matlab.models import Function, Class, Classfolder, Namespace, Property
16
+
17
+
18
+ ROOT_NAMESPACE = Namespace("", filepath="")
19
+ MODELS = {}
20
+
21
+
22
+ class MatlabHandler(BaseHandler):
23
+ """The `MatlabHandler` class is a handler for processing Matlab code documentation.
24
+
25
+ Attributes:
26
+ name (str): The handler's name.
27
+ domain (str): The cross-documentation domain/language for this handler.
28
+ enable_inventory (bool): Whether this handler is interested in enabling the creation of the `objects.inv` Sphinx inventory file.
29
+ fallback_theme (str): The fallback theme.
30
+ fallback_config (ClassVar[dict]): The configuration used to collect item during autorefs fallback.
31
+ default_config (ClassVar[dict]): The default configuration for the handler.
32
+
33
+ Methods:
34
+ __init__(self, *args, config_file_path=None, paths=None, startup_expression="", locale="en", **kwargs): Initializes a new instance of the `MatlabHandler` class.
35
+ get_templates_dir(self, handler=None): Returns the templates directory for the handler.
36
+ collect(self, identifier, config): Collects the documentation for the given identifier.
37
+ render(self, data, config): Renders the collected documentation data.
38
+ update_env(self, md, config): Updates the Jinja environment with custom filters and tests.
39
+ """
40
+
41
+ name: str = "matlab"
42
+ """The handler's name."""
43
+ domain: str = "mat" # to match Sphinx's default domain
44
+ """The cross-documentation domain/language for this handler."""
45
+ enable_inventory: bool = True
46
+ """Whether this handler is interested in enabling the creation of the `objects.inv` Sphinx inventory file."""
47
+ fallback_theme = "material"
48
+ """The fallback theme."""
49
+ fallback_config: ClassVar[dict] = {"fallback": True}
50
+ """The configuration used to collect item during autorefs fallback."""
51
+ default_config: ClassVar[dict] = {
52
+ # https://mkdocstrings.github.io/python/usage/
53
+ # General options
54
+ # "find_stubs_package": False,
55
+ # "allow_inspection": True,
56
+ "show_bases": True,
57
+ "show_inheritance_diagram": False, # not implemented
58
+ "show_source": False, # not implemented
59
+ # "preload_modules": None,
60
+ # Heading options
61
+ "heading_level": 2,
62
+ "parameter_headings": False,
63
+ "show_root_heading": False,
64
+ "show_root_toc_entry": True,
65
+ "show_root_full_path": True,
66
+ "show_root_members_full_path": False,
67
+ "show_object_full_path": False,
68
+ "show_category_heading": False,
69
+ "show_symbol_type_heading": False,
70
+ "show_symbol_type_toc": False,
71
+ # Member options
72
+ "inherited_members": False,
73
+ "members": None,
74
+ "members_order": rendering.Order.alphabetical.value,
75
+ "filters": ["!^_[^_]"],
76
+ "group_by_category": True,
77
+ "show_submodules": False,
78
+ "summary": False,
79
+ "show_labels": True,
80
+ # Docstring options
81
+ "docstring_style": "google",
82
+ "docstring_options": {},
83
+ "docstring_section_style": "table",
84
+ "merge_init_into_class": False,
85
+ "show_if_no_docstring": False,
86
+ "show_docstring_attributes": True,
87
+ "show_docstring_functions": True,
88
+ "show_docstring_classes": True,
89
+ "show_docstring_modules": True,
90
+ "show_docstring_description": True,
91
+ "show_docstring_examples": True,
92
+ "show_docstring_other_parameters": True,
93
+ "show_docstring_parameters": True,
94
+ "show_docstring_raises": True,
95
+ "show_docstring_receives": True,
96
+ "show_docstring_returns": True,
97
+ "show_docstring_warns": True,
98
+ "show_docstring_yields": True,
99
+ # Signature options
100
+ "annotations_path": "brief",
101
+ "line_length": 60,
102
+ "show_signature": True,
103
+ "show_signature_annotations": False,
104
+ "signature_crossrefs": False,
105
+ "separate_signature": True,
106
+ "unwrap_annotated": False,
107
+ "modernize_annotations": False,
108
+ }
109
+
110
+ def __init__(
111
+ self,
112
+ *args: Any,
113
+ config_file_path: str | None = None,
114
+ paths: list[str] | None = None,
115
+ startup_expression: str = "",
116
+ locale: str = "en",
117
+ **kwargs: Any,
118
+ ) -> None:
119
+ super().__init__(*args, **kwargs)
120
+
121
+ if paths is None:
122
+ paths = ""
123
+
124
+ self.engine = MatlabEngine()
125
+ self.engine.addpath(str(Path(__file__).parent / "matlab"))
126
+ self.engine.matlab_startup(paths, startup_expression)
127
+ self._locale = locale
128
+
129
+ def get_templates_dir(self, handler: str | None = None) -> Path:
130
+ # use the python handler templates
131
+ # (it assumes the python handler is installed)
132
+ return super().get_templates_dir("python")
133
+
134
+ def collect(self, identifier: str, config: Mapping[str, Any]) -> CollectorItem:
135
+ """Collect data given an identifier and user configuration.
136
+
137
+ In the implementation, you typically call a subprocess that returns JSON, and load that JSON again into
138
+ a Python dictionary for example, though the implementation is completely free.
139
+
140
+ Arguments:
141
+ identifier: An identifier for which to collect data.
142
+ config: The handler's configuration options.
143
+
144
+ Returns:
145
+ CollectorItem
146
+ """
147
+ final_config = ChainMap(config, self.default_config) # type: ignore[arg-type]
148
+ try:
149
+ ast_json = self.engine.docstring.resolve(identifier)
150
+ except MatlabExecutionError as error:
151
+ raise CollectionError(error.args[0].strip()) from error
152
+ ast_dict = json.loads(ast_json)
153
+
154
+ match ast_dict["type"]:
155
+ case "function":
156
+ return collect_function(ast_dict, final_config)
157
+ case "method":
158
+ return collect_function(ast_dict, final_config)
159
+ case "class":
160
+ return self.collect_class(ast_dict, final_config)
161
+ case _:
162
+ return None
163
+
164
+ def render(self, data: CollectorItem, config: Mapping[str, Any]) -> str:
165
+ """Render a template using provided data and configuration options.
166
+
167
+ Arguments:
168
+ data: The collected data to render.
169
+ config: The handler's configuration options.
170
+
171
+ Returns:
172
+ The rendered template as HTML.
173
+ """
174
+ final_config = ChainMap(config, self.default_config) # type: ignore[arg-type]
175
+
176
+ template_name = rendering.do_get_template(self.env, data)
177
+ template = self.env.get_template(template_name)
178
+
179
+ heading_level = final_config["heading_level"]
180
+
181
+ return template.render(
182
+ **{
183
+ "config": final_config,
184
+ data.kind.value: data,
185
+ "heading_level": heading_level,
186
+ "root": True,
187
+ "locale": self._locale,
188
+ },
189
+ )
190
+
191
+ def get_anchors(self, data: CollectorItem) -> tuple[str, ...]:
192
+ """Return the possible identifiers (HTML anchors) for a collected item.
193
+
194
+ Arguments:
195
+ data: The collected data.
196
+
197
+ Returns:
198
+ The HTML anchors (without '#'), or an empty tuple if this item doesn't have an anchor.
199
+ """
200
+ anchors = [data.path]
201
+ return tuple(anchors)
202
+
203
+ def update_env(self, md: Markdown, config: dict) -> None:
204
+ """Update the Jinja environment with custom filters and tests.
205
+
206
+ Parameters:
207
+ md: The Markdown instance.
208
+ config: The configuration dictionary.
209
+ """
210
+ super().update_env(md, config)
211
+ self.env.trim_blocks = True
212
+ self.env.lstrip_blocks = True
213
+ self.env.keep_trailing_newline = False
214
+ self.env.filters["split_path"] = rendering.do_split_path
215
+ self.env.filters["crossref"] = rendering.do_crossref
216
+ self.env.filters["multi_crossref"] = rendering.do_multi_crossref
217
+ self.env.filters["order_members"] = rendering.do_order_members
218
+ self.env.filters["format_code"] = rendering.do_format_code
219
+ self.env.filters["format_signature"] = rendering.do_format_signature
220
+ self.env.filters["format_attribute"] = rendering.do_format_attribute
221
+ self.env.filters["filter_objects"] = rendering.do_filter_objects
222
+ self.env.filters["stash_crossref"] = lambda ref, length: ref
223
+ self.env.filters["get_template"] = rendering.do_get_template
224
+ self.env.filters["as_attributes_section"] = rendering.do_as_attributes_section
225
+ self.env.filters["as_functions_section"] = rendering.do_as_functions_section
226
+ self.env.filters["as_classes_section"] = rendering.do_as_classes_section
227
+ self.env.filters["as_modules_section"] = rendering.do_as_modules_section
228
+ self.env.tests["existing_template"] = (
229
+ lambda template_name: template_name in self.env.list_templates()
230
+ )
231
+
232
+
233
+ def collect_class(self, ast_dict: dict, config: Mapping) -> Class:
234
+ docstring = (
235
+ Docstring(ast_dict["docstring"], parser=config["docstring_style"])
236
+ if ast_dict["docstring"]
237
+ else None
238
+ )
239
+ object = Class(
240
+ ast_dict["name"],
241
+ docstring=docstring,
242
+ parent=get_parent(Path(ast_dict["path"]).parent),
243
+ hidden=ast_dict["hidden"],
244
+ sealed=ast_dict["sealed"],
245
+ abstract=ast_dict["abstract"],
246
+ enumeration=ast_dict["enumeration"],
247
+ handle=ast_dict["handle"],
248
+ )
249
+
250
+
251
+ for property_dict in ast_dict["properties"]:
252
+ name = property_dict.pop("name")
253
+ defining_class = property_dict.pop("class")
254
+ property_doc = property_dict.pop("docstring")
255
+ docstring = (
256
+ Docstring(property_doc, parser=config["docstring_style"])
257
+ if property_doc
258
+ else None
259
+ )
260
+ if defining_class != object.canonical_path and not config["inherited_members"]:
261
+ continue
262
+
263
+ object.members[name] = Property(name, docstring=docstring, **property_dict)
264
+
265
+ for method_dict in ast_dict["methods"]:
266
+ name = method_dict.pop("name")
267
+ defining_class = method_dict.pop("class")
268
+ if defining_class != object.canonical_path and not config["inherited_members"]:
269
+ continue
270
+
271
+ method = self.collect(f"{defining_class}.{name}", config)
272
+ method._access = method_dict["access"]
273
+ method._static = method_dict["static"]
274
+ method._abstract = method_dict["abstract"]
275
+ method._sealed = method_dict["sealed"]
276
+ method._hidden = method_dict["hidden"]
277
+
278
+ object.members[name] = method
279
+
280
+ return object
281
+
282
+ def get_parent(path: Path) -> Namespace | Classfolder:
283
+ if path.stem[0] == "+":
284
+ if path in MODELS:
285
+ parent = MODELS[path]
286
+ else:
287
+ parent = Namespace(
288
+ path.stem[1:], filepath=str(path), parent=get_parent(path.parent)
289
+ )
290
+ MODELS[path] = parent
291
+ elif path.stem[0] == "@":
292
+ if path in MODELS:
293
+ parent = MODELS[path]
294
+ else:
295
+ parent = Classfolder(
296
+ path.stem[1:], filepath=str(path), parent=get_parent(path.parent)
297
+ )
298
+ MODELS[path] = parent
299
+ else:
300
+ parent = ROOT_NAMESPACE
301
+ return parent
302
+
303
+
304
+
305
+
306
+
307
+ def collect_function(ast_dict: dict, config: Mapping) -> Function:
308
+ parameters = []
309
+
310
+ inputs = (
311
+ ast_dict["inputs"]
312
+ if isinstance(ast_dict["inputs"], list)
313
+ else [ast_dict["inputs"]]
314
+ )
315
+ for input_dict in inputs:
316
+ if input_dict["name"] == "varargin":
317
+ parameter_kind = ParameterKind.var_positional
318
+ elif input_dict["kind"] == "positional":
319
+ parameter_kind = ParameterKind.positional_only
320
+ else:
321
+ parameter_kind = ParameterKind.keyword_only
322
+
323
+ parameters.append(
324
+ Parameter(
325
+ input_dict["name"],
326
+ kind=parameter_kind,
327
+ annotation=input_dict["class"],
328
+ default=input_dict["default"] if input_dict["default"] else None,
329
+ )
330
+ )
331
+
332
+ func = Function(
333
+ ast_dict["name"],
334
+ parameters=Parameters(*parameters),
335
+ docstring=Docstring(
336
+ ast_dict["docstring"],
337
+ parser=config["docstring_style"],
338
+ parser_options=config["docstring_options"],
339
+ )
340
+ if ast_dict["docstring"]
341
+ else None,
342
+ parent=get_parent(Path(ast_dict["path"]).parent),
343
+ )
344
+
345
+ return func
346
+
347
+
348
+ def get_handler(
349
+ *,
350
+ theme: str,
351
+ custom_templates: str | None = None,
352
+ config_file_path: str | None = None,
353
+ paths: list[str] | None = None,
354
+ startup_expression: str = "",
355
+ **config: Any,
356
+ ) -> MatlabHandler:
357
+ """
358
+ Returns a MatlabHandler object.
359
+
360
+ Parameters:
361
+ theme (str): The theme to use.
362
+ custom_templates (str | None, optional): Path to custom templates. Defaults to None.
363
+ config_file_path (str | None, optional): Path to configuration file. Defaults to None.
364
+ paths (list[str] | None, optional): List of paths to include. Defaults to None.
365
+ startup_expression (str, optional): Startup expression. Defaults to "".
366
+ **config (Any): Additional configuration options.
367
+
368
+ Returns:
369
+ MatlabHandler: The created MatlabHandler object.
370
+ """
371
+ return MatlabHandler(
372
+ handler="matlab",
373
+ theme=theme,
374
+ custom_templates=custom_templates,
375
+ config_file_path=config_file_path,
376
+ paths=paths,
377
+ startup_expression=startup_expression,
378
+ )
379
+
380
+
381
+ if __name__ == "__main__":
382
+ handler = get_handler(theme="material")
383
+ pprint(handler.collect("matlab_startup", {}).docstring.parse("google"))
@@ -0,0 +1,8 @@
1
+ function data = builtin(identifier)
2
+ data.name = identifier;
3
+ data.type = 'function';
4
+ data.doc = help(identifier);
5
+ data.location = string(which(identifier));
6
+ data.inputs = struct.empty();
7
+ data.outputs = struct.empty();
8
+ end
@@ -0,0 +1,12 @@
1
+ function data = class(identifier)
2
+
3
+ if isMATLABReleaseOlderThan('R2024a')
4
+ metaclass = @meta.class.fromName;
5
+ else
6
+ metaclass = @matlab.metadata.Class.fromName;
7
+ end
8
+
9
+ object = metaclass(identifier);
10
+ data = docstring.metadata.class(object);
11
+
12
+ end
@@ -0,0 +1,5 @@
1
+ function data = func(identifier)
2
+
3
+ object = matlab.internal.metafunction(identifier);
4
+ data = docstring.metadata.func(object);
5
+ end
@@ -0,0 +1,6 @@
1
+ function data = method(identifier)
2
+
3
+ object = matlab.internal.metafunction(identifier);
4
+ data = docstring.metadata.func(object);
5
+ data.type = 'method';
6
+ end
@@ -0,0 +1,28 @@
1
+ function data = namespace(identifier)
2
+
3
+ currentpath = split(pwd, filesep);
4
+
5
+ name = identifier(2:end);
6
+
7
+ if isfolder(identifier)
8
+ for i = numel(currentpath):-1:1
9
+ directory = currentpath(i);
10
+ if isempty(directory)
11
+ break
12
+ elseif strcmp(directory(1), '+')
13
+ name = sprintf("%s.%s", directory(2:end), name);
14
+ else
15
+ break
16
+ end
17
+ end
18
+ end
19
+
20
+ object = matlab.metadata.Namespace.fromName(name);
21
+
22
+ if isempty(object)
23
+ docstring.exception(identifier)
24
+ end
25
+
26
+ data = docstring.metadata.namespace(object);
27
+ data.name = name;
28
+ end
@@ -0,0 +1,58 @@
1
+ function data = argument(object)
2
+ arguments
3
+ object (1,:) matlab.internal.metadata.Argument
4
+ end
5
+
6
+ if isempty(object)
7
+ data = struct.empty();
8
+ return
9
+ elseif numel(object) > 1
10
+ for iArg = numel(object):-1:1
11
+ data(iArg) = docstring.metadata.argument(object(iArg));
12
+ end
13
+ return
14
+ end
15
+
16
+ data.name = object.Name;
17
+ data.kind = string(object.Kind);
18
+ data.presence = string(object.Presence);
19
+
20
+ if isempty(object.DefaultValue)
21
+ data.default = "";
22
+ elseif object.DefaultValue.IsConstant
23
+ data.default = string(object.DefaultValue.Value);
24
+ else
25
+ data.default = "*expression*";
26
+ end
27
+
28
+ if ~isempty(object.Validation)
29
+ if ~isempty(object.Validation.Class)
30
+ data.class = string(object.Validation.Class.Name);
31
+ else
32
+ data.class = "";
33
+ end
34
+ if ~isempty(object.Validation.Size)
35
+ for iSize = numel(object.Validation.Size):-1:1
36
+
37
+ if isprop(object.Validation.Size(iSize), 'Length')
38
+ data.size(iSize) = object.Validation.Size(iSize).Length;
39
+ else
40
+ data.size(iSize) = ":";
41
+ end
42
+ end
43
+ else
44
+ data.size = "";
45
+ end
46
+
47
+ if ~isempty(object.Validation.Functions)
48
+ data.validators = arrayfun(@(f) string(f.Name), object.Validation.Functions);
49
+ else
50
+ data.validators = "";
51
+ end
52
+ else
53
+ data.class = "";
54
+ data.size = "";
55
+ data.validators = "";
56
+ end
57
+
58
+ end
@@ -0,0 +1,44 @@
1
+ function data = class(object)
2
+ arguments
3
+ object (1,1) matlab.metadata.Class
4
+ end
5
+ data.type = 'class';
6
+
7
+ namespaces = split(object.Name, '.');
8
+ data.name = namespaces{end};
9
+
10
+ data.docstring = docstring.utils.parse_doc(object, true);
11
+ data.path = matlab.internal.metafunction(object.Name).Location;
12
+ data.hidden = object.Hidden;
13
+ data.sealed = object.Sealed;
14
+ data.abstract = object.Abstract;
15
+ data.enumeration = object.Enumeration;
16
+ data.superclasses = arrayfun(@(o) string(o.Name), object.SuperclassList);
17
+ data.handle = object.HandleCompatible;
18
+ data.aliases = object.Aliases;
19
+
20
+ data.methods = [];
21
+ for methodObject = object.MethodList'
22
+ if any(strcmp(methodObject.Name, {'empty', 'forInteractiveUse'}))
23
+ break
24
+ else
25
+ method.name = string(methodObject.Name);
26
+ method.class = string(methodObject.DefiningClass.Name);
27
+ method.access = methodObject.Access;
28
+ method.static = methodObject.Static;
29
+ method.abstract = methodObject.Abstract;
30
+ method.sealed = methodObject.Sealed;
31
+ method.hidden = methodObject.Hidden;
32
+ data.methods = [data.methods, method];
33
+ end
34
+ end
35
+
36
+ numProp = numel(object.PropertyList);
37
+ for iProp = numProp:-1:1
38
+ data.properties(iProp) = docstring.metadata.property(object.PropertyList(iProp));
39
+ end
40
+
41
+ nameparts = split(object.Name, '.');
42
+ data.constructor = any(strcmp(nameparts(end), [data.methods.name]));
43
+
44
+ end
@@ -0,0 +1,14 @@
1
+ function data = func(object)
2
+
3
+ arguments
4
+ object (1,1) matlab.metadata.MetaData
5
+ end
6
+
7
+ data.type = 'function';
8
+ data.name = object.Name;
9
+ data.docstring = docstring.utils.parse_doc(object);
10
+ data.path = object.Location;
11
+
12
+ data.inputs = docstring.metadata.argument(object.Signature.Inputs);
13
+ data.outputs = docstring.metadata.argument(object.Signature.Outputs);
14
+ end
@@ -0,0 +1,14 @@
1
+ function data = namespace(object)
2
+
3
+ arguments
4
+ object (1,1) matlab.metadata.Namespace
5
+ end
6
+
7
+ data.type = 'namespace';
8
+ data.docstring = docstring.utils.parse_doc(object);
9
+
10
+ data.classes = arrayfun(@(o) string(o.Name), object.ClassList);
11
+ data.functions = arrayfun(@(o) string(o.Name), object.FunctionList);
12
+ data.namespaces = arrayfun(@(o) string(o.Name), object.InnerNamespaces);
13
+ data.path = docstring.utils.get_namespace_path(object);
14
+ end
@@ -0,0 +1,41 @@
1
+ function data = argument(object)
2
+ arguments
3
+ object (1,:) meta.property
4
+ end
5
+
6
+ data.name = string(object.Name);
7
+ data.class = string(object.DefiningClass.Name);
8
+ data.docstring = docstring.utils.parse_doc(object);
9
+ data.get_access = object.GetAccess;
10
+ data.set_access = object.SetAccess;
11
+ data.dependent = object.Dependent;
12
+ data.constant = object.Constant;
13
+ data.abstract = object.Abstract;
14
+ data.transient = object.Transient;
15
+ data.hidden = object.Hidden;
16
+
17
+ if ~isempty(object.Validation) && ~isempty(object.Validation.Class)
18
+ data.annotation = string(object.Validation.Class.Name);
19
+ else
20
+ data.annotation = "";
21
+ end
22
+
23
+ if ~isempty(object.Validation) && ~isempty(object.Validation.Size)
24
+ for iSize = numel(object.Validation.Size):-1:1
25
+ if isprop(object.Validation.Size(iSize), 'Length')
26
+ sizeStr(iSize) = string(object.Validation.Size(iSize).Length);
27
+ else
28
+ sizeStr(iSize) = ":";
29
+ end
30
+ end
31
+ data.size = sprintf("[%s]", join(sizeStr, ","));
32
+ else
33
+ data.size = "";
34
+ end
35
+
36
+ if ~isempty(object.Validation) && ~isempty(object.Validation.ValidatorFunctions)
37
+ data.validation = join(cellfun(@(f) string(char(f)), object.Validation.ValidatorFunctions), ", ");
38
+ else
39
+ data.validation = "";
40
+ end
41
+ end
@@ -0,0 +1,15 @@
1
+ function data = script(object)
2
+ %SCRIPT Summary of this function goes here
3
+ % Detailed explanation goes here
4
+
5
+ arguments
6
+ object (1,1) matlab.metadata.MetaData
7
+ end
8
+
9
+ data.type = 'script';
10
+ data.Name = object.Name;
11
+ data.docstring = docstring.utils.parse_doc(object);
12
+ data.path = object.Location;
13
+
14
+ end
15
+
@@ -0,0 +1,34 @@
1
+ function parsedDoc = dedent(doc)
2
+ %DEDENT Summary of this function goes here
3
+ % Detailed explanation goes here
4
+ arguments
5
+ doc (1,1) string
6
+ end
7
+
8
+ lines = cellstr(splitlines(doc));
9
+
10
+
11
+ for iLine = numel(lines):-1:1
12
+ line = lines{iLine};
13
+ if ~isempty(line) && ~all(line == ' ')
14
+ indentations(iLine) = find(line~= ' ', 1);
15
+ else
16
+ indentations(iLine) = inf;
17
+ end
18
+ end
19
+
20
+ indentation = min(indentations);
21
+
22
+ for iLine = 1:numel(lines)
23
+ line = lines{iLine};
24
+ if isempty(line) || numel(line) <= indentation
25
+ lines{iLine} = '';
26
+ else
27
+ lines{iLine} = line(indentation:end);
28
+ end
29
+ end
30
+
31
+ parsedDoc = string(strjoin(lines, '\n'));
32
+
33
+ end
34
+
@@ -0,0 +1,26 @@
1
+ function path = get_namespace_path(object, parents)
2
+
3
+ arguments
4
+ object (1,1) matlab.metadata.Namespace
5
+ parents (1,1) double {mustBeInteger, mustBeNonnegative} = 0
6
+ end
7
+
8
+ if ~isempty(object.ClassList)
9
+
10
+ function_path = which(sprintf("%s.%s", object.Name, object.ClassList(1).Name));
11
+ path = fileparts(function_path);
12
+ elseif ~isempty(object.FunctionList)
13
+
14
+ function_path = which(sprintf("%s.%s", object.Name, object.FunctionList(1).Name));
15
+ path = fileparts(function_path);
16
+ elseif ~isempty(object.InnerNamespaces)
17
+ path = docstring.utils.get_namespace_path(...
18
+ object.InnerNamespaces(1), parents + 1);
19
+ else
20
+ error("Cannot get path of namespace %s", object.Name);
21
+ end
22
+
23
+ for i = 1:parents
24
+ path = fileparts(path);
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ function doc = parse_doc(object, combine)
2
+
3
+ arguments
4
+ object (1,1) matlab.metadata.MetaData
5
+ combine (1,1) logical = false
6
+ end
7
+
8
+ if isempty(object.DetailedDescription)
9
+ doc = object.Description;
10
+ else
11
+ if combine
12
+ doc = sprintf("%s\n%s", object.Description, ...
13
+ docstring.utils.dedent(object.DetailedDescription));
14
+ else
15
+ doc = docstring.utils.dedent(object.DetailedDescription);
16
+ end
17
+
18
+ end
19
+
20
+ end
21
+
@@ -0,0 +1,7 @@
1
+ function exception(identifier)
2
+
3
+ notFoundException = MException('mkdocs:callerInput', '%s %s %s', ...
4
+ 'The input', identifier, 'could not be found on the MATLAB path');
5
+ throwAsCaller(notFoundException)
6
+
7
+ end
@@ -0,0 +1,94 @@
1
+ function [jsonString, data] = resolve(identifier, cwd)
2
+ % Resolve the docstring for a given MATLAB entity
3
+ %
4
+ % ```matlab
5
+ % jsonString = resolve(name, cwd)
6
+ % ```
7
+ %
8
+ % Parameters:
9
+ % identifier (string): Name of the MATLAB entity
10
+ % cwd (string): Current working directory
11
+ %
12
+ % Returns:
13
+ % jsonString(string): JSON-encoded metadata object
14
+ % data(struct): metadata object
15
+
16
+ arguments
17
+ identifier (1, :) char
18
+ cwd (1,1) string {mustBeFolder} = pwd()
19
+ end
20
+
21
+ cd(cwd);
22
+
23
+ % Check if namespace
24
+ if strcmp(identifier(1), '+')
25
+ data = docstring.case.namespace(identifier);
26
+ jsonString = jsonencode(data);
27
+ return;
28
+ end
29
+
30
+ % Try namespace functions
31
+ if contains(identifier, '.')
32
+ object = matlab.internal.metafunction(identifier);
33
+ if ~isempty(object) && isa(object, 'matlab.internal.metadata.Function')
34
+ if isempty(object.Signature)
35
+ data = docstring.metadata.script(object);
36
+ else
37
+ data = docstring.metadata.func(object);
38
+ end
39
+ jsonString = jsonencode(data);
40
+ return;
41
+ end
42
+ end
43
+
44
+ % Try built-in aliases with which
45
+ if contains(which(identifier), ' built-in ')
46
+ data = docstring.case.builtin(identifier);
47
+ jsonString = jsonencode(data);
48
+ return
49
+ end
50
+
51
+ switch exist(identifier) %#ok<EXIST>
52
+ case 2
53
+ % if NAME is a file with extension .m, .mlx, .mlapp, or .sfx, or NAME
54
+ % is the name of a file with a non-registered file extension
55
+ % (.mat, .fig, .txt).
56
+
57
+ % Try with metafunction
58
+ object = matlab.internal.metafunction(identifier);
59
+ if ~isempty(object) && isa(object, 'matlab.internal.metadata.Function')
60
+ if isempty(object.Signature)
61
+ data = docstring.metadata.script(object);
62
+ else
63
+ data = docstring.metadata.func(object);
64
+ end
65
+ data.name = identifier;
66
+ else
67
+ docstring.exception(identifier);
68
+ end
69
+ case 5
70
+ % if NAME is a built-in MATLAB function. This does not include classes
71
+ data = docstring.case.builtin(identifier);
72
+ case 8
73
+ % if NAME is a class (exist returns 0 for Java classes if you
74
+ % start MATLAB with the -nojvm option.)
75
+ data = docstring.case.class(identifier);
76
+ case 0
77
+ if contains(identifier, '.')
78
+ nameparts = split(identifier, '.');
79
+ tryclassname = strjoin(nameparts(1:end-1), '.');
80
+ if exist(tryclassname, 'class')
81
+ data = docstring.case.method(identifier);
82
+ else
83
+ docstring.exception(identifier);
84
+ end
85
+ else
86
+ docstring.exception(identifier);
87
+ end
88
+ otherwise
89
+ docstring.exception(identifier);
90
+ end
91
+
92
+ jsonString = jsonencode(data);
93
+
94
+ end
@@ -0,0 +1,21 @@
1
+ function result = matlab_startup(paths, expression)
2
+ % Add paths and evaluate an expression
3
+ %
4
+ % Parameters:
5
+ % paths (string): Paths to add to the MATLAB path
6
+ % expression (string): MATLAB startup expression
7
+
8
+ arguments
9
+ paths (1, :) string
10
+ expression (1, 1) string = string.empty()
11
+ end
12
+ for path = paths
13
+ addpath(genpath(path));
14
+ end
15
+
16
+ if ~isempty(expression)
17
+ eval(expression);
18
+ end
19
+
20
+ result = nan;
21
+ end
@@ -0,0 +1,127 @@
1
+ from enum import Enum
2
+ from typing import Any
3
+ from griffe import Function as GriffeFunction, Class as GriffeClass, Module, Attribute
4
+ from _griffe.exceptions import BuiltinModuleError
5
+
6
+
7
+ class Access(Enum):
8
+ PUBLIC = "public"
9
+ PROTECTED = "protected"
10
+ PRIVATE = "private"
11
+ IMMUTABLE = "immutable"
12
+
13
+
14
+ class CanonicalPathMixin:
15
+ @property
16
+ def canonical_path(self) -> str:
17
+ """The full dotted path of this object.
18
+
19
+ The canonical path is the path where the object was defined (not imported).
20
+ """
21
+ if self.parent is None or self.parent.path == "":
22
+ return self.name
23
+ return f"{self.parent.path}.{self.name}"
24
+
25
+
26
+ class Class(CanonicalPathMixin, GriffeClass):
27
+ def __init__(
28
+ self,
29
+ *args: Any,
30
+ hidden: bool = False,
31
+ sealed: bool = False,
32
+ abstract: bool = False,
33
+ enumeration: bool = False,
34
+ handle: bool = False,
35
+ **kwargs: Any,
36
+ ) -> None:
37
+ super().__init__(*args, **kwargs)
38
+ self._hidden: bool = hidden
39
+ self._sealed: bool = sealed
40
+ self._abstract: bool = abstract
41
+ self._enumeration: bool = enumeration
42
+ self._handle: bool = handle
43
+
44
+ @property
45
+ def is_private(self) -> bool:
46
+ return self._hidden
47
+
48
+ @property
49
+ def canonical_path(self) -> str:
50
+ if isinstance(self.parent, Classfolder):
51
+ return self.parent.canonical_path
52
+ else:
53
+ return super().canonical_path
54
+
55
+
56
+ class Property(CanonicalPathMixin, Attribute):
57
+ def __init__(
58
+ self,
59
+ *args: Any,
60
+ get_access: Access = Access.PUBLIC,
61
+ set_access: Access = Access.PUBLIC,
62
+ dependent: bool = False,
63
+ constant: bool = False,
64
+ abstract: bool = False,
65
+ transient: bool = False,
66
+ hidden: bool = False,
67
+ get_observable: bool = False,
68
+ set_observable: bool = False,
69
+ abort_set: bool = False,
70
+ non_copyable: bool = False,
71
+ has_default: bool = False,
72
+ size: str | None = None,
73
+ validation: str | None = None,
74
+ **kwargs: Any,
75
+ ) -> None:
76
+ super().__init__(*args, **kwargs)
77
+ self._get_access: Access = get_access
78
+ self._set_access: Access = set_access
79
+ self._dependent: bool = dependent
80
+ self._constant: bool = constant
81
+ self._abstract: bool = abstract
82
+ self._transient: bool = transient
83
+ self._hidden: bool = hidden
84
+ self._get_observable: bool = get_observable
85
+ self._set_observable: bool = set_observable
86
+ self._abort_set: bool = abort_set
87
+ self._non_copyable: bool = non_copyable
88
+ self._has_default: bool = has_default
89
+ self._size: str | None = size
90
+ self._validation: str | None = validation
91
+
92
+ @property
93
+ def is_private(self) -> bool:
94
+ set_public = self._access == Access.PUBLIC | self._access == Access.IMMUTABLE
95
+ get_public = self._access == Access.PUBLIC
96
+ return (set_public or get_public) and not self._hidden
97
+
98
+
99
+ class Function(CanonicalPathMixin, GriffeFunction):
100
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
101
+ super().__init__(*args, **kwargs)
102
+ self._access: Access = Access.PUBLIC
103
+ self._static: bool = False
104
+ self._abstract: bool = False
105
+ self._sealed: bool = False
106
+ self._hidden: bool = False
107
+
108
+ @property
109
+ def is_private(self) -> bool:
110
+ public = self._access == Access.PUBLIC | self._access == Access.IMMUTABLE
111
+ return public and not self._hidden
112
+
113
+
114
+ class Namespace(CanonicalPathMixin, Module):
115
+ def __repr__(self) -> str:
116
+ try:
117
+ return f"Namespace({self.filepath!r})"
118
+ except BuiltinModuleError:
119
+ return f"Namespace({self.name!r})"
120
+
121
+
122
+ class Classfolder(CanonicalPathMixin, Module):
123
+ def __repr__(self) -> str:
124
+ try:
125
+ return f"Classfolder({self.filepath!r})"
126
+ except BuiltinModuleError:
127
+ return f"Classfolder({self.name!r})"