Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions autoapi/_mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,9 +312,10 @@ def _wrapped_prepare(value):
self._use_implicit_namespace = (
self.app.config.autoapi_python_use_implicit_namespaces
)
self._follow_symlinks = self.app.config.autoapi_follow_symlinks

@staticmethod
def find_files(patterns, dirs, ignore):
def find_files(patterns, dirs, ignore, follow_symlinks: bool):
if not ignore:
ignore = []

Expand All @@ -324,7 +325,9 @@ def find_files(patterns, dirs, ignore):
pattern_regexes.append((pattern, regex))

for _dir in dirs: # iterate autoapi_dirs
for root, subdirectories, filenames in os.walk(_dir):
for root, subdirectories, filenames in os.walk(
_dir, followlinks=follow_symlinks
):
# skip directories if needed
for sub_dir in subdirectories.copy():
# iterate copy as we adapt subdirectories during loop
Expand All @@ -342,6 +345,10 @@ def find_files(patterns, dirs, ignore):
for pattern, pattern_re in pattern_regexes:
for filename in fnmatch.filter(filenames, pattern):
match = re.match(pattern_re, filename)
if match is None:
raise ValueError(
f'Could not match pattern "{pattern_re}" to filename "{filename}".'
)
norm_name = match.groups()
if norm_name in seen:
continue
Expand Down Expand Up @@ -429,7 +436,12 @@ def _find_files(self, patterns, dirs, ignore):
):
dir_root = os.path.abspath(os.path.join(dir_, os.pardir))

for path in self.find_files(patterns=patterns, dirs=[dir_], ignore=ignore):
for path in self.find_files(
patterns=patterns,
dirs=[dir_],
ignore=ignore,
follow_symlinks=self._follow_symlinks,
):
yield dir_root, path

def load(self, patterns, dirs, ignore=None):
Expand Down
1 change: 1 addition & 0 deletions autoapi/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ def setup(app):
app.add_config_value("autoapi_add_toctree_entry", True, "html")
app.add_config_value("autoapi_template_dir", None, "html")
app.add_config_value("autoapi_include_summaries", None, "html")
app.add_config_value("autoapi_follow_symlinks", False, "html")
app.add_config_value("autoapi_python_use_implicit_namespaces", False, "html")
app.add_config_value("autoapi_python_class_content", "class", "html")
app.add_config_value("autoapi_generate_api_docs", True, "html")
Expand Down
1 change: 1 addition & 0 deletions docs/changes/+7eaa921c.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Handling case where match returns None to fix mypy unit test.
1 change: 1 addition & 0 deletions docs/changes/+d379912c.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Adding `autoapi_follow_symlinks`, which allows api to traverse into symlinked directories when generating the API documentation.
8 changes: 8 additions & 0 deletions docs/reference/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@ Customisation Options

A list of patterns to ignore when finding directories and files to document.

.. confval:: autoapi_follow_symlinks

Default: ``False``

If `True`, then follow symlinks when walking ``autoapi_dirs`` to generate the API
documentation. If `False`, then do not follow symlinks when when walking
``autoapi_dirs`` to generate the API documentation.

.. confval:: autoapi_root

Default: ``autoapi``
Expand Down
37 changes: 34 additions & 3 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,21 @@ def sphinx_build(test_dir, confoverrides=None):


class LanguageIntegrationTests:
def _run_test(self, test_dir, test_file, test_string):
with sphinx_build(test_dir):
def _run_test(
self,
test_dir,
test_file,
test_string,
confoverrides={},
test_missing: bool = False,
):
with sphinx_build(test_dir, confoverrides=confoverrides):
with open(test_file, encoding="utf8") as fin:
text = fin.read().strip()
assert test_string in text
if test_missing:
assert test_string not in text
else:
assert test_string in text


class TestIntegration(LanguageIntegrationTests):
Expand All @@ -54,3 +64,24 @@ def test_toctree_domain_insertion(self):
self._run_test(
"toctreeexample", "_build/text/index.txt", '* "example_function()"'
)

def test_symlink(self):
"""
Test that the example_function_2 gets added to the TOC Tree when running with symlinks
and that it does not get added when running without them.
"""
# Without symlinks, should not contain example_function_2
self._run_test(
"toctreeexample",
"_build/text/index.txt",
'* "example_function_2()"',
test_missing=True,
)

# With symlinks, should contain example_function_2
self._run_test(
"toctreeexample",
"_build/text/index.txt",
'* "example_function_2()"',
confoverrides={"autoapi_follow_symlinks": True},
)
1 change: 1 addition & 0 deletions tests/toctreeexample/example/example_2
8 changes: 8 additions & 0 deletions tests/toctreeexample/example_2/example_2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
__author__ = "leake"

import math


def example_function_2(x):
"""Compute the square of x and return it."""
return x**2