Skip to content

Commit adba27c

Browse files
committed
feat: configure python path based on command line and package options
Implements: #77
1 parent 20adc55 commit adba27c

File tree

16 files changed

+250
-13
lines changed

16 files changed

+250
-13
lines changed

CHANGELOG.adoc

+9
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@ The format is based on https://keepachangelog.com/en/1.0.0/[Keep a Changelog],
1818
and this project adheres to https://semver.org/spec/v2.0.0.html[Semantic Versioning].
1919

2020

21+
== Unreleased
22+
23+
=== Added
24+
25+
* Python path management to easily include external python scripts. By default the directory
26+
containing the document being generated is added to the python path when the file is generated.
27+
The python path can be overridden on the command line. For packages python scripts can be
28+
included in a separate directory and specified in the contents file.
29+
2130
== 0.8.6 (4 Sep 2022)
2231

2332
=== Added

asciidoxy/config.py

+8
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ class Configuration(argparse.Namespace):
6363
image_dir: Optional[Path] = None
6464
spec_file: Optional[Path] = None
6565
version_file: Optional[Path] = None
66+
python_dir: Optional[Path] = None
6667

6768
build_dir: Path
6869
destination_dir: Path
@@ -106,6 +107,13 @@ def parse_args(argv):
106107
help="Directory containing images to include. If no image directory is"
107108
" specified, only images in the `images` directory next to the input file"
108109
" can be included.")
110+
input_group.add_argument("--python-dir",
111+
metavar="PYTHON_DIR",
112+
type=PathArgument(existing_dir=True),
113+
help="Directory containg Python code to include in the documentation."
114+
" If no explicit Python directory is specified, either `BASE_DIR`"
115+
" or the directory containing the input file is used to find Python"
116+
" code")
109117

