Skip to content

Commit 2cf37c3

Browse files
authored
Parse class folder methods correctly (#213)
Previously, a class folder definition, `@ClassFolder` was treated like a collection of a module, class with methods and free functions. Module: target.@ClassFolder Class: target.@ClassFolder.ClassFolder Functions: target.@ClassFolder.method_in_file Now, we transform this into a single class definition, that can be referenced with: Class: target.ClassFolder This fixes #56. However, #44 is not solved by this.
1 parent e29d951 commit 2cf37c3

22 files changed

+370
-13
lines changed

CHANGES.rst

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
sphinxcontrib-matlabdomain-0.20.0 (2023-MM-DD)
1+
sphinxcontrib-matlabdomain-0.20.0 (2023-09-13)
22
==============================================
33

44
* Fixed `Issue 188`_ and `Issue 189`_, which caused the extension to crash if
@@ -8,10 +8,19 @@ sphinxcontrib-matlabdomain-0.20.0 (2023-MM-DD)
88
methods) to links! This means that we can write class documentation as `MATLAB
99
Class Help`_ suggests. Including property and methods lists in the class
1010
docstring.
11-
11+
* Fixed `Issue 56`_. `MATLAB Folder Class definitions`_, i.e. prefixed with
12+
``@``, are now treated as normal classes. All methods defined in the class
13+
defintion and in files in the ``@``-folder are availble. Further, it can
14+
referenced by a shortened name. Before you had to explicity write out the
15+
"module", class and methods. Now you can just write the class name. Only
16+
caveat is that `Issue 44`_ still applies.
17+
18+
.. _Issue 44: https://github.com/sphinx-contrib/matlabdomain/issues/44
19+
.. _Issue 56: https://github.com/sphinx-contrib/matlabdomain/issues/56
1220
.. _Issue 188: https://github.com/sphinx-contrib/matlabdomain/issues/188
1321
.. _Issue 189: https://github.com/sphinx-contrib/matlabdomain/issues/189
1422
.. _MATLAB Class Help: https://mathworks.com/help/matlab/matlab_prog/create-help-for-classes.html
23+
.. _MATLAB Folder Class definitions: https://mathworks.com/help/matlab/matlab_oop/organizing-classes-in-folders.html
1524

1625

1726
sphinxcontrib-matlabdomain-0.19.1 (2023-05-17)

README.rst

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ Usage
3030

3131
The Python package must be installed with::
3232

33-
pip install -U sphinxcontrib-matlabdomain
33+
pip install sphinxcontrib-matlabdomain
3434

3535
In general, the usage is the same as for documenting Python code. The package
36-
is tested with Python >= 3.8 and Sphinx >=4.0.0.
36+
is tested with Python >= 3.8 and Sphinx >= 4.5.0.
3737

3838
For a Python 2 compatible version the package must be installed with::
3939

@@ -67,17 +67,21 @@ Additional Configuration
6767
that everything is in the path as we would expect it in MATLAB. This will
6868
resemble a more MATLAB-like presentation. If it is ``True`` is forces
6969
``matlab_keep_package_prefix = False``. Further, it allows for much shorter
70-
and cleaner references. Example, given a path to a class like
71-
``target.subfolder.ClassFoo``.
70+
and cleaner references. Example, given a path to classes like
71+
``target.subfolder.ClassFoo`` and ``target.@ClassFolder.Classfolder``
7272

7373
* With ``False``::
7474

7575
:class:`target.subfolder.ClassFoo`
7676

77+
:class:`target.@ClassFolder.Classfolder`
78+
7779
* With ``True``::
7880

7981
:class:`ClassFoo`
8082

83+
:class:`ClassFolder`
84+
8185
Default is ``False``. *Added in Version 0.19.0*.
8286

8387
``matlab_auto_link``

sphinxcontrib/mat_types.py

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,16 +119,37 @@ def shortest_name(dotted_path):
119119
if len(parts) == 1:
120120
return parts[0].lstrip("+")
121121

122+
if "@" in dotted_path:
123+
return dotted_path
124+
122125
parts_to_keep = []
123126
for part in parts[:-1]:
124-
if part.startswith("+") or part.startswith("@"):
125-
parts_to_keep.append(part.lstrip("+@"))
127+
if part.startswith("+"):
128+
parts_to_keep.append(part.lstrip("+"))
126129
elif len(parts_to_keep) > 0:
127130
parts_to_keep = []
128-
parts_to_keep.append(parts[-1].lstrip("+@"))
131+
parts_to_keep.append(parts[-1].lstrip("+"))
129132
return ".".join(parts_to_keep)
130133

131134

