diff --git a/README.rst b/README.rst index 33f0ade..553d1cb 100644 --- a/README.rst +++ b/README.rst @@ -97,6 +97,24 @@ Additional Configuration If you want the closest to MATLAB documentation style, use ``matlab_short_links = True`` in your ``conf.py`` file. +``matlab_auto_link`` + Automatically convert the names of known entities (e.g. classes, functions, + properties, methods) to links Valid values are ``"basic"`` + and ``"all"``. + * ``"basic"`` - Auto-links (1) known classes or functions that appear + in docstring lines that begin with "See also" and any subsequent + lines before the next blank line (unknown names are wrapped in + double-backquotes), and (2) property and method names that appear in + lists under " Properties:" and " Methods:" headings + in class docstrings. + + * ``"all"`` - Auto-links everything included with ``"basic"``, plus all + known classes and functions everywhere else they appear in any docstring, + and any names ending with "()" within class, property, or method docstrings + that match a method of the corresponding class. + + Default is ``None``. *Added in Version 0.20.0*. + Roles and Directives -------------------- diff --git a/sphinxcontrib/mat_documenters.py b/sphinxcontrib/mat_documenters.py index 3fbe167..dcc130e 100644 --- a/sphinxcontrib/mat_documenters.py +++ b/sphinxcontrib/mat_documenters.py @@ -20,6 +20,7 @@ MatModuleAnalyzer, MatApplication, entities_table, + entities_name_map, strip_package_prefix, ) @@ -171,6 +172,8 @@ def add_content(self, more_content, no_docstring=False): # autodoc-process-docstring is fired and can add some # content if desired docstrings.append([]) + if self.env.config.matlab_auto_link: + docstrings = self.auto_link(docstrings) for i, line in enumerate(self.process_doc(docstrings)): self.add_line(line, sourcename, i) @@ -179,6 +182,129 @@ def add_content(self, more_content, no_docstring=False): for line, src in zip(more_content.data, more_content.items): self.add_line(line, src[0], src[1]) + def auto_link_basic(self, docstrings): + return self.auto_link_see_also(docstrings) + + def auto_link_see_also(self, docstrings): + # autolink known names in See also + see_also_re = re.compile(r"(See also:?\s*)(\b.*\b)(.*)", re.IGNORECASE) + see_also_cond_re = re.compile(r"(\s*)(\b.*\b)(.*)") + is_see_also_line = False + for i in range(len(docstrings)): + for j in range(len(docstrings[i])): + line = docstrings[i][j] + if line: # non-blank line + if is_see_also_line: + # find name + match = see_also_cond_re.search(line) + entries_str = match.group(2) # the entries + elif match := see_also_re.search(line): + is_see_also_line = True # line begins with "See also" + entries_str = match.group(2) # the entries + elif is_see_also_line: # blank line following see also section + is_see_also_line = False # end see also section + + if is_see_also_line and entries_str: + # split on , + entries = re.split(r"\s*,\s*", entries_str) + for k in range(len(entries)): + if entries[k].endswith("`"): + continue + + if ( + self.env.config.matlab_keep_package_prefix + and entries[k] in entities_table + ): + o = entities_table[entries[k]] + elif ( + not self.env.config.matlab_keep_package_prefix + and entries[k] in entities_name_map + ): + o = entities_table[entities_name_map[entries[k]]] + else: + o = None + if o: + role = o.ref_role() + if role in ["class", "func"]: + entries[k] = f":{role}:`{entries[k]}`" + else: + entries[k] = f"``{entries[k]}``" + docstrings[i][j] = ( + match.group(1) + ", ".join(entries) + match.group(3) + ) + return docstrings + + def auto_link_all(self, docstrings): + # auto-link known classes and functions everywhere + for n, o in entities_table.items(): + role = o.ref_role() + if role in ["class", "func"]: + nn = n.replace("+", "") # remove + from name + pat = ( + r"(?() + pat = r"(?`", + docstrings[i][j], + ) + + return docstrings + def get_object_members(self, want_all): """Return `(members_check_module, members)` where `members` is a list of `(membername, member)` pairs of the members of *self.object*. @@ -776,21 +902,7 @@ def make_baseclass_links(env, obj): if not entity: links.append(":class:`%s`" % base_class_name) else: - modname = entity.__module__ - classname = entity.name - if not env.config.matlab_keep_package_prefix: - modname = strip_package_prefix(modname) - - if env.config.matlab_short_links: - # modname is only used for package names - # - "target.+package" => "package" - # - "target" => "" - parts = modname.split(".") - parts = [part for part in parts if part.startswith("+")] - modname = ".".join(parts) - - link_name = f"{modname}.{classname}" - links.append(f":class:`{base_class_name}<{link_name}>`") + links.append(entity.link(env)) return links @@ -926,6 +1038,58 @@ def add_content(self, more_content, no_docstring=False): else: MatModuleLevelDocumenter.add_content(self, more_content) + def auto_link_basic(self, docstrings): + docstrings = MatlabDocumenter.auto_link_basic(self, docstrings) + return self.auto_link_class_members(docstrings) + + def auto_link_class_members(self, docstrings): + # auto link property and method names in class docstring + prop_re = re.compile(r"(.* Properties:)", re.IGNORECASE) + meth_re = re.compile(r"(.* Methods:)", re.IGNORECASE) + is_prop_line = False + is_meth_line = False + for i in range(len(docstrings)): + for j in range(len(docstrings[i])): + line = docstrings[i][j] + if line: # non-blank line + if prop_re.search(line): # line ends with "Properties:" + is_prop_line = True + is_meth_line = False + elif meth_re.search(line): # line ends with "Methods:" + is_prop_line = False + is_meth_line = True + elif is_prop_line: + # auto-link first word to corresponding property, if it exists + docstrings[i][j] = self.link_member("attr", line) + elif is_meth_line: + # auto-link first word to corresponding method, if it exists + docstrings[i][j] = self.link_member("meth", line) + elif is_prop_line: # blank line following properties section + is_prop_line = False # end properties section + elif is_meth_line: # blank line following methods section + is_meth_line = False # end methods section + + return docstrings + + def link_member(self, type, line): + if type == "meth": + parens = "()" + else: + parens = "" + p = re.compile(r"((\*\s*)?(\b\w*\b))(?=\s*-)") + if match := p.search(line): + name = match.group(3) + line = p.sub( + f"* :{type}:`{name}{parens} <{self.object.fullname(self.env)}.{name}>`", + line, + 1, + ) + return line + + def auto_link_all(self, docstrings): + docstrings = MatlabDocumenter.auto_link_all(self, docstrings) + return self.auto_link_methods(self.object, docstrings) + def document_members(self, all_members=False): if self.doc_as_attr: return @@ -1087,6 +1251,10 @@ def format_args(self): def document_members(self, all_members=False): pass + def auto_link_all(self, docstrings): + docstrings = MatlabDocumenter.auto_link_all(self, docstrings) + return self.auto_link_methods(self.object.cls, docstrings) + class MatAttributeDocumenter(MatClassLevelDocumenter): """ @@ -1157,6 +1325,10 @@ def add_content(self, more_content, no_docstring=False): # no_docstring = True MatClassLevelDocumenter.add_content(self, more_content, no_docstring) + def auto_link_all(self, docstrings): + docstrings = MatlabDocumenter.auto_link_all(self, docstrings) + return self.auto_link_methods(self.object.cls, docstrings) + class MatInstanceAttributeDocumenter(MatAttributeDocumenter): """ diff --git a/sphinxcontrib/mat_types.py b/sphinxcontrib/mat_types.py index 0206289..4908dde 100644 --- a/sphinxcontrib/mat_types.py +++ b/sphinxcontrib/mat_types.py @@ -51,6 +51,12 @@ # Will result in a short name of: package.ClassBar entities_table = {} +# Dictionary containing a map of names WITHOUT '+' in package names to +# the corresponding names WITH '+' in the package name. This is only +# used if "matlab_auto_link" is on AND "matlab_keep_package_prefix" +# is True AND a docstring with "see also" is encountered. +entities_name_map = {} + def shortest_name(dotted_path): # Creates the shortest valid MATLAB name from a dotted path @@ -108,6 +114,7 @@ def populate_entities_table(obj, path=""): fullpath = path + "." + o.name fullpath = fullpath.lstrip(".") entities_table[fullpath] = o + entities_name_map[strip_package_prefix(fullpath)] = fullpath if isinstance(o, MatModule): if o.entities: populate_entities_table(o, fullpath) @@ -152,6 +159,7 @@ def analyze(app): short_name = shortest_name(name) if short_name != name: short_names[short_name] = entity + entities_name_map[short_name] = short_name entities_table.update(short_names) @@ -191,6 +199,10 @@ def __init__(self, name): #: name of MATLAB object self.name = name + def ref_role(self): + """Returns role to use for references to this object (e.g. when generating auto-links)""" + return "ref" + @property def __name__(self): return self.name @@ -430,6 +442,10 @@ def __init__(self, name, path, package): #: entities found in the module: class, function, module (subpath and +package) self.entities = [] + def ref_role(self): + """Returns role to use for references to this object (e.g. when generating auto-links)""" + return "mod" + def safe_getmembers(self): logger.debug( f"[sphinxcontrib-matlabdomain] MatModule.safe_getmembers {self.name=}, {self.path=}, {self.package=}" @@ -842,6 +858,10 @@ def __init__(self, name, modname, tokens): if len(tks) > 0: self.rem_tks = tks # save extra tokens + def ref_role(self): + """Returns role to use for references to this object (e.g. when generating auto-links)""" + return "func" + @property def __doc__(self): return self.docstring @@ -1306,6 +1326,35 @@ def __init__(self, name, modname, tokens): self.rem_tks = idx # index of last token + def ref_role(self): + """Returns role to use for references to this object (e.g. when generating auto-links)""" + return "class" + + def fullname(self, env): + """Returns full name for class object, for use as link target""" + modname = self.__module__ + classname = self.name + if env.config.matlab_short_links: + # modname is only used for package names + # - "target.+package" => "package" + # - "target" => "" + parts = modname.split(".") + parts = [part for part in parts if part.startswith("+")] + modname = ".".join(parts) + + if not env.config.matlab_keep_package_prefix: + modname = strip_package_prefix(modname) + + return f"{modname}.{classname}".lstrip(".") + + def link(self, env, name=None): + """Returns link for class object""" + target = self.fullname(env) + if name: + return f":class:`{name} <{target}>`" + else: + return f":class:`{target}`" + def attributes(self, idx, attr_types): """ Retrieve MATLAB class, property and method attributes. @@ -1460,6 +1509,10 @@ def __init__(self, name, cls, attrs): self.docstring = attrs["docstring"] # self.class = attrs['class'] + def ref_role(self): + """Returns role to use for references to this object (e.g. when generating auto-links)""" + return "attr" + @property def __doc__(self): return self.docstring @@ -1472,6 +1525,10 @@ def __init__(self, modname, tks, cls, attrs): self.cls = cls self.attrs = attrs + def ref_role(self): + """Returns role to use for references to this object (e.g. when generating auto-links)""" + return "meth" + def skip_tokens(self): # Number of tokens to skip in `MatClass` num_rem_tks = len(self.rem_tks) diff --git a/sphinxcontrib/matlab.py b/sphinxcontrib/matlab.py index 1071802..d0da000 100644 --- a/sphinxcontrib/matlab.py +++ b/sphinxcontrib/matlab.py @@ -863,6 +863,7 @@ def setup(app): app.add_config_value("matlab_keep_package_prefix", False, "env") app.add_config_value("matlab_show_property_default_value", False, "env") app.add_config_value("matlab_short_links", False, "env") + app.add_config_value("matlab_auto_link", None, "env") app.registry.add_documenter("mat:module", doc.MatModuleDocumenter) app.add_directive_to_domain( diff --git a/tests/roots/test_autodoc/BaseClass.m b/tests/roots/test_autodoc/BaseClass.m index a2b8ba5..b434107 100644 --- a/tests/roots/test_autodoc/BaseClass.m +++ b/tests/roots/test_autodoc/BaseClass.m @@ -1,5 +1,13 @@ classdef BaseClass % A class in the very root of the directory +% +% BaseClass Methods: +% BaseClass - the constructor, whose description extends +% to the next line +% DoBase - another BaseClass method +% +% See Also +% target.ClassExample, baseFunction, ClassExample methods function obj = BaseClass(obj,args) diff --git a/tests/roots/test_autodoc/baseFunction.m b/tests/roots/test_autodoc/baseFunction.m index d1fa784..4b9fa81 100644 --- a/tests/roots/test_autodoc/baseFunction.m +++ b/tests/roots/test_autodoc/baseFunction.m @@ -1,3 +1,10 @@ function y = baseFunction(x) % Return the base of x +% +% See Also: +% target.submodule.ClassMeow +% target.package.ClassBar +% ClassMeow +% package.ClassBar + y = x; diff --git a/tests/roots/test_autodoc/target/+package/ClassBar.m b/tests/roots/test_autodoc/target/+package/ClassBar.m index fec1e58..6161af9 100644 --- a/tests/roots/test_autodoc/target/+package/ClassBar.m +++ b/tests/roots/test_autodoc/target/+package/ClassBar.m @@ -1,10 +1,10 @@ classdef ClassBar < handle -% The Bar and Foo handler +% The Bar and Foo handler, with a doFoo() method. properties bars = 'bars' % Number of bars - % Number of foos + % Number of foos, used by doBar() method foos = 10 end @@ -20,7 +20,7 @@ function doFoo(obj) end function doBar(obj) - % Doing bar + % Doing bar, not called by ClassBar() end end end diff --git a/tests/roots/test_autodoc/target/+package/funcFoo.m b/tests/roots/test_autodoc/target/+package/funcFoo.m index a49774d..435ccbf 100644 --- a/tests/roots/test_autodoc/target/+package/funcFoo.m +++ b/tests/roots/test_autodoc/target/+package/funcFoo.m @@ -1,5 +1,10 @@ function [x, y] = funcFoo(u, t) % Function that does Foo +% :: % +% x = package.funcFoo(u) +% [x, y] = package.funcFoo(u, t) +% +% Test for auto-linking with baseFunction and BaseClass, etc. x = u; y = t; diff --git a/tests/roots/test_autodoc/target/ClassExample.m b/tests/roots/test_autodoc/target/ClassExample.m index 0632b33..c6c4eec 100644 --- a/tests/roots/test_autodoc/target/ClassExample.m +++ b/tests/roots/test_autodoc/target/ClassExample.m @@ -1,9 +1,15 @@ classdef ClassExample < handle % Example class % - % :param a: first property of :class:`ClassExample` - % :param b: second property of :class:`ClassExample` - % :param c: third property of :class:`ClassExample` + % ClassExample Properties: + % a - first property of ClassExample + % b - second property of ClassExample + % c - third property of ClassExample + % ClassExample Methods: + % ClassExample - the constructor and a reference to mymethod() + % mymethod - a method in ClassExample + % + % See also BaseClass, baseFunction, unknownEntity. properties a % a property diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index ffc980a..607b07b 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -31,26 +31,97 @@ def test_target(make_app, rootdir): app.builder.build_all() content = pickle.loads((app.doctreedir / "index_target.doctree").read_bytes()) + property_section = content[0][2][1][2][0] # a bit fragile, I know + method_section = content[0][2][1][2][1] # a bit fragile, I know assert len(content) == 1 assert ( content[0].astext() - == "target\n\n\n\nclass target.ClassExample(a)\n\nBases: handle\n\nExample class\n\nParameters\n\na – first property of ClassExample\n\nb – second property of ClassExample\n\nc – third property of ClassExample\n\nProperty Summary\n\n\n\n\n\na\n\na property\n\n\n\nb\n\na property with default value\n\n\n\nc\n\na property with multiline default value\n\nMethod Summary\n\n\n\n\n\nmymethod(b)\n\nA method in ClassExample\n\nParameters\n\nb – an input to mymethod()" + == "target\n\n\n\nclass target.ClassExample(a)\n\nBases: handle\n\nExample class\n\nClassExample Properties:\n\na - first property of ClassExample\nb - second property of ClassExample\nc - third property of ClassExample\n\nClassExample Methods:\n\nClassExample - the constructor and a reference to mymethod()\nmymethod - a method in ClassExample\n\nSee also BaseClass, baseFunction, unknownEntity.\n\nProperty Summary\n\n\n\n\n\na\n\na property\n\n\n\nb\n\na property with default value\n\n\n\nc\n\na property with multiline default value\n\nMethod Summary\n\n\n\n\n\nmymethod(b)\n\nA method in ClassExample\n\nParameters\n\nb – an input to mymethod()" + ) + assert ( + property_section.rawsource + == "ClassExample Properties:\na - first property of ClassExample\nb - second property of ClassExample\nc - third property of ClassExample" + ) + assert ( + method_section.rawsource + == "ClassExample Methods:\nClassExample - the constructor and a reference to mymethod()\nmymethod - a method in ClassExample\n" ) @pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") def test_target_show_default_value(make_app, rootdir): srcdir = rootdir / "roots" / "test_autodoc" - app = make_app( - srcdir=srcdir, confoverrides={"matlab_show_property_default_value": True} - ) + confdict = {"matlab_show_property_default_value": True} + app = make_app(srcdir=srcdir, confoverrides=confdict) app.builder.build_all() content = pickle.loads((app.doctreedir / "index_target.doctree").read_bytes()) + property_section = content[0][2][1][2][0] # a bit fragile, I know + method_section = content[0][2][1][2][1] # a bit fragile, I know assert len(content) == 1 assert ( content[0].astext() - == "target\n\n\n\nclass target.ClassExample(a)\n\nBases: handle\n\nExample class\n\nParameters\n\na – first property of ClassExample\n\nb – second property of ClassExample\n\nc – third property of ClassExample\n\nProperty Summary\n\n\n\n\n\na\n\na property\n\n\n\nb = 10\n\na property with default value\n\n\n\nc = [10; ... 30]\n\na property with multiline default value\n\nMethod Summary\n\n\n\n\n\nmymethod(b)\n\nA method in ClassExample\n\nParameters\n\nb – an input to mymethod()" + == "target\n\n\n\nclass target.ClassExample(a)\n\nBases: handle\n\nExample class\n\nClassExample Properties:\n\na - first property of ClassExample\nb - second property of ClassExample\nc - third property of ClassExample\n\nClassExample Methods:\n\nClassExample - the constructor and a reference to mymethod()\nmymethod - a method in ClassExample\n\nSee also BaseClass, baseFunction, unknownEntity.\n\nProperty Summary\n\n\n\n\n\na\n\na property\n\n\n\nb = 10\n\na property with default value\n\n\n\nc = [10; ... 30]\n\na property with multiline default value\n\nMethod Summary\n\n\n\n\n\nmymethod(b)\n\nA method in ClassExample\n\nParameters\n\nb – an input to mymethod()" + ) + assert ( + property_section.rawsource + == "ClassExample Properties:\na - first property of ClassExample\nb - second property of ClassExample\nc - third property of ClassExample" + ) + assert ( + method_section.rawsource + == "ClassExample Methods:\nClassExample - the constructor and a reference to mymethod()\nmymethod - a method in ClassExample\n" + ) + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") +def test_target_auto_link_basic(make_app, rootdir): + srcdir = rootdir / "roots" / "test_autodoc" + confdict = {"matlab_auto_link": "basic"} + app = make_app(srcdir=srcdir, confoverrides=confdict) + app.builder.build_all() + + content = pickle.loads((app.doctreedir / "index_target.doctree").read_bytes()) + property_section = content[0][2][1][2][0] # a bit fragile, I know + method_section = content[0][2][1][2][1] # a bit fragile, I know + see_also_line = content[0][2][1][3] # a bit fragile, I know + assert len(content) == 1 + assert ( + property_section.rawsource + == "ClassExample Properties:\n* :attr:`a ` - first property of ClassExample\n* :attr:`b ` - second property of ClassExample\n* :attr:`c ` - third property of ClassExample" + ) + assert ( + method_section.rawsource + == "ClassExample Methods:\n* :meth:`ClassExample() ` - the constructor and a reference to mymethod()\n* :meth:`mymethod() ` - a method in ClassExample\n" + ) + assert ( + see_also_line.rawsource + == "See also :class:`BaseClass`, :func:`baseFunction`, ``unknownEntity``." + ) + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") +def test_target_auto_link_all(make_app, rootdir): + srcdir = rootdir / "roots" / "test_autodoc" + confdict = {"matlab_auto_link": "all"} + app = make_app(srcdir=srcdir, confoverrides=confdict) + app.builder.build_all() + + content = pickle.loads((app.doctreedir / "index_target.doctree").read_bytes()) + property_section = content[0][2][1][2][0] # a bit fragile, I know + method_section = content[0][2][1][2][1] # a bit fragile, I know + see_also_line = content[0][2][1][3] # a bit fragile, I know + assert len(content) == 1 + assert ( + property_section.rawsource + == "ClassExample Properties:\n* :attr:`a ` - first property of :class:`ClassExample`\n* :attr:`b ` - second property of :class:`ClassExample`\n* :attr:`c ` - third property of :class:`ClassExample`" + ) + assert ( + method_section.rawsource + == "ClassExample Methods:\n* :meth:`ClassExample() ` - the constructor and a reference to :meth:`mymethod() `\n* :meth:`mymethod() ` - a method in :class:`ClassExample`\n" + ) + assert ( + see_also_line.rawsource + == "See also :class:`BaseClass`, :func:`baseFunction`, ``unknownEntity``." ) @@ -75,26 +146,61 @@ def test_package(make_app, rootdir): app.builder.build_all() content = pickle.loads((app.doctreedir / "index_package.doctree").read_bytes()) + docstring1 = content[0][2][1][1] # a bit fragile, I know + docstring2 = content[0][2][1][2][0][1][1][4][1][0] # a bit fragile, I know + docstring3 = content[0][2][1][2][0][2][1][2][1][0] # a bit fragile, I know assert len(content) == 1 assert ( content[0].astext() - == "package\n\n\n\nclass target.package.ClassBar\n\nBases: handle\n\nThe Bar and Foo handler\n\nConstructor Summary\n\n\n\n\n\nClassBar()\n\nInitialize the bars and foos\n\nProperty Summary\n\n\n\n\n\nbars\n\nNumber of bars\n\n\n\nfoos\n\nNumber of foos\n\nMethod Summary\n\n\n\n\n\ndoBar()\n\nDoing bar\n\n\n\ndoFoo()\n\nDoing foo\n\n\n\n\n\n\n\ntarget.package.funcFoo(u, t)\n\nFunction that does Foo" + == "package\n\n\n\nclass target.package.ClassBar\n\nBases: handle\n\nThe Bar and Foo handler, with a doFoo() method.\n\nConstructor Summary\n\n\n\n\n\nClassBar()\n\nInitialize the bars and foos\n\nProperty Summary\n\n\n\n\n\nbars\n\nNumber of bars\n\n\n\nfoos\n\nNumber of foos, used by doBar() method\n\nMethod Summary\n\n\n\n\n\ndoBar()\n\nDoing bar, not called by ClassBar()\n\n\n\ndoFoo()\n\nDoing foo\n\n\n\n\n\n\n\ntarget.package.funcFoo(u, t)\n\nFunction that does Foo\n\nx = package.funcFoo(u)\n[x, y] = package.funcFoo(u, t)\n\nTest for auto-linking with baseFunction and BaseClass, etc." ) + assert docstring1.rawsource == "The Bar and Foo handler, with a doFoo() method." + assert docstring2.rawsource == "Number of foos, used by doBar() method" + assert docstring3.rawsource == "Doing bar, not called by ClassBar()" @pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") def test_package_show_default_value(make_app, rootdir): srcdir = rootdir / "roots" / "test_autodoc" - app = make_app( - srcdir=srcdir, confoverrides={"matlab_show_property_default_value": True} + confdict = {"matlab_show_property_default_value": True} + app = make_app(srcdir=srcdir, confoverrides=confdict) + app.builder.build_all() + + content = pickle.loads((app.doctreedir / "index_package.doctree").read_bytes()) + assert len(content) == 1 + assert ( + content[0].astext() + == "package\n\n\n\nclass target.package.ClassBar\n\nBases: handle\n\nThe Bar and Foo handler, with a doFoo() method.\n\nConstructor Summary\n\n\n\n\n\nClassBar()\n\nInitialize the bars and foos\n\nProperty Summary\n\n\n\n\n\nbars = 'bars'\n\nNumber of bars\n\n\n\nfoos = 10\n\nNumber of foos, used by doBar() method\n\nMethod Summary\n\n\n\n\n\ndoBar()\n\nDoing bar, not called by ClassBar()\n\n\n\ndoFoo()\n\nDoing foo\n\n\n\n\n\n\n\ntarget.package.funcFoo(u, t)\n\nFunction that does Foo\n\nx = package.funcFoo(u)\n[x, y] = package.funcFoo(u, t)\n\nTest for auto-linking with baseFunction and BaseClass, etc." ) + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") +def test_package_auto_link_all(make_app, rootdir): + srcdir = rootdir / "roots" / "test_autodoc" + confdict = {"matlab_auto_link": "all"} + app = make_app(srcdir=srcdir, confoverrides=confdict) app.builder.build_all() content = pickle.loads((app.doctreedir / "index_package.doctree").read_bytes()) + docstring1 = content[0][2][1][1] # a bit fragile, I know + docstring2 = content[0][2][1][2][0][1][1][4][1][0] # a bit fragile, I know + docstring3 = content[0][2][1][2][0][2][1][2][1][0] # a bit fragile, I know assert len(content) == 1 assert ( content[0].astext() - == "package\n\n\n\nclass target.package.ClassBar\n\nBases: handle\n\nThe Bar and Foo handler\n\nConstructor Summary\n\n\n\n\n\nClassBar()\n\nInitialize the bars and foos\n\nProperty Summary\n\n\n\n\n\nbars = 'bars'\n\nNumber of bars\n\n\n\nfoos = 10\n\nNumber of foos\n\nMethod Summary\n\n\n\n\n\ndoBar()\n\nDoing bar\n\n\n\ndoFoo()\n\nDoing foo\n\n\n\n\n\n\n\ntarget.package.funcFoo(u, t)\n\nFunction that does Foo" + == "package\n\n\n\nclass target.package.ClassBar\n\nBases: handle\n\nThe Bar and Foo handler, with a doFoo() method.\n\nConstructor Summary\n\n\n\n\n\nClassBar()\n\nInitialize the bars and foos\n\nProperty Summary\n\n\n\n\n\nbars\n\nNumber of bars\n\n\n\nfoos\n\nNumber of foos, used by doBar() method\n\nMethod Summary\n\n\n\n\n\ndoBar()\n\nDoing bar, not called by ClassBar()\n\n\n\ndoFoo()\n\nDoing foo\n\n\n\n\n\n\n\ntarget.package.funcFoo(u, t)\n\nFunction that does Foo\n\nx = package.funcFoo(u)\n[x, y] = package.funcFoo(u, t)\n\nTest for auto-linking with baseFunction() and BaseClass, etc." + ) + assert ( + docstring1.rawsource + == "The Bar and Foo handler, with a :meth:`doFoo() ` method." + ) + assert ( + docstring2.rawsource + == "Number of foos, used by :meth:`doBar() ` method" + ) + assert ( + docstring3.rawsource + == "Doing bar, not called by :meth:`ClassBar() `" ) @@ -105,26 +211,27 @@ def test_submodule(make_app, rootdir): app.builder.build_all() content = pickle.loads((app.doctreedir / "index_submodule.doctree").read_bytes()) + bases_line = content[0][2][1][0] assert len(content) == 1 assert ( content[0].astext() - == "submodule\n\n\n\nclass target.submodule.ClassMeow\n\nBases: package.ClassBar\n\nClass which inherits from a package\n\nMethod Summary\n\n\n\n\n\nsay()\n\nSay Meow\n\n\n\ntarget.submodule.funcMeow(input)\n\nTests a function with comments after docstring" + == "submodule\n\n\n\nclass target.submodule.ClassMeow\n\nBases: target.package.ClassBar\n\nClass which inherits from a package\n\nMethod Summary\n\n\n\n\n\nsay()\n\nSay Meow\n\n\n\ntarget.submodule.funcMeow(input)\n\nTests a function with comments after docstring" ) + assert bases_line.rawsource == "Bases: :class:`target.package.ClassBar`" @pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") def test_submodule_show_default_value(make_app, rootdir): srcdir = rootdir / "roots" / "test_autodoc" - app = make_app( - srcdir=srcdir, confoverrides={"matlab_show_property_default_value": True} - ) + confdict = {"matlab_show_property_default_value": True} + app = make_app(srcdir=srcdir, confoverrides=confdict) app.builder.build_all() content = pickle.loads((app.doctreedir / "index_submodule.doctree").read_bytes()) assert len(content) == 1 assert ( content[0].astext() - == "submodule\n\n\n\nclass target.submodule.ClassMeow\n\nBases: package.ClassBar\n\nClass which inherits from a package\n\nMethod Summary\n\n\n\n\n\nsay()\n\nSay Meow\n\n\n\ntarget.submodule.funcMeow(input)\n\nTests a function with comments after docstring" + == "submodule\n\n\n\nclass target.submodule.ClassMeow\n\nBases: target.package.ClassBar\n\nClass which inherits from a package\n\nMethod Summary\n\n\n\n\n\nsay()\n\nSay Meow\n\n\n\ntarget.submodule.funcMeow(input)\n\nTests a function with comments after docstring" ) @@ -138,23 +245,48 @@ def test_root(make_app, rootdir): assert len(content) == 1 assert ( content[0].astext() - == "root\n\n\n\nclass BaseClass(args)\n\nA class in the very root of the directory\n\nConstructor Summary\n\n\n\n\n\nBaseClass(args)\n\nThe constructor\n\nMethod Summary\n\n\n\n\n\nDoBase()\n\nDo the Base thing\n\n\n\nbaseFunction(x)\n\nReturn the base of x" + == "root\n\n\n\nclass BaseClass(args)\n\nA class in the very root of the directory\n\nBaseClass Methods:\n\nBaseClass - the constructor, whose description extends\n\nto the next line\n\nDoBase - another BaseClass method\n\nSee Also\n\ntarget.ClassExample, baseFunction, ClassExample\n\nConstructor Summary\n\n\n\n\n\nBaseClass(args)\n\nThe constructor\n\nMethod Summary\n\n\n\n\n\nDoBase()\n\nDo the Base thing\n\n\n\nbaseFunction(x)\n\nReturn the base of x\n\nSee Also:\n\ntarget.submodule.ClassMeow\ntarget.package.ClassBar\nClassMeow\npackage.ClassBar" ) @pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") def test_root_show_default_value(make_app, rootdir): srcdir = rootdir / "roots" / "test_autodoc" - app = make_app( - srcdir=srcdir, confoverrides={"matlab_show_property_default_value": True} - ) + confdict = {"matlab_show_property_default_value": True} + app = make_app(srcdir=srcdir, confoverrides=confdict) app.builder.build_all() content = pickle.loads((app.doctreedir / "index_root.doctree").read_bytes()) assert len(content) == 1 assert ( content[0].astext() - == "root\n\n\n\nclass BaseClass(args)\n\nA class in the very root of the directory\n\nConstructor Summary\n\n\n\n\n\nBaseClass(args)\n\nThe constructor\n\nMethod Summary\n\n\n\n\n\nDoBase()\n\nDo the Base thing\n\n\n\nbaseFunction(x)\n\nReturn the base of x" + == "root\n\n\n\nclass BaseClass(args)\n\nA class in the very root of the directory\n\nBaseClass Methods:\n\nBaseClass - the constructor, whose description extends\n\nto the next line\n\nDoBase - another BaseClass method\n\nSee Also\n\ntarget.ClassExample, baseFunction, ClassExample\n\nConstructor Summary\n\n\n\n\n\nBaseClass(args)\n\nThe constructor\n\nMethod Summary\n\n\n\n\n\nDoBase()\n\nDo the Base thing\n\n\n\nbaseFunction(x)\n\nReturn the base of x\n\nSee Also:\n\ntarget.submodule.ClassMeow\ntarget.package.ClassBar\nClassMeow\npackage.ClassBar" + ) + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") +def test_root_auto_link_basic(make_app, rootdir): + srcdir = rootdir / "roots" / "test_autodoc" + confdict = {"matlab_auto_link": "basic"} + app = make_app(srcdir=srcdir, confoverrides=confdict) + app.builder.build_all() + + content = pickle.loads((app.doctreedir / "index_root.doctree").read_bytes()) + method_section = content[0][2][1][1][0] # a bit fragile, I know + see_also_line_1 = content[0][2][1][1][1] # a bit fragile, I know + see_also_line_2 = content[0][4][1][1][0] # a bit fragile, I know + assert len(content) == 1 + assert ( + method_section.rawsource + == "BaseClass Methods:\n* :meth:`BaseClass() ` - the constructor, whose description extends\n to the next line\n* :meth:`DoBase() ` - another BaseClass method\n" + ) + assert ( + see_also_line_1.rawsource + == "See Also\n:class:`target.ClassExample`, :func:`baseFunction`, :class:`ClassExample`\n\n" + ) + assert ( + see_also_line_2.rawsource + == "See Also:\n:class:`target.submodule.ClassMeow`\n:class:`target.package.ClassBar`\n:class:`ClassMeow`\n:class:`package.ClassBar`" ) diff --git a/tests/test_autodoc_short_links.py b/tests/test_autodoc_short_links.py new file mode 100644 index 0000000..f83af7c --- /dev/null +++ b/tests/test_autodoc_short_links.py @@ -0,0 +1,299 @@ +# -*- coding: utf-8 -*- +""" + test_autodoc + ~~~~~~~~~~~~ + + Test the autodoc extension. + + :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +import pickle +import os +import sys + +import pytest + +from sphinx import addnodes +from sphinx.testing.fixtures import make_app, test_params # noqa: F811; +from sphinx.testing.path import path + + +@pytest.fixture(scope="module") +def rootdir(): + return path(os.path.dirname(__file__)).abspath() + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") +def test_target(make_app, rootdir): + srcdir = rootdir / "roots" / "test_autodoc" + confdict = {"matlab_short_links": True} + app = make_app(srcdir=srcdir, confoverrides=confdict) + app.builder.build_all() + + content = pickle.loads((app.doctreedir / "index_target.doctree").read_bytes()) + property_section = content[0][2][1][2][0] # a bit fragile, I know + method_section = content[0][2][1][2][1] # a bit fragile, I know + assert len(content) == 1 + assert ( + content[0].astext() + == "target\n\n\n\nclass ClassExample(a)\n\nBases: handle\n\nExample class\n\nClassExample Properties:\n\na - first property of ClassExample\nb - second property of ClassExample\nc - third property of ClassExample\n\nClassExample Methods:\n\nClassExample - the constructor and a reference to mymethod()\nmymethod - a method in ClassExample\n\nSee also BaseClass, baseFunction, unknownEntity.\n\nProperty Summary\n\n\n\n\n\na\n\na property\n\n\n\nb\n\na property with default value\n\n\n\nc\n\na property with multiline default value\n\nMethod Summary\n\n\n\n\n\nmymethod(b)\n\nA method in ClassExample\n\nParameters\n\nb – an input to mymethod()" + ) + assert ( + property_section.rawsource + == "ClassExample Properties:\na - first property of ClassExample\nb - second property of ClassExample\nc - third property of ClassExample" + ) + assert ( + method_section.rawsource + == "ClassExample Methods:\nClassExample - the constructor and a reference to mymethod()\nmymethod - a method in ClassExample\n" + ) + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") +def test_target_show_default_value(make_app, rootdir): + srcdir = rootdir / "roots" / "test_autodoc" + confdict = {"matlab_short_links": True, "matlab_show_property_default_value": True} + app = make_app(srcdir=srcdir, confoverrides=confdict) + app.builder.build_all() + + content = pickle.loads((app.doctreedir / "index_target.doctree").read_bytes()) + property_section = content[0][2][1][2][0] # a bit fragile, I know + method_section = content[0][2][1][2][1] # a bit fragile, I know + assert len(content) == 1 + assert ( + content[0].astext() + == "target\n\n\n\nclass ClassExample(a)\n\nBases: handle\n\nExample class\n\nClassExample Properties:\n\na - first property of ClassExample\nb - second property of ClassExample\nc - third property of ClassExample\n\nClassExample Methods:\n\nClassExample - the constructor and a reference to mymethod()\nmymethod - a method in ClassExample\n\nSee also BaseClass, baseFunction, unknownEntity.\n\nProperty Summary\n\n\n\n\n\na\n\na property\n\n\n\nb = 10\n\na property with default value\n\n\n\nc = [10; ... 30]\n\na property with multiline default value\n\nMethod Summary\n\n\n\n\n\nmymethod(b)\n\nA method in ClassExample\n\nParameters\n\nb – an input to mymethod()" + ) + assert ( + property_section.rawsource + == "ClassExample Properties:\na - first property of ClassExample\nb - second property of ClassExample\nc - third property of ClassExample" + ) + assert ( + method_section.rawsource + == "ClassExample Methods:\nClassExample - the constructor and a reference to mymethod()\nmymethod - a method in ClassExample\n" + ) + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") +def test_target_auto_link_basic(make_app, rootdir): + srcdir = rootdir / "roots" / "test_autodoc" + confdict = {"matlab_short_links": True, "matlab_auto_link": "basic"} + app = make_app(srcdir=srcdir, confoverrides=confdict) + app.builder.build_all() + + content = pickle.loads((app.doctreedir / "index_target.doctree").read_bytes()) + property_section = content[0][2][1][2][0] # a bit fragile, I know + method_section = content[0][2][1][2][1] # a bit fragile, I know + see_also_line = content[0][2][1][3] # a bit fragile, I know + assert len(content) == 1 + assert ( + property_section.rawsource + == "ClassExample Properties:\n* :attr:`a ` - first property of ClassExample\n* :attr:`b ` - second property of ClassExample\n* :attr:`c ` - third property of ClassExample" + ) + assert ( + method_section.rawsource + == "ClassExample Methods:\n* :meth:`ClassExample() ` - the constructor and a reference to mymethod()\n* :meth:`mymethod() ` - a method in ClassExample\n" + ) + assert ( + see_also_line.rawsource + == "See also :class:`BaseClass`, :func:`baseFunction`, ``unknownEntity``." + ) + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") +def test_target_auto_link_all(make_app, rootdir): + srcdir = rootdir / "roots" / "test_autodoc" + confdict = {"matlab_short_links": True, "matlab_auto_link": "all"} + app = make_app(srcdir=srcdir, confoverrides=confdict) + app.builder.build_all() + + content = pickle.loads((app.doctreedir / "index_target.doctree").read_bytes()) + property_section = content[0][2][1][2][0] # a bit fragile, I know + method_section = content[0][2][1][2][1] # a bit fragile, I know + see_also_line = content[0][2][1][3] # a bit fragile, I know + assert len(content) == 1 + assert ( + property_section.rawsource + == "ClassExample Properties:\n* :attr:`a ` - first property of :class:`ClassExample`\n* :attr:`b ` - second property of :class:`ClassExample`\n* :attr:`c ` - third property of :class:`ClassExample`" + ) + assert ( + method_section.rawsource + == "ClassExample Methods:\n* :meth:`ClassExample() ` - the constructor and a reference to :meth:`mymethod() `\n* :meth:`mymethod() ` - a method in :class:`ClassExample`\n" + ) + assert ( + see_also_line.rawsource + == "See also :class:`BaseClass`, :func:`baseFunction`, ``unknownEntity``." + ) + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") +def test_classfolder(make_app, rootdir): + srcdir = rootdir / "roots" / "test_autodoc" + confdict = {"matlab_short_links": True} + app = make_app(srcdir=srcdir, confoverrides=confdict) + app.builder.build_all() + + content = pickle.loads((app.doctreedir / "index_classfolder.doctree").read_bytes()) + assert len(content) == 1 + assert ( + content[0].astext() + == "classfolder\n\n\n\nclass ClassFolder(p)\n\nA class in a folder\n\nProperty Summary\n\n\n\n\n\np\n\na property of a class folder\n\nMethod Summary\n\n\n\n\n\nmethod_inside_classdef(a, b)\n\nMethod inside class definition" + ) + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") +def test_package(make_app, rootdir): + srcdir = rootdir / "roots" / "test_autodoc" + confdict = {"matlab_short_links": True} + app = make_app(srcdir=srcdir, confoverrides=confdict) + app.builder.build_all() + + content = pickle.loads((app.doctreedir / "index_package.doctree").read_bytes()) + docstring1 = content[0][2][1][1] # a bit fragile, I know + docstring2 = content[0][2][1][2][0][1][1][4][1][0] # a bit fragile, I know + docstring3 = content[0][2][1][2][0][2][1][2][1][0] # a bit fragile, I know + assert len(content) == 1 + assert ( + content[0].astext() + == "package\n\n\n\nclass package.ClassBar\n\nBases: handle\n\nThe Bar and Foo handler, with a doFoo() method.\n\nConstructor Summary\n\n\n\n\n\nClassBar()\n\nInitialize the bars and foos\n\nProperty Summary\n\n\n\n\n\nbars\n\nNumber of bars\n\n\n\nfoos\n\nNumber of foos, used by doBar() method\n\nMethod Summary\n\n\n\n\n\ndoBar()\n\nDoing bar, not called by ClassBar()\n\n\n\ndoFoo()\n\nDoing foo\n\n\n\n\n\n\n\npackage.funcFoo(u, t)\n\nFunction that does Foo\n\nx = package.funcFoo(u)\n[x, y] = package.funcFoo(u, t)\n\nTest for auto-linking with baseFunction and BaseClass, etc." + ) + assert docstring1.rawsource == "The Bar and Foo handler, with a doFoo() method." + assert docstring2.rawsource == "Number of foos, used by doBar() method" + assert docstring3.rawsource == "Doing bar, not called by ClassBar()" + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") +def test_package_show_default_value(make_app, rootdir): + srcdir = rootdir / "roots" / "test_autodoc" + confdict = {"matlab_short_links": True, "matlab_show_property_default_value": True} + app = make_app(srcdir=srcdir, confoverrides=confdict) + app.builder.build_all() + + content = pickle.loads((app.doctreedir / "index_package.doctree").read_bytes()) + assert len(content) == 1 + assert ( + content[0].astext() + == "package\n\n\n\nclass package.ClassBar\n\nBases: handle\n\nThe Bar and Foo handler, with a doFoo() method.\n\nConstructor Summary\n\n\n\n\n\nClassBar()\n\nInitialize the bars and foos\n\nProperty Summary\n\n\n\n\n\nbars = 'bars'\n\nNumber of bars\n\n\n\nfoos = 10\n\nNumber of foos, used by doBar() method\n\nMethod Summary\n\n\n\n\n\ndoBar()\n\nDoing bar, not called by ClassBar()\n\n\n\ndoFoo()\n\nDoing foo\n\n\n\n\n\n\n\npackage.funcFoo(u, t)\n\nFunction that does Foo\n\nx = package.funcFoo(u)\n[x, y] = package.funcFoo(u, t)\n\nTest for auto-linking with baseFunction and BaseClass, etc." + ) + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") +def test_package_auto_link_all(make_app, rootdir): + srcdir = rootdir / "roots" / "test_autodoc" + confdict = {"matlab_short_links": True, "matlab_auto_link": "all"} + app = make_app(srcdir=srcdir, confoverrides=confdict) + app.builder.build_all() + + content = pickle.loads((app.doctreedir / "index_package.doctree").read_bytes()) + docstring1 = content[0][2][1][1] # a bit fragile, I know + docstring2 = content[0][2][1][2][0][1][1][4][1][0] # a bit fragile, I know + docstring3 = content[0][2][1][2][0][2][1][2][1][0] # a bit fragile, I know + assert len(content) == 1 + assert ( + content[0].astext() + == "package\n\n\n\nclass package.ClassBar\n\nBases: handle\n\nThe Bar and Foo handler, with a doFoo() method.\n\nConstructor Summary\n\n\n\n\n\nClassBar()\n\nInitialize the bars and foos\n\nProperty Summary\n\n\n\n\n\nbars\n\nNumber of bars\n\n\n\nfoos\n\nNumber of foos, used by doBar() method\n\nMethod Summary\n\n\n\n\n\ndoBar()\n\nDoing bar, not called by ClassBar()\n\n\n\ndoFoo()\n\nDoing foo\n\n\n\n\n\n\n\npackage.funcFoo(u, t)\n\nFunction that does Foo\n\nx = package.funcFoo(u)\n[x, y] = package.funcFoo(u, t)\n\nTest for auto-linking with baseFunction() and BaseClass, etc." + ) + assert ( + docstring1.rawsource + == "The Bar and Foo handler, with a :meth:`doFoo() ` method." + ) + assert ( + docstring2.rawsource + == "Number of foos, used by :meth:`doBar() ` method" + ) + assert ( + docstring3.rawsource + == "Doing bar, not called by :meth:`ClassBar() `" + ) + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") +def test_submodule(make_app, rootdir): + srcdir = rootdir / "roots" / "test_autodoc" + confdict = {"matlab_short_links": True} + app = make_app(srcdir=srcdir, confoverrides=confdict) + app.builder.build_all() + + content = pickle.loads((app.doctreedir / "index_submodule.doctree").read_bytes()) + bases_line = content[0][2][1][0] + assert len(content) == 1 + assert ( + content[0].astext() + == "submodule\n\n\n\nclass ClassMeow\n\nBases: package.ClassBar\n\nClass which inherits from a package\n\nMethod Summary\n\n\n\n\n\nsay()\n\nSay Meow\n\n\n\nfuncMeow(input)\n\nTests a function with comments after docstring" + ) + assert bases_line.rawsource == "Bases: :class:`package.ClassBar`" + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") +def test_submodule_show_default_value(make_app, rootdir): + srcdir = rootdir / "roots" / "test_autodoc" + confdict = {"matlab_short_links": True, "matlab_show_property_default_value": True} + app = make_app(srcdir=srcdir, confoverrides=confdict) + app.builder.build_all() + + content = pickle.loads((app.doctreedir / "index_submodule.doctree").read_bytes()) + assert len(content) == 1 + assert ( + content[0].astext() + == "submodule\n\n\n\nclass ClassMeow\n\nBases: package.ClassBar\n\nClass which inherits from a package\n\nMethod Summary\n\n\n\n\n\nsay()\n\nSay Meow\n\n\n\nfuncMeow(input)\n\nTests a function with comments after docstring" + ) + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") +def test_root(make_app, rootdir): + srcdir = rootdir / "roots" / "test_autodoc" + confdict = {"matlab_short_links": True} + app = make_app(srcdir=srcdir, confoverrides=confdict) + app.builder.build_all() + + content = pickle.loads((app.doctreedir / "index_root.doctree").read_bytes()) + assert len(content) == 1 + assert ( + content[0].astext() + == "root\n\n\n\nclass BaseClass(args)\n\nA class in the very root of the directory\n\nBaseClass Methods:\n\nBaseClass - the constructor, whose description extends\n\nto the next line\n\nDoBase - another BaseClass method\n\nSee Also\n\ntarget.ClassExample, baseFunction, ClassExample\n\nConstructor Summary\n\n\n\n\n\nBaseClass(args)\n\nThe constructor\n\nMethod Summary\n\n\n\n\n\nDoBase()\n\nDo the Base thing\n\n\n\nbaseFunction(x)\n\nReturn the base of x\n\nSee Also:\n\ntarget.submodule.ClassMeow\ntarget.package.ClassBar\nClassMeow\npackage.ClassBar" + ) + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") +def test_root_show_default_value(make_app, rootdir): + srcdir = rootdir / "roots" / "test_autodoc" + confdict = {"matlab_short_links": True, "matlab_show_property_default_value": True} + app = make_app(srcdir=srcdir, confoverrides=confdict) + app.builder.build_all() + + content = pickle.loads((app.doctreedir / "index_root.doctree").read_bytes()) + assert len(content) == 1 + assert ( + content[0].astext() + == "root\n\n\n\nclass BaseClass(args)\n\nA class in the very root of the directory\n\nBaseClass Methods:\n\nBaseClass - the constructor, whose description extends\n\nto the next line\n\nDoBase - another BaseClass method\n\nSee Also\n\ntarget.ClassExample, baseFunction, ClassExample\n\nConstructor Summary\n\n\n\n\n\nBaseClass(args)\n\nThe constructor\n\nMethod Summary\n\n\n\n\n\nDoBase()\n\nDo the Base thing\n\n\n\nbaseFunction(x)\n\nReturn the base of x\n\nSee Also:\n\ntarget.submodule.ClassMeow\ntarget.package.ClassBar\nClassMeow\npackage.ClassBar" + ) + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") +def test_root_auto_link_basic(make_app, rootdir): + srcdir = rootdir / "roots" / "test_autodoc" + confdict = {"matlab_short_links": True, "matlab_auto_link": "basic"} + app = make_app(srcdir=srcdir, confoverrides=confdict) + app.builder.build_all() + + content = pickle.loads((app.doctreedir / "index_root.doctree").read_bytes()) + method_section = content[0][2][1][1][0] # a bit fragile, I know + see_also_line_1 = content[0][2][1][1][1] # a bit fragile, I know + see_also_line_2 = content[0][4][1][1][0] # a bit fragile, I know + assert len(content) == 1 + assert ( + method_section.rawsource + == "BaseClass Methods:\n* :meth:`BaseClass() ` - the constructor, whose description extends\n to the next line\n* :meth:`DoBase() ` - another BaseClass method\n" + ) + assert ( + see_also_line_1.rawsource + == "See Also\n:class:`target.ClassExample`, :func:`baseFunction`, :class:`ClassExample`\n\n" + ) + assert ( + see_also_line_2.rawsource + == "See Also:\n:class:`target.submodule.ClassMeow`\n:class:`target.package.ClassBar`\n:class:`ClassMeow`\n:class:`package.ClassBar`" + ) + + +if __name__ == "__main__": + pytest.main([__file__]) diff --git a/tests/test_duplicated_link.py b/tests/test_duplicated_link.py index e05dc7c..79b71e4 100644 --- a/tests/test_duplicated_link.py +++ b/tests/test_duplicated_link.py @@ -39,7 +39,7 @@ def test_with_prefix(make_app, rootdir): assert ( section.astext() == "NiceFiniteGroup\n\n\n\nclass +replab.NiceFiniteGroup\n\nBases: " - "replab.FiniteGroup\n\nA nice finite group is a finite group equipped " + "+replab.FiniteGroup\n\nA nice finite group is a finite group equipped " "with an injective homomorphism into a permutation group\n\nReference that triggers the error: eqv" ) diff --git a/tests/test_package_links.py b/tests/test_package_links.py index d4c895f..63105b2 100644 --- a/tests/test_package_links.py +++ b/tests/test_package_links.py @@ -37,7 +37,7 @@ def test_with_prefix(make_app, rootdir): assert ( content[5].astext() - == "class +replab.Action\n\nBases: replab.Str\n\nAn action group …\n\nMethod Summary\n\n\n\n\n\nleftAction(g, p)\n\nReturns the left action" + == "class +replab.Action\n\nBases: +replab.Str\n\nAn action group …\n\nMethod Summary\n\n\n\n\n\nleftAction(g, p)\n\nReturns the left action" )