mkdocstrings-matlab 0.9.7__py3-none-any.whl → 1.0.0__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 (45) hide show
  1. mkdocstrings_handlers/matlab/__init__.py +26 -3
  2. mkdocstrings_handlers/matlab/config.py +885 -0
  3. mkdocstrings_handlers/matlab/handler.py +155 -296
  4. mkdocstrings_handlers/matlab/logger.py +111 -0
  5. mkdocstrings_handlers/matlab/rendering.py +818 -0
  6. mkdocstrings_handlers/matlab/templates/material/attributes.html.jinja +25 -0
  7. mkdocstrings_handlers/matlab/templates/material/backlinks.html.jinja +17 -0
  8. mkdocstrings_handlers/matlab/templates/material/children.html.jinja +70 -62
  9. mkdocstrings_handlers/matlab/templates/material/class.html.jinja +236 -0
  10. mkdocstrings_handlers/matlab/templates/material/docstring/admonition.html.jinja +20 -0
  11. mkdocstrings_handlers/matlab/templates/material/docstring/classes.html.jinja +85 -0
  12. mkdocstrings_handlers/matlab/templates/material/docstring/examples.html.jinja +27 -0
  13. mkdocstrings_handlers/matlab/templates/material/docstring/functions.html.jinja +91 -0
  14. mkdocstrings_handlers/matlab/templates/material/docstring/input_arguments.html.jinja +171 -0
  15. mkdocstrings_handlers/matlab/templates/material/docstring/name_value_arguments.html.jinja +166 -0
  16. mkdocstrings_handlers/matlab/templates/material/docstring/namespaces.html.jinja +5 -6
  17. mkdocstrings_handlers/matlab/templates/material/docstring/output_arguments.html.jinja +152 -0
  18. mkdocstrings_handlers/matlab/templates/material/docstring/properties.html.jinja +25 -26
  19. mkdocstrings_handlers/matlab/templates/material/docstring.html.jinja +53 -0
  20. mkdocstrings_handlers/matlab/templates/material/expression.html.jinja +55 -0
  21. mkdocstrings_handlers/matlab/templates/material/folder.html.jinja +31 -39
  22. mkdocstrings_handlers/matlab/templates/material/function.html.jinja +148 -0
  23. mkdocstrings_handlers/matlab/templates/material/language.html.jinja +18 -0
  24. mkdocstrings_handlers/matlab/templates/material/languages/en.html.jinja +38 -0
  25. mkdocstrings_handlers/matlab/templates/material/languages/ja.html.jinja +38 -0
  26. mkdocstrings_handlers/matlab/templates/material/languages/zh.html.jinja +38 -0
  27. mkdocstrings_handlers/matlab/templates/material/namespace.html.jinja +32 -38
  28. mkdocstrings_handlers/matlab/templates/material/property.html.jinja +39 -35
  29. mkdocstrings_handlers/matlab/templates/material/script.html.jinja +3 -25
  30. mkdocstrings_handlers/matlab/templates/material/signature.html.jinja +105 -0
  31. mkdocstrings_handlers/matlab/templates/material/style.css +179 -4
  32. mkdocstrings_handlers/matlab/templates/material/summary/classes.html.jinja +25 -0
  33. mkdocstrings_handlers/matlab/templates/material/summary/functions.html.jinja +25 -0
  34. mkdocstrings_handlers/matlab/templates/material/summary/namespaces.html.jinja +17 -13
  35. mkdocstrings_handlers/matlab/templates/material/summary/properties.html.jinja +17 -13
  36. mkdocstrings_handlers/matlab/templates/material/summary.html.jinja +6 -6
  37. {mkdocstrings_matlab-0.9.7.dist-info → mkdocstrings_matlab-1.0.0.dist-info}/METADATA +17 -19
  38. mkdocstrings_matlab-1.0.0.dist-info/RECORD +41 -0
  39. mkdocstrings_handlers/matlab/collect.py +0 -783
  40. mkdocstrings_handlers/matlab/enums.py +0 -54
  41. mkdocstrings_handlers/matlab/models.py +0 -633
  42. mkdocstrings_handlers/matlab/treesitter.py +0 -707
  43. mkdocstrings_matlab-0.9.7.dist-info/RECORD +0 -22
  44. {mkdocstrings_matlab-0.9.7.dist-info → mkdocstrings_matlab-1.0.0.dist-info}/WHEEL +0 -0
  45. {mkdocstrings_matlab-0.9.7.dist-info → mkdocstrings_matlab-1.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,347 +1,189 @@
1
1
  """The mkdocstrings handler for processing MATLAB code documentation."""
2
2
 
3
- import re
4
- from collections import ChainMap
3
+ from __future__ import annotations
4
+
5
+ from contextlib import suppress
6
+ from dataclasses import asdict
5
7
  from pathlib import Path
6
- from pprint import pprint
7
- from typing import Any, ClassVar, Mapping
8
+ from typing import TYPE_CHECKING, Any, ClassVar
8
9
 
9
- from jinja2.loaders import FileSystemLoader
10
- from markdown import Markdown
10
+ from griffe import AliasResolutionError, Parser
11
+ from maxx.collection import LinesCollection, PathsCollection
11
12
  from mkdocs.exceptions import PluginError
12
- from mkdocstrings.handlers.base import BaseHandler, CollectionError, CollectorItem
13
+ from mkdocstrings import (
14
+ BaseHandler,
15
+ CollectionError,
16
+ CollectorItem,
17
+ HandlerOptions,
18
+ get_logger,
19
+ )
20
+
21
+ from mkdocstrings_handlers.matlab import rendering
22
+ from mkdocstrings_handlers.matlab.config import MatlabConfig, MatlabOptions
23
+
24
+ if TYPE_CHECKING:
25
+ from collections.abc import Mapping, MutableMapping
13
26
 
14
- from mkdocstrings_handlers.matlab.collect import LinesCollection, PathCollection
15
- from mkdocstrings_handlers.python import rendering
27
+ from mkdocs.config.defaults import MkDocsConfig
28
+
29
+
30
+ _logger = get_logger(__name__)
16
31
 
17
32
 
18
33
  class MatlabHandler(BaseHandler):
19
34
  """The `MatlabHandler` class is a handler for processing Matlab code documentation."""
20
35
 
21
- name: str = "matlab"
22
- """The handler's name."""
23
- domain: str = "mat" # to match Sphinx's default domain
36
+ name: ClassVar[str] = "matlab"
37
+ """The MATLAB handler class."""
38
+
39
+ domain: ClassVar[str] = "mat" # to match Sphinx's default domain
24
40
  """The cross-documentation domain/language for this handler."""
25
- enable_inventory: bool = True
41
+
42
+ enable_inventory: ClassVar[bool] = True
26
43
  """Whether this handler is interested in enabling the creation of the `objects.inv` Sphinx inventory file."""
27
- fallback_theme = "material"
44
+
45
+ fallback_theme: ClassVar[str] = "material"
28
46
  """The fallback theme."""
29
- fallback_config: ClassVar[dict] = {
30
- "fallback": True,
31
- }
32
- """The configuration used to collect item during autorefs fallback."""
33
- default_config: ClassVar[dict] = {
34
- # General options
35
- "show_bases": True,
36
- "show_inheritance_diagram": False,
37
- "show_source": True,
38
- # Heading options
39
- "heading_level": 2,
40
- "parameter_headings": False,
41
- "show_root_heading": False,
42
- "show_root_toc_entry": True,
43
- "show_root_full_path": True,
44
- "show_root_members_full_path": False,
45
- "show_object_full_path": False,
46
- "show_category_heading": False,
47
- "show_symbol_type_heading": False,
48
- "show_symbol_type_toc": False,
49
- # Member options
50
- "members": None,
51
- "hidden_members": False,
52
- "private_members": False,
53
- "inherited_members": False,
54
- "members_order": rendering.Order.alphabetical.value,
55
- "filters": ["!^delete$|^disp$"],
56
- "group_by_category": True,
57
- "show_subnamespaces": False,
58
- "summary": False,
59
- "show_labels": True,
60
- # Docstring options
61
- "docstring_style": "google",
62
- "docstring_options": {},
63
- "docstring_section_style": "table",
64
- "parse_arguments": False,
65
- "merge_constructor_into_class": False,
66
- "merge_constructor_ignore_summary": False,
67
- "show_if_no_docstring": False,
68
- "show_docstring_propeties": True,
69
- "show_docstring_functions": True,
70
- "show_docstring_classes": True,
71
- "show_docstring_namespaces": True,
72
- "show_docstring_description": True,
73
- "show_docstring_examples": True,
74
- "show_docstring_input_arguments": True,
75
- "show_docstring_name_value_arguments": True,
76
- "show_docstring_output_arguments": True,
77
- # Signature options
78
- "show_signature": True,
79
- "show_signature_annotations": False,
80
- "separate_signature": False,
81
- "signature_crossrefs": False,
82
- }
83
- """Default handler configuration.
84
-
85
- Attributes: General options:
86
- show_bases (bool): Show the base classes of a class. Default: `True`.
87
- show_inheritance_diagram (bool): Show the inheritance diagram of a class using Mermaid. Default: `False`.
88
- show_source (bool): Show the source code of this object. Default: `True`.
89
-
90
-
91
- Attributes: Headings options:
92
- heading_level (int): The initial heading level to use. Default: `2`.
93
- parameter_headings (bool): Whether to render headings for parameters (therefore showing parameters in the ToC). Default: `False`.
94
- show_root_heading (bool): Show the heading of the object at the root of the documentation tree
95
- (i.e. the object referenced by the identifier after `:::`). Default: `False`.
96
- show_root_toc_entry (bool): If the root heading is not shown, at least add a ToC entry for it. Default: `True`.
97
- show_root_full_path (bool): Show the full path for the root object heading. Default: `True`.
98
- show_root_members_full_path (bool): Show the full path of the root members. Default: `False`.
99
- show_object_full_path (bool): Show the full path of every object. Default: `False`.
100
- show_category_heading (bool): When grouped by categories, show a heading for each category. Default: `False`.
101
- show_symbol_type_heading (bool): Show the symbol type in headings (e.g. mod, class, meth, func and attr). Default: `False`.
102
- show_symbol_type_toc (bool): Show the symbol type in the Table of Contents (e.g. mod, class, methd, func and attr). Default: `False`.
103
-
104
- Attributes: Members options:
105
- members (list[str] | bool | None): A boolean, or an explicit list of members to render.
106
- If true, select all members without further filtering.
107
- If false or empty list, do not render members.
108
- If none, select all members and apply further filtering with filters and docstrings. Default: `None`.
109
- hidden_members (list[str] | bool | None): A boolean, or an explicit list of hidden members to render.
110
- If true, select all inherited members, which can then be filtered with `members`.
111
- If false or empty list, do not select any hidden member. Default: `False`.
112
- private_members (list[str] | bool | None): A boolean, or an explicit list of private members to render.
113
- If true, select all inherited members, which can then be filtered with `members`.
114
- If false or empty list, do not select any private member. Default: `False`.
115
- inherited_members (list[str] | bool | None): A boolean, or an explicit list of inherited members to render.
116
- If true, select all inherited members, which can then be filtered with `members`.
117
- If false or empty list, do not select any inherited member. Default: `False`.
118
- members_order (str): The members ordering to use. Options: `alphabetical` - order by the members names,
119
- `source` - order members as they appear in the source file. Default: `"alphabetical"`.
120
- filters (list[str] | None): A list of filters applied to filter objects based on their name.
121
- A filter starting with `!` will exclude matching objects instead of including them.
122
- The `members` option takes precedence over `filters` (filters will still be applied recursively
123
- to lower members in the hierarchy). Default: `["!^delete$|^disp$"]`.
124
- group_by_category (bool): Group the object's children by categories: properties, classes, functions, and namespaces. Default: `True`.
125
- show_subnamespaces (bool): When rendering a namespace, show its subnamespaces recursively. Default: `False`.
126
- summary (bool | dict[str, bool]): Whether to render summaries of namespaces, classes, functions (methods) and properties. Default: `False`.
127
- show_labels (bool): Whether to show labels of the members. Default: `True`.
128
-
129
- Attributes: Docstrings options:
130
- docstring_style (str): The docstring style to use: `google`, `numpy`, `sphinx`, or `None`. Default: `"google"`.
131
- docstring_options (dict): The options for the docstring parser. See [docstring parsers](https://mkdocstrings.github.io/griffe/reference/docstrings/) and their options in Griffe docs.
132
- docstring_section_style (str): The style used to render docstring sections. Options: `table`, `list`, `spacy`. Default: `"table"`.
133
- parse_arguments (bool): Whether to load inputs and output parameters based on argument validation blocks. Default: `True`.
134
- merge_constructor_into_class (bool): Whether to merge the constructor method into the class' signature and docstring. Default: `False`.
135
- merge_constructor_ignore_summary (bool): Whether to ignore the constructor summary when merging it into the class. Default: `False`.
136
- show_if_no_docstring (bool): Show the object heading even if it has no docstring or children with docstrings. Default: `False`.
137
- show_docstring_properties (bool): Whether to display the "Properties" section in the object's docstring. Default: `True`.
138
- show_docstring_functions (bool): Whether to display the "Functions" or "Methods" sections in the object's docstring. Default: `True`.
139
- show_docstring_classes (bool): Whether to display the "Classes" section in the object's docstring. Default: `True`.
140
- show_docstring_namespaces (bool): Whether to display the "Namespaces" section in the object's docstring. Default: `True`.
141
- show_docstring_description (bool): Whether to display the textual block (including admonitions) in the object's docstring. Default: `True`.
142
- show_docstring_examples (bool): Whether to display the "Examples" section in the object's docstring. Default: `True`.
143
- show_docstring_input_arguments (bool): Whether to display the "Input arguments" section in the object's docstring. Default: `True`.
144
- show_docstring_name_value_arguments (bool): Whether to display the "Name-value pairs" section in the object's docstring. Default: `True`.
145
- show_docstring_output_arguments (bool): Whether to display the "Output arguments" section in the object's docstring. Default: `True`.
146
-
147
- Attributes: Signatures/annotations options:
148
- show_signature (bool): Show methods and functions signatures. Default: `True`.
149
- show_signature_annotations (bool): Show the type annotations in methods and functions signatures. Default: `False`.
150
- separate_signature (bool): Whether to put the whole signature in a code block below the heading.
151
- signature_crossrefs (bool): Whether to render cross-references for type annotations in signatures. Default: `False`.
152
- """
153
47
 
154
48
  def __init__(
155
49
  self,
156
- handler: str,
157
- theme: str,
158
- custom_templates: str | None = None,
159
- config_file_path: str | None = None,
160
- paths: list[str] | None = None,
161
- paths_recursive: bool = False,
162
- locale: str = "en",
50
+ config: MatlabConfig,
51
+ base_dir: Path,
163
52
  **kwargs: Any,
164
53
  ) -> None:
165
54
  """
166
55
  Initialize the handler with the given configuration.
167
56
 
168
57
  Args:
169
- handler: The name of the handler.
170
- theme: The name of theme to use.
171
- custom_templates: Directory containing custom templates.
172
- config_file_path (str | None, optional): Path to the configuration file. Defaults to None.
173
- paths (list[str] | None, optional): List of paths to include. Defaults to None.
174
- paths_recursive (bool, optional): Whether to include paths recursively. Defaults to False.
175
- locale (str, optional): Locale setting. Defaults to "en".
176
- **kwargs (Any): Arbitrary keyword arguments.
58
+ config: The handler configuration.
59
+ base_dir: The base directory of the project.
60
+ **kwargs: Arguments passed to the parent constructor.
177
61
 
178
62
  Returns:
179
63
  None
180
64
  """
181
-
182
- super().__init__(handler, theme, custom_templates=custom_templates)
183
-
184
- theme_path = Path(__file__).resolve().parent / "templates" / theme
185
- if theme_path.exists() and isinstance(self.env.loader, FileSystemLoader):
186
- # Insert our templates directory at the beginning of the search path to overload the Python templates
187
- self.env.loader.searchpath.insert(0, str(theme_path))
188
- css_path = theme_path / "style.css"
189
- if css_path.exists():
190
- self.extra_css += "\n" + css_path.read_text(encoding="utf-8")
191
-
192
- if paths is None or config_file_path is None:
193
- config_path = None
65
+ super().__init__(**kwargs)
66
+
67
+ self.config = config
68
+ self.base_dir = base_dir
69
+ self.global_options = config.options
70
+
71
+ # Warn if user overrides base templates.
72
+ if self.custom_templates:
73
+ for theme_dir in base_dir.joinpath(self.custom_templates, "matlab").iterdir():
74
+ if theme_dir.joinpath("_base").is_dir():
75
+ _logger.warning(
76
+ f"Overriding base template '{theme_dir.name}/_base/<template>.html.jinja' is not supported, "
77
+ f"override '{theme_dir.name}/<template>.html.jinja' instead",
78
+ )
79
+
80
+ if config.paths:
194
81
  full_paths = []
82
+ for path in config.paths:
83
+ if "*" in path:
84
+ full_paths.extend([d for d in base_dir.glob(path) if d.is_dir()])
85
+ else:
86
+ full_paths.append((base_dir / path).resolve())
195
87
  else:
196
- config_path = Path(config_file_path).resolve().parent
197
- full_paths = [(config_path / path).resolve() for path in paths]
88
+ full_paths = []
198
89
 
199
- if pathIds := [str(path) for path in full_paths if not path.is_dir()]:
90
+ if path_ids := [str(path) for path in full_paths if not path.is_dir()]:
200
91
  raise PluginError(
201
- "The following paths do not exist or are not directories: "
202
- + ", ".join(pathIds)
92
+ "The following paths do not exist or are not directories: " + ", ".join(path_ids)
203
93
  )
204
94
 
205
- self.paths: PathCollection = PathCollection(
206
- full_paths, recursive=paths_recursive, config_path=config_path
95
+ self._paths = full_paths
96
+ self._paths_collection: PathsCollection = PathsCollection(
97
+ full_paths, recursive=config.paths_recursive, working_directory=base_dir
207
98
  )
208
- self.lines: LinesCollection = self.paths.lines_collection
209
- self._locale: str = locale
99
+ self._lines_collection: LinesCollection = self._paths_collection.lines_collection
100
+
101
+ def get_options(self, local_options: Mapping[str, Any]) -> HandlerOptions:
102
+ """Get combined default, global and local options.
103
+
104
+ Arguments:
105
+ local_options: The local options.
210
106
 
211
- def get_templates_dir(self, *args, **kwargs) -> Path:
212
- # use the python handler templates
213
- # (it assumes the python handler is installed)
214
- return super().get_templates_dir("python")
107
+ Returns:
108
+ The combined options.
109
+ """
215
110
 
216
- def render(self, data: CollectorItem, config: Mapping[str, Any]) -> str:
111
+ extra = {
112
+ **self.global_options.get("extra", {}),
113
+ **local_options.get("extra", {}),
114
+ }
115
+ options = {**self.global_options, **local_options, "extra": extra}
116
+ try:
117
+ return MatlabOptions.from_data(**options)
118
+ except Exception as error:
119
+ raise PluginError(f"Invalid options: {error}") from error
120
+
121
+ def render(self, data: CollectorItem, options: MatlabOptions) -> str:
217
122
  """Render a template using provided data and configuration options.
218
123
 
219
124
  Arguments:
220
125
  data: The collected data to render.
221
- config: The handler's configuration options.
126
+ options: The handler's configuration options.
222
127
 
223
128
  Returns:
224
129
  The rendered template as HTML.
225
130
  """
226
- final_config = ChainMap(config, self.default_config) # type: ignore[arg-type]
227
131
 
228
- template_name = rendering.do_get_template(self.env, data)
132
+ template_name = rendering.do_get_template(data)
229
133
  template = self.env.get_template(template_name)
230
134
 
231
- heading_level = final_config["heading_level"]
232
-
233
- try:
234
- final_config["members_order"] = rendering.Order(
235
- final_config["members_order"]
236
- )
237
- except ValueError as error:
238
- choices = "', '".join(item.value for item in rendering.Order)
239
- raise PluginError(
240
- f"Unknown members_order '{final_config['members_order']}', choose between '{choices}'.",
241
- ) from error
242
-
243
- if final_config["filters"]:
244
- final_config["filters"] = [
245
- (re.compile(filtr.lstrip("!")), filtr.startswith("!"))
246
- for filtr in final_config["filters"]
247
- ]
248
-
249
- summary = final_config["summary"]
250
- if summary is True:
251
- final_config["summary"] = {
252
- "attributes": True,
253
- "functions": True,
254
- "classes": True,
255
- "modules": True,
256
- }
257
- elif summary is False:
258
- final_config["summary"] = {
259
- "attributes": False,
260
- "functions": False,
261
- "classes": False,
262
- "modules": False,
263
- }
264
- else:
265
- final_config["summary"] = {
266
- "attributes": summary.get(
267
- "properties", False
268
- ), # Map properties (MATLAB) to attributes (Python)
269
- "functions": summary.get("functions", False),
270
- "classes": summary.get("classes", False),
271
- "modules": summary.get(
272
- "namespaces", False
273
- ), # Map namespaces (MATLAB) to modules (Python)
274
- }
275
-
276
- # Map docstring options
277
- final_config["show_docstring_attributes"] = config.get(
278
- "show_docstring_properties", True
279
- )
280
- final_config["show_docstring_modules"] = config.get(
281
- "show_docstring_namespaces", True
282
- )
283
- final_config["show_docstring_parameters"] = config.get(
284
- "show_docstring_input_arguments", True
285
- )
286
- final_config["show_docstring_other_parameters"] = config.get(
287
- "show_docstring_name_value_arguments", True
288
- )
289
- final_config["show_docstring_returns"] = config.get(
290
- "show_docstring_output_arguments", True
291
- )
135
+ heading_level = options.heading_level
292
136
 
293
- # These settings must be present to avoid errors
294
- for setting in [
295
- "merge_init_into_class",
296
- "show_docstring_raises",
297
- "show_docstring_receives",
298
- "show_docstring_yields",
299
- "show_docstring_warns",
300
- ]:
301
- final_config[setting] = False
302
- final_config["line_length"] = 88
303
-
304
- return template.render(
137
+ html = template.render(
305
138
  **{
306
- "config": final_config,
139
+ "config": options,
307
140
  data.kind.value: data,
308
141
  "heading_level": heading_level,
309
142
  "root": True,
310
- "locale": self._locale,
143
+ "locale": self.config.locale,
311
144
  },
312
145
  )
313
146
 
314
- def update_env(self, md: Markdown, config: dict) -> None:
147
+ if self.env.filters["stash_crossref"].stash:
148
+ pass
149
+
150
+ return html
151
+
152
+ def update_env(self, config: Any) -> None: # noqa: ARG002
315
153
  """Update the Jinja environment with custom filters and tests.
316
154
 
317
155
  Parameters:
318
- md: The Markdown instance.
319
- config: The configuration dictionary.
156
+ config: The SSG configuration.
320
157
  """
321
- super().update_env(md, config)
322
158
  self.env.trim_blocks = True
323
159
  self.env.lstrip_blocks = True
324
160
  self.env.keep_trailing_newline = False
325
- self.env.filters["split_path"] = rendering.do_split_path
326
- self.env.filters["crossref"] = rendering.do_crossref
327
- self.env.filters["multi_crossref"] = rendering.do_multi_crossref
328
161
  self.env.filters["order_members"] = rendering.do_order_members
329
- self.env.filters["format_code"] = rendering.do_format_code
330
162
  self.env.filters["format_signature"] = rendering.do_format_signature
331
- self.env.filters["format_attribute"] = rendering.do_format_attribute
163
+ self.env.filters["format_property"] = rendering.do_format_property
164
+ self.env.filters["format_arguments"] = rendering.do_format_arguments
332
165
  self.env.filters["filter_objects"] = rendering.do_filter_objects
333
166
  self.env.filters["stash_crossref"] = rendering.do_stash_crossref
334
167
  self.env.filters["get_template"] = rendering.do_get_template
335
- self.env.filters["as_attributes_section"] = rendering.do_as_attributes_section
168
+ self.env.filters["function_docstring"] = rendering.do_function_docstring
169
+ self.env.filters["as_properties_section"] = rendering.do_as_properties_section
336
170
  self.env.filters["as_functions_section"] = rendering.do_as_functions_section
337
171
  self.env.filters["as_classes_section"] = rendering.do_as_classes_section
338
- self.env.filters["as_modules_section"] = rendering.do_as_modules_section
172
+ self.env.filters["as_namespaces_section"] = rendering.do_as_namespaces_section
173
+ self.env.filters["as_inheritance_diagram_section"] = (
174
+ rendering.do_as_inheritance_diagram_section
175
+ )
339
176
  self.env.globals["AutorefsHook"] = rendering.AutorefsHook
340
177
  self.env.tests["existing_template"] = (
341
178
  lambda template_name: template_name in self.env.list_templates()
342
179
  )
180
+ # The following is required since in MATLAB there is a concept called namespace
181
+ # This is used as a variable in Jinja templates and would overwrite the namespace macro
182
+ # Thus we create an alias for this.
183
+ self.env.globals["jinja_namespace"] = self.env.globals["namespace"]
184
+ self.env.globals["paths_collection"] = self._paths_collection
343
185
 
344
- def collect(self, identifier: str, config: Mapping[str, Any]) -> CollectorItem:
186
+ def collect(self, identifier: str, options: MatlabOptions) -> CollectorItem:
345
187
  """Collect data given an identifier and user configuration.
346
188
 
347
189
  In the implementation, you typically call a subprocess that returns JSON, and load that JSON again into
@@ -349,7 +191,7 @@ class MatlabHandler(BaseHandler):
349
191
 
350
192
  Arguments:
351
193
  identifier: An identifier for which to collect data.
352
- config: The handler's configuration options.
194
+ options: The handler's configuration options.
353
195
 
354
196
  Returns:
355
197
  CollectorItem
@@ -357,51 +199,68 @@ class MatlabHandler(BaseHandler):
357
199
  if identifier == "":
358
200
  raise CollectionError("Empty identifier")
359
201
 
360
- final_config = ChainMap(config, self.default_config) # type: ignore[arg-type]
202
+ if options == {}:
203
+ options = self.get_options({})
204
+
361
205
  try:
362
- model = self.paths.resolve(identifier, config=final_config)
206
+ if "/" in identifier:
207
+ # If the identifier contains a slash, it is a path to a file.
208
+ # We use the lines collection to get the model.
209
+ path = (self.base_dir / identifier).resolve()
210
+ if path in self._paths_collection._folders:
211
+ # If the path is a folder, we return the folder model.
212
+ model = self._paths_collection._folders[path]
213
+ else:
214
+ raise CollectionError(
215
+ f"Path '{identifier}' is not a valid path in the collection"
216
+ )
217
+ else:
218
+ model = self._paths_collection.get_member(identifier)
363
219
  except SyntaxError as ex:
364
220
  msg = str(ex)
365
221
  if ex.text:
366
- msg += ":\n" + ex.text
222
+ msg += ":\n" + str(ex.text)
367
223
  raise CollectionError(msg) from ex
368
- except Exception as ex:
224
+ except KeyError as ex:
225
+ raise CollectionError(str(ex)) from ex
226
+ except AliasResolutionError as ex:
369
227
  raise CollectionError(str(ex)) from ex
228
+
370
229
  if model is None:
371
230
  raise CollectionError(f"Identifier '{identifier}' not found")
231
+
232
+ parser_name = options.docstring_style
233
+ parser = parser_name and Parser(parser_name)
234
+ parser_options = options.docstring_options and asdict(
235
+ options.docstring_options # ty: ignore[invalid-argument-type]
236
+ )
237
+
238
+ with suppress(AliasResolutionError):
239
+ if model.docstring is not None:
240
+ model.docstring.parser = parser
241
+ model.docstring.parser_options = parser_options or {}
242
+
372
243
  return model
373
244
 
374
245
 
375
246
  def get_handler(
376
- *,
377
- theme: str,
378
- custom_templates: str | None = None,
379
- config_file_path: str | None = None,
380
- **config: Any,
247
+ handler_config: MutableMapping[str, Any],
248
+ tool_config: MkDocsConfig,
249
+ **kwargs: Any,
381
250
  ) -> MatlabHandler:
382
251
  """
383
252
  Create and return a MatlabHandler object with the specified configuration.
384
253
 
385
254
  Parameters:
386
- theme (str): The theme to be used by the handler.
387
- custom_templates (str | None, optional): Path to custom templates. Defaults to None.
388
- config_file_path (str | None, optional): Path to the configuration file. Defaults to None.
389
- paths (list[str] | None, optional): List of paths to include. Defaults to None.
390
- paths_recursive (bool, optional): Whether to include paths recursively. Defaults to False.
391
- **config (Any): Additional configuration options.
255
+ handler_config: The handler configuration.
256
+ tool_config: The tool (SSG) configuration.
392
257
 
393
258
  Returns:
394
259
  MatlabHandler: An instance of MatlabHandler configured with the provided parameters.
395
260
  """
261
+ base_dir = Path(tool_config.config_file_path or "./mkdocs.yml").parent
396
262
  return MatlabHandler(
397
- handler="matlab",
398
- theme=theme,
399
- custom_templates=custom_templates,
400
- config_file_path=config_file_path,
401
- **config['handler_config']
263
+ config=MatlabConfig.from_data(**handler_config),
264
+ base_dir=base_dir,
265
+ **kwargs,
402
266
  )
403
-
404
-
405
- if __name__ == "__main__":
406
- handler = get_handler(theme="material")
407
- pprint(handler.collect("matlab_startup", {}).docstring.parse("google"))
@@ -0,0 +1,111 @@
1
+ # This module contains the logger used throughout Griffe.
2
+ # The logger is actually a wrapper around the standard Python logger.
3
+ # We wrap it so that it is easier for other downstream libraries to patch it.
4
+ # For example, mkdocstrings-python patches the logger to relocate it as a child
5
+ # of `mkdocs.plugins` so that it fits in the MkDocs logging configuration.
6
+ #
7
+ # We use a single, global logger because our public API is exposed in a single module, `griffe`.
8
+ # Extensions however should use their own logger, which is why we provide the `get_logger` function.
9
+
10
+ from __future__ import annotations
11
+
12
+ import logging
13
+ from contextlib import contextmanager
14
+ from typing import TYPE_CHECKING, Any, Callable, ClassVar
15
+
16
+ if TYPE_CHECKING:
17
+ from collections.abc import Iterator
18
+
19
+
20
+ class Logger:
21
+ _default_logger: Any = logging.getLogger
22
+ _instances: ClassVar[dict[str, Logger]] = {}
23
+
24
+ def __init__(self, name: str) -> None:
25
+ # Default logger that can be patched by third-party.
26
+ self._logger = self.__class__._default_logger(name)
27
+
28
+ def __getattr__(self, name: str) -> Any:
29
+ # Forward everything to the logger.
30
+ return getattr(self._logger, name)
31
+
32
+ @contextmanager
33
+ def disable(self) -> Iterator[None]:
34
+ """Temporarily disable logging."""
35
+ old_level = self._logger.level
36
+ self._logger.setLevel(100)
37
+ try:
38
+ yield
39
+ finally:
40
+ self._logger.setLevel(old_level)
41
+
42
+ @classmethod
43
+ def _get(cls, name: str = "griffe") -> Logger:
44
+ if name not in cls._instances:
45
+ cls._instances[name] = cls(name)
46
+ return cls._instances[name]
47
+
48
+ @classmethod
49
+ def _patch_loggers(cls, get_logger_func: Callable) -> None:
50
+ # Patch current instances.
51
+ for name, instance in cls._instances.items():
52
+ instance._logger = get_logger_func(name)
53
+
54
+ # Future instances will be patched as well.
55
+ cls._default_logger = get_logger_func
56
+
57
+
58
+ logger: Logger = Logger._get()
59
+ """Our global logger, used throughout the library.
60
+
61
+ Griffe's output and error messages are logging messages.
62
+
63
+ Griffe provides the [`patch_loggers`][griffe.patch_loggers]
64
+ function so dependent libraries can patch Griffe loggers as they see fit.
65
+
66
+ For example, to fit in the MkDocs logging configuration
67
+ and prefix each log message with the module name:
68
+
69
+ ```python
70
+ import logging
71
+ from griffe import patch_loggers
72
+
73
+
74
+ class LoggerAdapter(logging.LoggerAdapter):
75
+ def __init__(self, prefix, logger):
76
+ super().__init__(logger, {})
77
+ self.prefix = prefix
78
+
79
+ def process(self, msg, kwargs):
80
+ return f"{self.prefix}: {msg}", kwargs
81
+
82
+
83
+ def get_logger(name):
84
+ logger = logging.getLogger(f"mkdocs.plugins.{name}")
85
+ return LoggerAdapter(name, logger)
86
+
87
+
88
+ patch_loggers(get_logger)
89
+ ```
90
+ """
91
+
92
+
93
+ def get_logger(name: str = "griffe") -> Logger:
94
+ """Create and return a new logger instance.
95
+
96
+ Parameters:
97
+ name: The logger name.
98
+
99
+ Returns:
100
+ The logger.
101
+ """
102
+ return Logger._get(name)
103
+
104
+
105
+ def patch_loggers(get_logger_func: Callable[[str], Any]) -> None:
106
+ """Patch Griffe logger and Griffe extensions' loggers.
107
+
108
+ Parameters:
109
+ get_logger_func: A function accepting a name as parameter and returning a logger.
110
+ """
111
+ Logger._patch_loggers(get_logger_func)