135+
def classfolder_class_name(dotted_path):
136+
# Returns a @ClassFolder classname if applicable, otherwise the dotted_path is returned
137+
#
138+
if "@" not in dotted_path:
139+
return dotted_path
140+
141+
parts = dotted_path.split(".")
142+
if len(parts) == 1:
143+
return dotted_path
144+
145+
stripped_parts = [part.lstrip("@") for part in parts]
146+
147+
if stripped_parts[-1] == stripped_parts[-2]:
148+
return ".".join(parts[0:-2] + [stripped_parts[-1]])
149+
else:
150+
return dotted_path
151+
152+
132153
def recursive_find_all(obj):
133154
# Recursively finds all entities in all "modules" aka directories.
134155
for _, o in obj.entities:
@@ -204,6 +225,43 @@ def analyze(app):
204225
populate_entities_table(root)
205226
entities_table["."] = root
206227

228+
# Transform Class Folders classes from
229+
#
230+
# @ClassFolder (Module)
231+
# ClassFolder (Class)
232+
# method1 (Function)
233+
# method2 (Function)
234+
#
235+
# To
236+
#
237+
# ClassFolder (Class) with the method1 and method2 add to the ClassFolder Class.
238+
class_folder_modules = {
239+
k: v for k, v in entities_table.items() if "@" in k and isinstance(v, MatModule)
240+
}
241+
# For each Class Folder module
242+
for cf_name, cf_entity in class_folder_modules.items():
243+
# Find the class entity class.
244+
class_entities = [e for e in cf_entity.entities if isinstance(e[1], MatClass)]
245+
func_entities = [e for e in cf_entity.entities if isinstance(e[1], MatFunction)]
246+
assert len(class_entities) == 1
247+
cls = class_entities[0][1]
248+
249+
# Add functions to class
250+
for func_name, func in func_entities:
251+
func.__class__ = MatMethod
252+
func.cls = cls
253+
# TODO: Find the method attributes defined in classfolder class defintion.
254+
func.attrs = {}
255+
cls.methods[func.name] = func
256+
257+
# Transform @ClassFolder names. Specifically
258+
class_folder_names = {}
259+
for name, entity in entities_table.items():
260+
alt_name = classfolder_class_name(name)
261+
if name != alt_name:
262+
class_folder_names[alt_name] = entity
263+
entities_table.update(class_folder_names)
264+
207265
# Find alternative names to entities
208266
# target.+package.+sub.Class -> package.sub.Class
209267
# folder.subfolder.Class -> Class
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
classdef First
2+
% The first class
3+
4+
properties
5+
a % The property
6+
end
7+
8+
methods
9+
function self = First(a)
10+
% Constructor for First
11+
self.a = a;
12+
end
13+
14+
function method_inside_classdef(obj, b)
15+
% Method inside class definition
16+
obj.a = b;
17+
end
18+
end
19+
end
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
function [varargout] = method_in_folder(obj, varargin)
2+
% A method defined in the folder
3+
varargout = varargin;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Minimal makefile for Sphinx documentation
2+
#
3+
4+
# You can set these variables from the command line.
5+
SPHINXOPTS =
6+
SPHINXBUILD = sphinx-build
7+
SPHINXPROJ = test_classfolder
8+
SOURCEDIR = .
9+
BUILDDIR = _build
10+
11+
# Put it first so that "make" without argument is like "make help".
12+
help:
13+
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14+
15+
.PHONY: help Makefile
16+
17+
# Catch-all target: route all unknown targets to Sphinx using the new
18+
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19+
%: Makefile
20+
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import os
2+
3+
matlab_src_dir = os.path.abspath(".")
4+
matlab_short_links = True
5+
extensions = ["sphinx.ext.autodoc", "sphinxcontrib.matlab"]
6+
primary_domain = "mat"
7+
project = "test_classfolder"
8+
master_doc = "index"
9+
source_suffix = ".rst"
10+
nitpicky = True
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
Description
2+
===========
3+
4+
In this directory we test basic autodoc features for class folders. The
5+
folder layout is::
6+
7+
test_classfolder
8+
src - A typically folder
9+
@Second
10+
+pkg
11+
@Third
12+
@First
13+
First.m
14+
15+
16+
Table of contents
17+
=================
18+
19+
.. toctree::
20+
:maxdepth: 2
21+
22+
index_first
23+
index_second
24+
index_third
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
First
2+
-----
3+
.. currentmodule:: .
4+
5+
.. autoclass:: First
6+
:show-inheritance:
7+
:members:
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Second
2+
------
3+
4+
.. currentmodule:: src
5+
6+
.. autoclass:: Second
7+
:show-inheritance:
8+
:members:

0 commit comments

Comments
 (0)