Skip to content

Commit 115dd54

Browse files
committed
Initial commit
0 parents  commit 115dd54

5 files changed

Lines changed: 202 additions & 0 deletions

File tree

README.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# mkdocstrings-python-accessors
2+
3+
Python handler for [mkdocstrings](https://github.com/mkdocstrings/mkdocstrings)
4+
supporting documentation of accessors.
5+
Takes inspiration from [sphinx-autosummary-accessors](https://github.com/xarray-contrib/sphinx-autosummary-accessors).
6+
7+
This package extends [mkdocstrings-python](https://github.com/mkdocstrings/python)
8+
(well, technically, [mkdocstrings-python-xref](https://github.com/analog-garage/mkdocstrings-python-xref))
9+
to support more desirable documentation of accessors.
10+
11+
The accessors pattern is normally something like the following.
12+
Let's take [`pandas`](https://pandas.pydata.org/docs/development/extending.html#registering-custom-accessors).
13+
It is possible to register custom accessors, so you can do operations via that namespace.
14+
For example, `pd.DataFrame.custom_namespace.operation()`.
15+
When implemented, this is usually done via some sub-class,
16+
which is then registered with the upstream package (in this case pandas).
17+
The pattern normally looks something like the below
18+
19+
```python
20+
@pd.register_accessor("custom_namespace")
21+
class CustomNamespaceAccessor:
22+
def __init__(self, pandas_obj):
23+
self._obj = pandas_obj
24+
25+
def operation(self):
26+
# Normally you do a more elaborate operation than this,
27+
# but you get the idea.
28+
return self._obj * 2
29+
```
30+
31+
When you come to document this,
32+
you normally get just the documentation for the class `CustomNamespaceAccessor`.
33+
For example, if you include the following in your docs.
34+
35+
```md
36+
::: CustomNamespaceAccessor
37+
handler: python
38+
```
39+
40+
Then you will get documentation for `CustomNamespaceAccessor`.
41+
42+
This package introduces the following options.
43+
44+
```md
45+
::: CustomNamespaceAccessor
46+
handler: python_accessors
47+
options:
48+
namespace: "pd.DataFrame.custom_namespace"
49+
```
50+
51+
With this, the documentation will be transformed.
52+
Instead of creating docs for `CustomNamespaceAccessor`,
53+
you will instead get docs for `pd.DataFrame.custom_namespace`.
54+
55+
The configuration we have found works best is the below,
56+
but you can use all the normal options that can be passed to
57+
[mkdocstrings-python](https://github.com/mkdocstrings/python)
58+
and [mkdocstrings-python-xref](https://github.com/analog-garage/mkdocstrings-python-xref)
59+
to modify the appearance as you wish.
60+
61+
```md
62+
::: CustomNamespaceAccessor
63+
handler: python_accessors
64+
options:
65+
namespace: "pd.DataFrame.custom_namespace"
66+
show_root_full_path: false
67+
show_root_heading: true
68+
```

pyproject.toml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
[project]
2+
name = "mkdocstrings-python-accessors"
3+
version = "0.3.4a1"
4+
description = "A Python accessor handler for mkdocstrings."
5+
authors = [{name = "Zebedee Nicholls", email = "zebedee.nicholls@climate-resource.com"}]
6+
license = {text = "BSD-3-Clause"}
7+
readme = "README.md"
8+
requires-python = ">=3.9"
9+
keywords = []
10+
classifiers = [
11+
# TODO: pull some of these across into our copier template
12+
"Development Status :: 4 - Beta",
13+
"Intended Audience :: Developers",
14+
"Programming Language :: Python",
15+
"Programming Language :: Python :: 3",
16+
"Programming Language :: Python :: 3 :: Only",
17+
"Programming Language :: Python :: 3.9",
18+
"Programming Language :: Python :: 3.10",
19+
"Programming Language :: Python :: 3.11",
20+
"Programming Language :: Python :: 3.12",
21+
"Programming Language :: Python :: 3.13",
22+
"Topic :: Documentation",
23+
"Topic :: Software Development",
24+
"Topic :: Utilities",
25+
"Typing :: Typed",
26+
]
27+
dependencies = [
28+
"mkdocstrings-python-xref>=1.0",
29+
]
30+
31+
# TODO: pull into our copier template
32+
[project.urls]
33+
Homepage = "https://climate-resource.github.io/mkdocstrings-python-accessors"
34+
Documentation = "https://climate-resource.github.io/mkdocstrings-python-accessors"
35+
Changelog = "https://climate-resource.github.io/mkdocstrings-python-accessors/changelog"
36+
Repository = "https://github.com/climate-resource/mkdocstrings-python-accessors"
37+
Issues = "https://github.com/climate-resource/mkdocstrings-python-accessors/issues"
38+
Discussions = "https://github.com/climate-resource/mkdocstrings-python-accessors/discussions"
39+
40+
[build-system]
41+
requires = ["pdm-backend"]
42+
build-backend = "pdm.backend"
43+
44+
[tool.pdm.build]
45+
package-dir = "src"
46+
includes = ["src/mkdocstrings_handlers"]
47+
# Consider adding something like this to our copier template.
48+
# Include as much as possible in the source distribution, to help redistributors.
49+
excludes = ["**/.pytest_cache"]
50+
source-includes = [
51+
# "docs",
52+
# "scripts",
53+
# "tests",
54+
# "mkdocs.yml",
55+
# "*.md",
56+
"LICENCE",
57+
]
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"""Python handler for documenting accessors with mkdocstrings"""
2+
3+
from .handler import PythonAccessorsHandler
4+
5+
__all__ = ["get_handler"]
6+
7+
get_handler = PythonAccessorsHandler
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
"""
2+
Implementation of python_accessors handler
3+
"""
4+
5+
from __future__ import annotations
6+
7+
from collections.abc import Mapping
8+
from typing import Any
9+
10+
import griffe
11+
from mkdocstrings.loggers import get_logger
12+
from mkdocstrings_handlers.python_xref.handler import PythonRelXRefHandler
13+
14+
__all__ = ["PythonAccessorsHandler"]
15+
16+
logger = get_logger(__name__)
17+
18+
19+
class PythonAccessorsHandler(PythonRelXRefHandler):
20+
"""
21+
Extended version of mkdocstrings PythonRelXRefHandler handler
22+
23+
Converts accessors into the namespace of the user's choosing.
24+
25+
Could also be done with the standard Python handler I expect,
26+
but that's not what the project I'm working on needs.
27+
"""
28+
29+
def render(self, data: griffe.Object, config: Mapping[str, Any]) -> str:
30+
"""
31+
Render the docs
32+
33+
Parameters
34+
----------
35+
data
36+
Data to render
37+
38+
config
39+
Configuration
40+
41+
Returns
42+
-------
43+
:
44+
Rendered docs
45+
"""
46+
if not isinstance(data, griffe.Class):
47+
raise NotImplementedError(data)
48+
49+
try:
50+
namespace = config["namespace"]
51+
except KeyError:
52+
msg = f"Please specify the namespace to use with {data.name}. {data.path=}"
53+
raise KeyError(msg)
54+
55+
# Set overall name
56+
data.name = namespace
57+
58+
# Then update names for individual members
59+
member_keys = list(data.members.keys())
60+
for name in member_keys:
61+
if name.startswith("_"):
62+
# Don't document hidden methods etc.
63+
# Could make this configuration instead
64+
# if real customisation was needed.
65+
data.del_member(name)
66+
continue
67+
68+
data.members[name].name = f"{namespace}.{name}"
69+
70+
return super().render(data, config)

src/mkdocstrings_handlers/python_accessors/py.typed

Whitespace-only changes.

0 commit comments

Comments
 (0)