mkdocstrings-matlab 0.6.0__py3-none-any.whl → 0.7.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,7 +3,7 @@
3
3
  from collections import defaultdict, deque
4
4
  from copy import copy, deepcopy
5
5
  from pathlib import Path
6
- from typing import Mapping, Sequence
6
+ from typing import Mapping, Sequence, Callable, TypeVar
7
7
 
8
8
  from _griffe.collections import LinesCollection as GLC, ModulesCollection
9
9
  from _griffe.docstrings.models import (
@@ -24,14 +24,16 @@ from mkdocstrings_handlers.matlab.models import (
24
24
  Docstring,
25
25
  DocstringSectionText,
26
26
  Function,
27
+ Folder,
27
28
  MatlabMixin,
28
- Object,
29
29
  Namespace,
30
- ROOT,
30
+ PathMixin,
31
31
  )
32
32
  from mkdocstrings_handlers.matlab.treesitter import FileParser
33
33
 
34
34
 
35
+ PathType = TypeVar("PathType", bound=PathMixin)
36
+
35
37
  __all__ = ["LinesCollection", "PathCollection"]
36
38
 
37
39
 
@@ -104,6 +106,7 @@ class PathCollection(ModulesCollection):
104
106
  matlab_path (Sequence[str | Path]): A list of strings or Path objects representing the MATLAB paths.
105
107
  recursive (bool, optional): If True, recursively adds all subdirectories of the given paths to the search path. Defaults to False.
106
108
  config (Mapping, optional): Configuration settings for the PathCollection. Defaults to {}.
109
+ config_path (Path | None, optional): The path to the configuration file. Defaults to None.
107
110
 
108
111
  Methods:
109
112
  members() -> dict:
@@ -130,6 +133,7 @@ class PathCollection(ModulesCollection):
130
133
  matlab_path: Sequence[str | Path],
131
134
  recursive: bool = False,
132
135
  config: Mapping = {},
136
+ config_path: Path | None = None,
133
137
  ) -> None:
134
138
  """
135
139
  Initialize an instance of PathCollection.
@@ -148,6 +152,8 @@ class PathCollection(ModulesCollection):
148
152
  self._mapping: dict[str, deque[Path]] = defaultdict(deque)
149
153
  self._models: dict[Path, LazyModel] = {}
150
154
  self._members: dict[Path, list[tuple[str, Path]]] = defaultdict(list)
155
+ self._folders: dict[str, LazyModel] = {}
156
+ self._config_path = config_path
151
157
 
152
158
  self.config = config
153
159
  self.lines_collection = LinesCollection()
@@ -188,6 +194,26 @@ class PathCollection(ModulesCollection):
188
194
  model = self._models[self._mapping[identifier][0]].model()
189
195
  if model is not None:
190
196
  model = self.update_model(model, config)
197
+
198
+ elif self._config_path is not None and "/" in identifier:
199
+ absolute_path = (self._config_path / Path(identifier)).resolve()
200
+ if absolute_path.exists():
201
+ path = absolute_path.relative_to(self._config_path)
202
+ if path.suffix:
203
+ path, member = path.parent, path.stem
204
+ else:
205
+ member = None
206
+ lazymodel = self._folders.get(str(path), None)
207
+
208
+ if lazymodel is not None:
209
+ model = lazymodel.model()
210
+ if model is not None and member is not None:
211
+ model = model.members.get(member, None)
212
+ else:
213
+ model = None
214
+ else:
215
+ model = None
216
+
191
217
  else:
192
218
  model = None
193
219
  name_parts = identifier.split(".")
@@ -511,13 +537,25 @@ class PathCollection(ModulesCollection):
511
537
  else:
512
538
  self._path.appendleft(path)
513
539
 
514
- members = PathGlobber(path, recursive=recursive)
515
- for member in members:
540
+ for member in PathGlobber(path, recursive=recursive):
516
541
  model = LazyModel(member, self)
517
542
  self._models[member] = model
518
543
  self._mapping[model.name].append(member)
519
544
  self._members[path].append((model.name, member))
520
545
 
546
+ if self._config_path is not None and member.parent.stem[0] not in [
547
+ "+",
548
+ "@",
549
+ ]:
550
+ if member.parent.is_relative_to(self._config_path):
551
+ relative_path = member.parent.relative_to(self._config_path)
552
+ if member.parent not in self._folders:
553
+ self._folders[str(relative_path)] = LazyModel(
554
+ member.parent, self
555
+ )
556
+ else:
557
+ pass # TODO: Issue warning?
558
+
521
559
  def rm_path(self, path: str | Path, recursive: bool = False):
522
560
  """
