Skip to content

Commit f7a5169

Browse files
committed
Fixes and documentation
1 parent 262942a commit f7a5169

File tree

8 files changed

+150
-34
lines changed

8 files changed

+150
-34
lines changed

mkdocs/docs/configuration.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Configuration
2+
3+
## Full Example
4+
5+
For mkdocstrings-python configuration: [see here](https://mkdocstrings.github.io/python/usage/)
6+
7+
```yaml
8+
9+
- mkdocstrings-python-generator:
10+
nav_heading:
11+
- Code Reference
12+
search:
13+
- src/my_package
14+
base: src
15+
ignore:
16+
- test_*
17+
18+
19+
edit_uri: edit/main/src/
20+
init_section_index: true
21+
prune_nav_prefix: my_package
22+
23+
# This part of the config is up to you.
24+
- mkdocstrings:
25+
handlers:
26+
python:
27+
...
28+
```
29+
30+
## Options
31+
32+
| Option Name | Description | Value Type |
33+
|----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------|
34+
| `nav_heading` | Describes where to place the generated files on the nav. Eg: if you want `Reference` > `Code Reference` then set `["Reference", "Code Reference"]` | List of Strings |
35+
| `search` | Search paths to look for python files relative to mkdocs.yaml | List of Strings |
36+
| `base` | If different from the search paths, set this to the base directory of python files similar to might be added as `PYTHONPATH`. ⚠️ Only one base may be specified | String |
37+
| `ignore` | List of [glob expressions](https://docs.python.org/3/library/glob.html#glob.glob) to ignore from the search. These are applied per file and so cannot specify directories. | List of Strings |
38+
| `edit_uri` | Override mkdocs [edit_uri](https://www.mkdocs.org/user-guide/configuration/#edit_uri) for generated files | String |
39+
| `edit_uri_template` | Override mkdocs [edit_uri_template](https://www.mkdocs.org/user-guide/configuration/#edit_uri_template) for generated files | String |
40+
| `init_section_index` | Some themes such as Material support [section indexes](https://squidfunk.github.io/mkdocs-material/setup/setting-up-navigation/#section-index-pages). If enabled `__init__.py` files with content will be used to define section index pages | Boolean |
41+
| `prune_nav_prefix` | If your package exists deep in some namespace you can remove specific parents from the nav keeping the nav cleaner and simpler. Simply name the python prefix you want to prune from the nav. Eg: `foo.bar.my_namespace_package` | String |

mkdocs/docs/index.md

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,41 @@
1-
# Mkdocstrings Python Generator
2-
Hello!
1+
# mkdocstrings-python-generator
2+
3+
mkdocstrings-python-generator is a mkdocs plugin for generating markdown pages automatically from python source code.
4+
5+
It is intended to fill a gap which is currently left to each user of mkdoctstings-python. Namely: generating markdown
6+
files for each python file.
7+
8+
_Note despite the name there is no affiliation between mkdocstrings and mkdocstrings-python-generator. Please try to
9+
determine which plugin is to blame before posting issues here or [there](https://github.com/mkdocstrings/python)._
10+
11+
12+
## Features
13+
14+
It's advantages over the [mkdocstrings-python recopy](https://mkdocstrings.github.io/recipes/#automatic-code-reference-pages) are:
15+
16+
- ✅ Easier to use (no writing code for yourself)
17+
- ✅ Well formatted nav out of the box. Package names with underscores are not title case 📦
18+
- ✅ Compatibility with both explicit nav defined in mkdocs.yaml and implicit nav with no definition in mkdocs.yaml
19+
- ✅ Supports __init__.py files as [section indexes](https://squidfunk.github.io/mkdocs-material/setup/setting-up-navigation/#section-index-pages) if supported by the theme.
20+
- ✅ Edit URI compatible with both `edit_uri` and `edit_uri_template`
21+
22+
## Minimal Example
23+
24+
See [Configuration](../configuration/) for more detail
25+
26+
27+
```yaml
28+
# Configure mkdocstrings-python
29+
- mkdocstrings:
30+
handlers:
31+
python:
32+
options:
33+
show_submodules: false
34+
35+
# Configure mkdocstrings-python-generator
36+
- mkdocstrings-python-generator:
37+
search:
38+
# Path to your source directory relative to mkdocs.yaml directory.
39+
- src/
40+
41+
```

mkdocs/mkdocs.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ site_dir: dist/mkdocs_site
66
repo_url: https://github.com/couling/MkdocstringsPythonGenerator
77
edit_uri: edit/main/mkdocs/docs/
88

9+
nav:
10+
- Welcome: index.md
11+
- Configuration: configuration.md
912

1013
# Enable the theme "Material"
1114
theme:
@@ -54,6 +57,7 @@ plugins:
5457
init_section_index: true
5558
search:
5659
- ../source/mkdocstrings_python_generator
60+
prune_nav_prefix: mkdocstrings_python_generator
5761

5862
watch:
5963
- docs

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ packages = [
1111

1212
[tool.poetry.dependencies]
1313
python = "^3.7"
14-
mkdocstrings-python = "^1.1.2"
14+
mkdocstrings-python = "^1.0.0"
1515
mkdocs = "^1.4.0"
1616

1717

@@ -46,5 +46,5 @@ mkdocstrings-python-generator = "mkdocstrings_python_generator.plugin:GeneratePy
4646

4747

4848
[tool.yapf]
49-
based_on_style="facebook"
49+
based_on_style="pep8"
5050
column_limit=120
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +0,0 @@
1-
"""hello world"""

source/mkdocstrings_python_generator/files_generator.py

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,31 @@
1515

1616

1717
class FileEntry(NamedTuple):
18+
"""Stores key information about a markdown/python file internal to mkdocs-python-genfiles"""
1819
source_file_path: Path
20+
"""Absolute path to python source file on disk"""
1921
doc_file_path: Path
22+
"""Absolute path to markdown file on disk"""
2023
module_id: ModuleId
24+
"""Abstract representation of a python module similar to it's dot-part string divided as a tuple
25+
26+
Importantly, a package __init__ file for package foo.bar will be ``("foo", "bar", "__init__")``"""
2127
file: File
28+
"""Mkdocs File object for use by mkdocs only"""
2229

2330

2431
class Source(NamedTuple):
32+
"""Source of python files"""
2533
base_path: Path
34+
"""Root of the python files
35+
36+
This is as python might see in PYTHONPATH"""
2637
dir_path: Path
38+
"""Directory to search
39+
40+
May be the same as base_path but may also be a subdirectory of it, eg: to avoid unit tests."""
2741
ignore: List[str]
42+
"""List of glob expressions for files and directories to ignore."""
2843

2944

3045
class FilesGenerator:
@@ -78,20 +93,17 @@ def generate_page(self, source_base_path: Path, source_path: Path, page_template
7893
module_name=self.module_name(module_id, full_name=False, printable=True),
7994
printable_module_id=self.module_name(module_id, full_name=True, printable=True),
8095
module_id=self.module_name(module_id, full_name=True, printable=False),
81-
)
82-
)
83-
84-
entry = FileEntry(
85-
source_file_path=source_path,
86-
doc_file_path=target_path,
87-
module_id=module_id,
88-
file=File(
89-
str(target_path.relative_to(self.base_path)),
90-
src_dir=str(self.base_path),
91-
dest_dir=self._dest_dir,
92-
use_directory_urls=self._use_directory_urls,
93-
)
94-
)
96+
))
97+
98+
entry = FileEntry(source_file_path=source_path,
99+
doc_file_path=target_path,
100+
module_id=module_id,
101+
file=File(
102+
str(target_path.relative_to(self.base_path)),
103+
src_dir=str(self.base_path),
104+
dest_dir=self._dest_dir,
105+
use_directory_urls=self._use_directory_urls,
106+
))
95107
self.generated_pages[entry.doc_file_path.absolute()] = entry
96108
return entry
97109

@@ -122,7 +134,7 @@ def file_path_for_module_id(self, module_id: ModuleId) -> Path:
122134
:return: An absolute path
123135
"""
124136
if self._init_section_index and module_id[-1] == "__init__":
125-
module_id = module_id[:-1] + ("README.md",)
137+
module_id = module_id[:-1] + ("README.md", )
126138
return Path(self.base_path, "_ref", *module_id).with_suffix(".md")
127139

128140
def module_name(self, module_id: ModuleId, full_name: bool, printable: bool) -> str:
@@ -150,7 +162,6 @@ def get_module_id(module_path: Path, base_path: Path) -> ModuleId:
150162
return module_path.relative_to(base_path).with_suffix("").parts
151163

152164

153-
154165
def discover_python_files(source_dir: Path, ignore: List[str]) -> Iterable[Path]:
155166
"""
156167
Discover python files recursively from a directory

source/mkdocstrings_python_generator/nav_util.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,16 @@ class PageEntry(NamedTuple):
2121
file: FileEntry
2222

2323

24-
def build_reference_nav(
25-
nav: Navigation, generated_pages: Dict[Path, FileEntry], section_heading: List[str], config: MkDocsConfig
26-
) -> None:
24+
def build_reference_nav(nav: Navigation, generated_pages: Dict[Path, FileEntry], section_heading: List[str],
25+
prune_prefix_package: ModuleId, config: MkDocsConfig) -> None:
2726
"""
2827
[re]build parts of the navigation which were created through dynamic generation
2928
:param nav: Navigation
3029
:param generated_pages: generated_pages from files_generator
3130
:param section_heading: List of titles indicating where to put the generated pages. Eg ``["Reference"]``.
3231
:param config: MkdocsConfig
32+
:param prune_prefix_package: Module id to remove if it exists from every pacakge in the nav.
33+
This is typically useful for pruning namespaces from the nav
3334
:return:
3435
"""
3536
generated_pages = generated_pages.copy()
@@ -45,11 +46,27 @@ def build_reference_nav(
4546
pages_to_add.sort(key=lambda item: item.file.module_id)
4647

4748
for page, entry in pages_to_add:
48-
add_page_to_nav(nav.items, page, (*section_heading, *entry.module_id))
49+
add_page_to_nav(nav.items, page, nav_path_for_module(section_heading, prune_prefix_package, entry))
4950

5051
patch_nav_refs(nav)
5152

5253

54+
def nav_path_for_module(section_heading: List[str], prune_prefix_package: ModuleId,
55+
entry: FileEntry) -> Tuple[str, ...]:
56+
"""
57+
Calculate the position of a module in the nav
58+
:param section_heading: The section heading for reference documentation
59+
:param prune_prefix_package: Configured prefix to prune from every module
60+
:param entry: The module's entry
61+
"""
62+
prefix_length = len(prune_prefix_package)
63+
if entry.module_id[:prefix_length] == prune_prefix_package:
64+
module_id = entry.module_id[prefix_length:]
65+
else:
66+
module_id = entry.module_id
67+
return *section_heading, *module_id
68+
69+
5370
def ensure_nav_section(nav_parent: List[NavItem], section_path: Union[List[str], Tuple[str, ...]]) -> Section:
5471
"""Ensure a specific nav section exists and return it
5572

source/mkdocstrings_python_generator/plugin.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class GeneratePythonDocsConfig(base.Config):
2727
search: list[str] = c.ListOfItems(c.Dir(exists=True))
2828
ignore: list[str] = c.ListOfItems(c.Type(str), default=["test", "tests", "__main__.py"])
2929
init_section_index: bool = c.Type(bool, default=False)
30-
flatten_nav_package: str = c.Type(str, default="")
30+
prune_nav_prefix: str = c.Type(str, default="")
3131

3232

3333
class GeneratePythonDocs(BasePlugin[GeneratePythonDocsConfig]):
@@ -63,10 +63,9 @@ def patch_config_nav(self, config: Config) -> None:
6363
The only solid reason for this to exist is to suppress warnings from mkdocs. Without it, mkdocs would warn that
6464
generated markdown files were not in the nav.
6565
"""
66-
dummy_nav_list = [
67-
{".".join(module.module_id): module.file.src_path}
68-
for module in self._files_generator.generated_pages.values()
69-
]
66+
dummy_nav_list = [{
67+
".".join(module.module_id): module.file.src_path
68+
} for module in self._files_generator.generated_pages.values()]
7069
config["nav"].append({"_ref": dummy_nav_list})
7170

7271
def on_pre_page(self, page: Page, *, config: MkDocsConfig, files: Files) -> Optional[Page]:
@@ -75,7 +74,13 @@ def on_pre_page(self, page: Page, *, config: MkDocsConfig, files: Files) -> Opti
7574
page.edit_url = self.make_edit_url(config, generated_file)
7675

7776
def on_nav(self, nav: Navigation, *, config: MkDocsConfig, files: Files) -> None:
78-
nav_util.build_reference_nav(nav, self._files_generator.generated_pages, self.config.nav_heading, config)
77+
nav_util.build_reference_nav(
78+
nav=nav,
79+
generated_pages=self._files_generator.generated_pages,
80+
section_heading=self.config.nav_heading,
81+
prune_prefix_package=tuple(self.config.prune_nav_prefix.split(".")),
82+
config=config,
83+
)
7984

8085
def on_post_build(self, *, config: MkDocsConfig) -> None:
8186
self._cleanup()
@@ -104,8 +109,8 @@ def make_edit_url(self, config: Config, file: files_generator.FileEntry) -> str:
104109

105110
# Make a dummy file with the python source file as src_uri instead of the .md file
106111
dummy_file = File(
107-
path=str(file.source_file_path.relative_to(Path(self.config.base))),
108-
src_dir=self.config.base,
112+
path=("/".join(file.module_id)) + ".py",
113+
src_dir="",
109114
dest_dir=dummy_config["site_dir"],
110115
use_directory_urls=dummy_config["use_directory_urls"],
111116
)
@@ -114,4 +119,4 @@ def make_edit_url(self, config: Config, file: files_generator.FileEntry) -> str:
114119
dummy_page = Page("if you can see this something broke", dummy_file, dummy_config)
115120

116121
# extract the edit_url
117-
return dummy_page.edit_url
122+
return dummy_page.edit_url

0 commit comments

Comments
 (0)