110118
external_data_group = parser.add_argument_group(title="Loading external data")
111119
external_data_group.add_argument("-s",

asciidoxy/document.py

+8
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ class Package:
4242
adoc_image_dir: Directory containing images to include in the documentation.
4343
adoc_root_doc: Entry point document for the package. To be linked to if no specific file
4444
in the package is mentioned.
45+
python_dir: Directory containing Python code to include in the documentation. This
46+
directory will be added to the Python path for documents in this
47+
package
4548
scoped: True if this is a new-style, scoped package.
4649
copy_adoc_src_dir: True if the content of `adoc_src_dir` should be copied to the working
4750
directory.
@@ -54,6 +57,7 @@ class Package:
5457
adoc_src_dir: Optional[Path] = None
5558
adoc_image_dir: Optional[Path] = None
5659
adoc_root_doc: Optional[Path] = None
60+
python_dir: Optional[Path] = None
5761
scoped: bool = False
5862
copy_adoc_src_dir: bool = True
5963

@@ -82,6 +86,10 @@ def load_from_toml(self, pkg_root: Path, data: Mapping[str, Any]) -> None:
8286
if self.adoc_src_dir is not None:
8387
self.adoc_root_doc = path_from_toml(adoc, "root_doc", self.adoc_src_dir)
8488

89+
python = data.get("python", None)
90+
if python is not None:
91+
self.python_dir = path_from_toml(python, "dir", pkg_root)
92+
8593
self.scoped = True
8694

8795

asciidoxy/generator/cache.py

+21-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"""Cache implementation for Mako templates supporting package resources."""
1515

1616
from pathlib import Path
17-
from typing import Optional
17+
from typing import List, Optional
1818

1919
from mako.exceptions import TopLevelLookupException
2020
from mako.lookup import TemplateLookup
@@ -41,10 +41,28 @@ def __init__(self, cache_name: str, cache_dir: Optional[Path] = None, *args, **k
4141
super().__init__(*args, **kwargs)
4242

4343

44+
def _generate_python_path_code(python_paths: Optional[List[Path]]) -> List[str]:
45+
if python_paths:
46+
imports = ["import sys"]
47+
imports.extend(f"sys.path.insert(1, \"{p}\")" for p in python_paths)
48+
imports.append("del sys")
49+
return imports
50+
51+
return []
52+
53+
4454
class DocumentCache(BaseCache):
4555
"""Cache for input documents."""
46-
def __init__(self, cache_dir: Optional[Path] = None, *args, **kwargs):
47-
super().__init__("documents", cache_dir, *args, **kwargs)
56+
def __init__(self,
57+
cache_dir: Optional[Path] = None,
58+
python_paths: Optional[List[Path]] = None,
59+
*args,
60+
**kwargs):
61+
super().__init__("documents",
62+
cache_dir,
63+
imports=_generate_python_path_code(python_paths),
64+
*args,
65+
**kwargs)
4866

4967
def get_document(self, document: Document) -> Template:
5068
return self.get_template(str(document.original_file))

asciidoxy/generator/context.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ def __init__(self, reference: ApiReference, package_manager: PackageManager, doc
181181
self.document_stack = [document]
182182

183183
self.templates = TemplateCache(config.template_dir, config.cache_dir)
184-
self.document_cache = DocumentCache(config.cache_dir)
184+
self.document_cache = DocumentCache(config.cache_dir, package_manager.python_paths())
185185

186186
self.config = config
187187

asciidoxy/packaging/collect.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -447,9 +447,8 @@ def _combine_dict(a: Dict[Any, Any], b: Dict[Any, Any]):
447447
return a
448448

449449

450-
def specs_from_file(
451-
spec_file: Union[PurePath, str],
452-
version_file: Optional[Union[PurePath, str]] = None) -> Sequence[PackageSpec]:
450+
def specs_from_file(spec_file: Union[PurePath, str],
451+
version_file: Optional[Union[PurePath, str]] = None) -> Sequence[PackageSpec]:
453452
"""Load package specifications from a file.
454453
455454
Optionally a version CSV file is used to provide package versions.

asciidoxy/packaging/manager.py

+19-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import logging
1818
import shutil
1919
from pathlib import Path
20-
from typing import Dict, Optional, Tuple, Union
20+
from typing import Dict, List, Optional, Tuple, Union
2121

2222
from tqdm import tqdm
2323

@@ -81,7 +81,8 @@ def image_work_dir(self) -> Path:
8181
def set_input_files(self,
8282
in_file: Path,
8383
include_dir: Optional[Path] = None,
84-
image_dir: Optional[Path] = None) -> None:
84+
image_dir: Optional[Path] = None,
85+
python_dir: Optional[Path] = None) -> None:
8586
"""Set the input files to collect.
8687
8788
Args:
@@ -91,6 +92,9 @@ def set_input_files(self,
9192
image_dir: Directory containing iamges to include from AsciiDoc files. If `None` and
9293
directory named `images` is present next to the `in_file`, that
9394
directory is used for images. Otherwise, no images are copied.
95+
python_dir: Directory containing Python code to include in the documentation. This
96+
directory will be added to the Python path for the input file and any
97+
document in the include_dir.
9498
"""
9599
pkg = Package(Package.INPUT_PACKAGE_NAME)
96100
pkg.adoc_src_dir = include_dir
@@ -106,6 +110,11 @@ def set_input_files(self,
106110
pkg.copy_adoc_src_dir = False
107111
pkg.adoc_src_dir = in_file.parent
108112

113+
if python_dir:
114+
pkg.python_dir = python_dir
115+
else:
116+
pkg.python_dir = pkg.adoc_src_dir
117+
109118
self.packages[Package.INPUT_PACKAGE_NAME] = pkg
110119

111120
def collect(self,
@@ -298,6 +307,14 @@ def make_document(self,
298307

299308
return doc
300309

310+
def python_paths(self) -> List[Path]:
311+
"""Get the paths containing python code to be included in the generated documents.
312+
313+
Returns:
314+
A list of python paths. Can be empty.
315+
"""
316+
return [pkg.python_dir for pkg in self.packages.values() if pkg.python_dir]
317+
301318
def _warning_or_error(self, error: Exception):
302319
if self.warnings_are_errors:
303320
raise error

documentation/getting-started/using-python.adoc

+14
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,20 @@ This results in:
105105
The factorial of 5 is ${math.factorial(5)}.
106106
====
107107

108+
=== Python path handling
109+
110+
For `import` statements to work, the imported modules need to be on the python path. For libraries
111+
included with python or installed with `pip` this is automatic. To include your own python modules,
112+
AsciiDoxy needs to know where to look.
113+
114+
By default, the directory containing the input AsciiDoc file is used to look for additional python
115+
modules. If needed, you can specify a different path to be included using the `--python-dir`
116+
command line option.
117+
118+
When using ${cross_document_ref("packages.adoc", link_text="packages")}, you can specify a
119+
${cross_document_ref("../reference/packages.adoc", anchor="_python_section",
120+
link_text="subdirectory in the package to load python code from")}.
121+
108122
== Custom functions
109123

110124
Module-level blocks can also be used to define custom functions to reuse in your AsciiDoc files.

documentation/reference/packages.adoc

+9
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ following sections:
195195
`package`:: Metadata for the package.
196196
`reference`:: The API reference information contained in the package.
197197
`asciidoc`:: AsciiDoc and other files to be included in AsciiDoc generation.
198+
`python`:: Python scripts to include in AsciiDoc generation.
198199

199200
=== Package section
200201

@@ -226,3 +227,11 @@ AsciiDoc files. These files are copied to the image directory used for all Ascii
226227
`root_doc`:: (Optional) Document to include by default for the package. Used if not specific file
227228
in the package is mentioned.
228229

230+
=== Python section
231+
232+
The `python` section describes files in the package that are to be included when generating the
233+
AsciiDoc files. The subdirectory containing the python files is automatically added to the python
234+
path for all files.
235+
236+
`dir`:: (Required) Subdirectory inside the package containing python code to be included in
237+
generation.

pyproject.toml

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ markers = [
2424
"slow: marks tests as slow (deselect with '-m \"not slow\"')",
2525
]
2626
junit_family = "legacy"
27+
asyncio_mode = "auto"
2728

2829
[tool.isort]
2930
profile = "hug"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
= Include some python code from another file
2+
3+
<%
4+
import include_python
5+
%>
6+
${include_python.test()}

tests/data/adoc/include_python.py

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
def test():
2+
return "This is included python code"

tests/unit/generator/test_generator.py

+21-3
Original file line numberDiff line numberDiff line change
@@ -1486,10 +1486,8 @@ def test_process_adoc_custom_templates(warnings_are_errors, single_and_multipage
14861486

14871487

14881488
def test_process_adoc_access_config(warnings_are_errors, single_and_multipage, adoc_data,
1489-
api_reference, package_manager, update_expected_results,
1490-
doxygen_version, tmp_path, default_config):
1489+
api_reference, package_manager, tmp_path, default_config):
14911490
input_file = adoc_data / "access_config.input.adoc"
1492-
adoc_data_expected_result_file(input_file, single_and_multipage, doxygen_version)
14931491

14941492
package_manager.set_input_files(input_file)
14951493
doc = package_manager.prepare_work_directory(input_file)
@@ -1507,6 +1505,26 @@ def test_process_adoc_access_config(warnings_are_errors, single_and_multipage, a
15071505
assert f"Build dir: {default_config.build_dir}" in content
15081506

15091507

1508+
def test_process_adoc_include_python(warnings_are_errors, single_and_multipage, adoc_data,
1509+
api_reference, package_manager, tmp_path, default_config):
1510+
input_file = adoc_data / "include_python.input.adoc"
1511+
1512+
package_manager.set_input_files(input_file)
1513+
doc = package_manager.prepare_work_directory(input_file)
1514+
1515+
progress_mock = ProgressMock()
1516+
default_config.warnings_are_errors = warnings_are_errors
1517+
output_doc = process_adoc(doc,
1518+
api_reference,
1519+
package_manager,
1520+
config=default_config,
1521+
progress=progress_mock)[0]
1522+
assert output_doc.work_file.is_file()
1523+
1524+
content = output_doc.work_file.read_text()
1525+
assert "This is included python code" in content
1526+
1527+
15101528
@pytest.mark.parametrize("api_reference_set", [("cpp/default", "cpp/consumer")])
15111529
@pytest.mark.parametrize(
15121530
"test_file_name",

0 commit comments

Comments
 (0)