523
561
  Removes a path from the search path and updates the namespace and database accordingly.
@@ -610,6 +648,10 @@ class LazyModel:
610
648
  self._path_collection: PathCollection = path_collection
611
649
  self._lines_collection: LinesCollection = path_collection.lines_collection
612
650
 
651
+ @property
652
+ def is_folder(self) -> bool:
653
+ return self._path.is_dir() and self._path.name[0] not in ["+", "@"]
654
+
613
655
  @property
614
656
  def is_class_folder(self) -> bool:
615
657
  return self._path.is_dir() and self._path.name[0] == "@"
@@ -648,7 +690,7 @@ class LazyModel:
648
690
  else:
649
691
  return name
650
692
 
651
- def model(self):
693
+ def model(self) -> MatlabMixin | None:
652
694
  if not self._path.exists():
653
695
  return None
654
696
 
@@ -657,19 +699,22 @@ class LazyModel:
657
699
  self._model = self._collect_classfolder(self._path)
658
700
  elif self.is_namespace:
659
701
  self._model = self._collect_namespace(self._path)
702
+ elif self.is_folder:
703
+ self._model = self._collect_folder(self._path)
660
704
  else:
661
705
  self._model = self._collect_path(self._path)
662
706
  if self._model is not None:
663
707
  self._model.parent = self._collect_parent(self._path.parent)
664
708
  return self._model
665
709
 
666
- def _collect_parent(self, path: Path) -> Object | _ParentGrabber:
710
+ def _collect_parent(self, path: Path) -> _ParentGrabber | None:
667
711
  if self.is_in_namespace:
668
- parent = _ParentGrabber(
669
- lambda: self._path_collection._models[path].model() or ROOT
670
- )
712
+ grabber: Callable[[], MatlabMixin | None] = self._path_collection._models[
713
+ path
714
+ ].model
715
+ parent = _ParentGrabber(grabber)
671
716
  else:
672
- parent = ROOT
717
+ parent = None
673
718
  return parent
674
719
 
675
720
  def _collect_path(self, path: Path) -> MatlabMixin:
@@ -678,6 +723,27 @@ class LazyModel:
678
723
  self._lines_collection[path] = file.content.split("\n")
679
724
  return model
680
725
 
726
+ def _collect_directory(self, path: Path, model: PathType) -> PathType:
727
+ for member in path.iterdir():
728
+ if member.is_dir() and member.name[0] in ["+", "@"]:
729
+ submodel = self._path_collection._models[member].model()
730
+ if submodel is not None:
731
+ model.members[submodel.name] = submodel
732
+
733
+ elif member.is_file() and member.suffix == ".m":
734
+ if member.name == "Contents.m":
735
+ contentsfile = self._collect_path(member)
736
+ model.docstring = contentsfile.docstring
737
+ else:
738
+ submodel = self._path_collection._models[member].model()
739
+ if submodel is not None:
740
+ model.members[submodel.name] = submodel
741
+
742
+ if model.docstring is None:
743
+ model.docstring = self._collect_readme_md(path, model)
744
+
745
+ return model
746
+
681
747
  def _collect_classfolder(self, path: Path) -> Classfolder | None:
682
748
  classfile = path / (path.name[1:] + ".m")
683
749
  if not classfile.exists():
@@ -698,31 +764,17 @@ class LazyModel:
698
764
  model.docstring = self._collect_readme_md(path, model)
699
765
  return model
700
766
 
701
- def _collect_namespace(self, path: Path) -> Namespace | None:
767
+ def _collect_namespace(self, path: Path) -> Namespace:
702
768
  name = self.name[1:].split(".")[-1]
703
769
  model = Namespace(name, filepath=path, path_collection=self._path_collection)
770
+ return self._collect_directory(path, model)
704
771
 
705
- for member in path.iterdir():
706
- if member.is_dir() and member.name[0] in ["+", "@"]:
707
- submodel = self._path_collection._models[member].model()
708
- if submodel is not None:
709
- model.members[submodel.name] = submodel
710
-
711
- elif member.is_file() and member.suffix == ".m":
712
- if member.name == "Contents.m":
713
- contentsfile = self._collect_path(member)
714
- model.docstring = contentsfile.docstring
715
- else:
716
- submodel = self._path_collection._models[member].model()
717
- if submodel is not None:
718
- model.members[submodel.name] = submodel
719
-
720
- if model.docstring is None:
721
- model.docstring = self._collect_readme_md(path, model)
722
-
723
- return model
772
+ def _collect_folder(self, path: Path) -> Folder:
773
+ name = path.stem
774
+ model = Folder(name, filepath=path, path_collection=self._path_collection)
775
+ return self._collect_directory(path, model)
724
776
 
725
- def _collect_readme_md(self, path, parent: MatlabMixin) -> Docstring | None:
777
+ def _collect_readme_md(self, path, parent: PathMixin) -> Docstring | None:
726
778
  if (path / "README.md").exists():
727
779
  readme = path / "README.md"
728
780
  elif (path / "readme.md").exists():
@@ -176,13 +176,14 @@ class MatlabHandler(BaseHandler):
176
176
  super().__init__(*args, **kwargs)
177
177
 
178
178
  if paths is None or config_file_path is None:
179
+ config_path = None
179
180
  full_paths = []
180
181
  else:
181
182
  config_path = Path(config_file_path).parent
182
183
  full_paths = [(config_path / path).resolve() for path in paths]
183
184
 
184
185
  self.paths: PathCollection = PathCollection(
185
- full_paths, recursive=paths_recursive
186
+ full_paths, recursive=paths_recursive, config_path=config_path
186
187
  )
187
188
  self.lines: LinesCollection = self.paths.lines_collection
188
189
  self._locale: str = locale
@@ -253,9 +254,7 @@ class MatlabHandler(BaseHandler):
253
254
  }
254
255
 
255
256
  # Map docstring options
256
- final_config["show_submodules"] = config.get(
257
- "show_subnamespaces", False
258
- )
257
+ final_config["show_submodules"] = config.get("show_subnamespaces", False)
259
258
  final_config["show_docstring_attributes"] = config.get(
260
259
  "show_docstring_properties", True
261
260
  )
@@ -99,7 +99,7 @@ class _ParentGrabber:
99
99
  __call__(): Calls the grabber function and returns a MatlabObject.
100
100
  """
101
101
 
102
- def __init__(self, grabber: "Callable[[], Object]") -> None:
102
+ def __init__(self, grabber: "Callable[[], MatlabMixin | None]") -> None:
103
103
  """
104
104
  Initializes the _ParentGrabber with a grabber function.
105
105
 
@@ -109,7 +109,7 @@ class _ParentGrabber:
109
109
  self._grabber = grabber
110
110
 
111
111
  @property
112
- def parent(self) -> "Object":
112
+ def parent(self) -> "MatlabMixin | None":
113
113
  """
114
114
  Calls the grabber function and returns a MatlabObject.
115
115
 
@@ -156,7 +156,7 @@ class MatlabObject(Object):
156
156
  Returns:
157
157
  str: The canonical path of the object.
158
158
  """
159
- if isinstance(self.parent, _Root):
159
+ if self.parent is None:
160
160
  return self.name
161
161
 
162
162
  if isinstance(self.parent, MatlabObject):
@@ -165,7 +165,7 @@ class MatlabObject(Object):
165
165
  parent = getattr(self.parent, "model", self.parent)
166
166
 
167
167
  if isinstance(parent, Classfolder) and self.name == parent.name:
168
- if isinstance(parent.parent, _Root) or parent.parent is None:
168
+ if parent.parent is None:
169
169
  return self.name
170
170
  else:
171
171
  return f"{parent.parent.canonical_path}.{self.name}"
@@ -173,23 +173,6 @@ class MatlabObject(Object):
173
173
  return f"{parent.canonical_path}.{self.name}" if parent else self.name
174
174
 
175
175
 
176
- class _Root(MatlabObject):
177
- """
178
- A class representing the root object in a MATLAB structure.
179
- All the objects that have the root object as parent are at the top level,
180
- and can be called directly.
181
- """
182
-
183
- def __init__(self) -> None:
184
- super().__init__("ROOT", parent=None)
185
-
186
- def __repr__(self) -> str:
187
- return "MATLABROOT"
188
-
189
-
190
- ROOT = _Root()
191
-
192
-
193
176
  class PathMixin(Object):
194
177
  """
195
178
  A mixin class that provides a filepath attribute and related functionality.
@@ -200,7 +183,6 @@ class PathMixin(Object):
200
183
 
201
184
  def __init__(self, *args: Any, filepath: Path | None = None, **kwargs: Any) -> None:
202
185
  self._filepath: Path | None = filepath
203
-
204
186
  super().__init__(*args, **kwargs)
205
187
 
206
188
  @property
@@ -215,22 +197,22 @@ class MatlabMixin(Object):
215
197
  def __init__(
216
198
  self,
217
199
  *args: Any,
218
- parent: "Class | Classfolder | Namespace | _Root | None" = None,
200
+ parent: "Class | Classfolder | Namespace | None" = None,
219
201
  docstring: Docstring | None = None,
220
202
  **kwargs: Any,
221
203
  ):
222
- self._parent: "Class | Classfolder | Namespace | _Root | _ParentGrabber | None" = parent
204
+ self._parent: "Class | Classfolder | Namespace | _ParentGrabber | None" = parent
223
205
  self._docstring: Docstring | None = docstring
224
206
  super().__init__(*args, **kwargs)
225
207
 
226
208
  @property
227
- def parent(self) -> Object:
209
+ def parent(self) -> Object | None:
228
210
  if isinstance(self._parent, MatlabMixin):
229
211
  return self._parent
230
212
  elif isinstance(self._parent, _ParentGrabber):
231
213
  return self._parent.parent
232
214
  else:
233
- return ROOT
215
+ return None
234
216
 
235
217
  @parent.setter
236
218
  def parent(self, value):
@@ -559,14 +541,34 @@ class Function(MatlabMixin, PathMixin, GriffeFunction, MatlabObject):
559
541
  pass
560
542
 
561
543
 
544
+ class Folder(MatlabMixin, PathMixin, Module, MatlabObject):
545
+ """
546
+ A class representing a Folder in a MATLAB project.
547
+
548
+ Inherits from:
549
+ - MatlabMixin: A mixin class providing MATLAB-specific functionality.
550
+ - PathMixin: A mixin class providing path-related functionality.
551
+ - Module: A class representing a module.
552
+ - MatlabObject: A base class for MATLAB objects.
553
+ """
554
+
555
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
556
+ super().__init__(*args, **kwargs)
557
+ self.labels = {"Folder"}
558
+
559
+ def __repr__(self) -> str:
560
+ return f"Folder({self.path!r})"
561
+
562
+
562
563
  class Namespace(MatlabMixin, PathMixin, Module, MatlabObject):
563
564
  """
564
565
  A class representing a namespace in a MATLAB project.
565
566
 
566
567
  Inherits from:
568
+ - MatlabMixin: A mixin class providing MATLAB-specific functionality.
567
569
  - PathMixin: A mixin class providing path-related functionality.
568
- - MatlabObject: A base class for MATLAB objects.
569
570
  - Module: A class representing a module.
571
+ - MatlabObject: A base class for MATLAB objects.
570
572
  """
571
573
 
572
574
  def __init__(self, *args: Any, **kwargs: Any) -> None:
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkdocstrings-matlab
3
- Version: 0.6.0
3
+ Version: 0.7.0
4
4
  Summary: A MATLAB handler for mkdocstrings
5
5
  Author-email: Mark Hu <watermarkhu@gmail.com>
6
- License: ISC
6
+ License: MIT
7
7
  License-File: LICENSE
8
8
  Classifier: Development Status :: 4 - Beta
9
9
  Classifier: Intended Audience :: Developers
@@ -40,7 +40,7 @@ Description-Content-Type: text/markdown
40
40
  [![documentation](https://img.shields.io/badge/docs-mkdocs-708FCC.svg?style=flat)](https://watermarkhu.nl/mkdocstrings-matlab)
41
41
  [![pypi version](https://img.shields.io/pypi/v/mkdocstrings-matlab.svg)](https://pypi.org/project/mkdocstrings-matlab/)
42
42
 
43
- The MATLAB handler uses [Tree-sitter](https://tree-sitter.github.io/tree-sitter/) and its [MATLAB parser](https://github.com/acristoffers/tree-sitter-matlab) to collect documentation from MATLAB source code. Via the python bindings the Abstract Syntax Tree (AST) of the source code is traversed to extract useful information. The imported objected are imported as custom [Griffe](https://mkdocstrings.github.io/griffe/) objects and mocked for the [python handler](https://mkdocstrings.github.io/python/).
43
+ The MATLAB handler uses [Tree-sitter](https://tree-sitter.github.io/tree-sitter/) and its [MATLAB parser](https://github.com/acristoffers/tree-sitter-matlab) to collect documentation from MATLAB source code. The AST information are imported as custom [Griffe](https://mkdocstrings.github.io/griffe/) objects and mocked for the [python handler](https://mkdocstrings.github.io/python/).
44
44
 
45
45
 
46
46
  You can install this handler by specifying it as a dependency:
@@ -64,10 +64,10 @@ dependencies = [
64
64
 
65
65
  - **Support for argument validation blocks:** Tree-sitter collects your [function and method argument validation](https://mathworks.com/help/matlab/matlab_prog/function-argument-validation-1.html)
66
66
  blocks to display input and output argument types and default values.
67
- It is even able to automatically add cross-references o other objects from your API.
67
+ It is even able to automatically add cross-references to other objects from your API.
68
68
 
69
- - **Recursive documentation of MATLAB [namespaces](https://mathworks.com/help/matlab/matlab_oop/namespaces.html):**
70
- just add `+` to the identifer, and you get the full namespace docs. You don't need to inject documentation for each class, function, and script. Additionaly, the parent namespace documentation will be either extracted from the `Contents.m` or the `readme.md` file at the root of the namespace.
69
+ - **Recursive documentation of MATLAB [namespaces](https://mathworks.com/help/matlab/matlab_oop/namespaces.html) and folders:**
70
+ just add `+` to the identifer for namespaces or the relative path for folder, and you get documentation for the entire directory. You don't need to inject documentation for each class, function, and script. Additionaly, the directory documentation will be either extracted from the `Contents.m` or the `readme.md` file at the root of the namespace or folder.
71
71
 
72
72
  - **Support for documented properties:** properties definitions followed by a docstring will be recognized in classes.
73
73
 
@@ -2,14 +2,14 @@ mkdocs_material_matlab/__init__.py,sha256=9pmrwWbkIyr0T7qvADbsz3OR5bB3rWb231e6JS
2
2
  mkdocs_material_matlab/mkdocs_material_matlab.py,sha256=s7vI1lv6hD8s7kDHWBfYKgN6EcldyUstGYJ45sA-VWY,850
3
3
  mkdocs_material_matlab/css/style.css,sha256=iVTPIKljgvK899jEQylD7yu9yjKfrVE_4GLaITjhk4g,132
4
4
  mkdocstrings_handlers/matlab/__init__.py,sha256=w5R9cGtqeJF0GUP_Jc_ad8FnS4FpbutnmHvzVRlohPM,1124
5
- mkdocstrings_handlers/matlab/collect.py,sha256=i9OudR4oJX-ELPL4GVv_AJSA5N6iZk-zTSY58dfWGNw,27512
5
+ mkdocstrings_handlers/matlab/collect.py,sha256=WXuRIDYL0T1fKv1MwwOC6uWAB80PxRCs4uYDgOHiQcg,29749
6
6
  mkdocstrings_handlers/matlab/enums.py,sha256=lr3wLlhPxyBym3O7Rt0cLUZYqPCz6wQ2PYBibLKLTek,982
7
- mkdocstrings_handlers/matlab/handler.py,sha256=koMpRSftxQbI8R2l0fGfBOe36yUkne6lATwDauBa5w0,18676
8
- mkdocstrings_handlers/matlab/models.py,sha256=7WWZ-nLaL0dWF-vOYWP-O3kNEGKtMfa5DK_J23mkhhU,18174
7
+ mkdocstrings_handlers/matlab/handler.py,sha256=-KA6kj8L55T9jxgKzNM_k59AXsyTSQJqFd8ltN00LQI,18710
8
+ mkdocstrings_handlers/matlab/models.py,sha256=0qGk6UrGHB4fC49I2cjCgqLFAIozqtmLAxl9PPN6Blc,18461
9
9
  mkdocstrings_handlers/matlab/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  mkdocstrings_handlers/matlab/treesitter.py,sha256=FkWGuH7EmE_aO2qub5-_NnOVacYeXW353SkB7cNMlRo,21820
11
- mkdocstrings_matlab-0.6.0.dist-info/METADATA,sha256=3Q5DCapIgie22A0okETTVhAkZKtde7wJ_G5W4SgRkJM,4779
12
- mkdocstrings_matlab-0.6.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
13
- mkdocstrings_matlab-0.6.0.dist-info/entry_points.txt,sha256=qUZFuB2TKh7KPlg4dR2npfbNgNExw6O6j1vF276PtPw,92
14
- mkdocstrings_matlab-0.6.0.dist-info/licenses/LICENSE,sha256=TZQpwBuA3KLH56--XDAY2Qwo9gGdxeTITYhMOylmYhg,743
15
- mkdocstrings_matlab-0.6.0.dist-info/RECORD,,
11
+ mkdocstrings_matlab-0.7.0.dist-info/METADATA,sha256=5jzSXQm4dSLd9GBaJWSAvJm1JGBcuAGUaLWLpMoiGu0,4736
12
+ mkdocstrings_matlab-0.7.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
13
+ mkdocstrings_matlab-0.7.0.dist-info/entry_points.txt,sha256=qUZFuB2TKh7KPlg4dR2npfbNgNExw6O6j1vF276PtPw,92
14
+ mkdocstrings_matlab-0.7.0.dist-info/licenses/LICENSE,sha256=TZQpwBuA3KLH56--XDAY2Qwo9gGdxeTITYhMOylmYhg,743
15
+ mkdocstrings_matlab-0.7.0.dist-info/RECORD,,