Automatic documentation from sources, for MkDocs.
—
Object inventory system for managing and cross-referencing documentation objects within and across projects. The inventory system enables linking between different documentation sets and provides compatibility with Sphinx inventories.
Dictionary-based storage for collected documentation objects with Sphinx inventory compatibility.
class Inventory(dict):
"""Dictionary-based inventory of collected objects."""
def __init__(
self,
items: list[InventoryItem] | None = None,
project: str = "project",
version: str = "0.0.0"
) -> None:
"""
Initialize inventory.
Args:
items: Initial list of inventory items
project: Project name
version: Project version
"""
def register(
self,
name: str,
domain: str,
role: str,
uri: str,
priority: int = 1,
dispname: str | None = None
) -> None:
"""
Register new inventory item.
Args:
name: Item name/identifier
domain: Item domain (e.g., "py", "js")
role: Item role (e.g., "function", "class")
uri: Item URI/link
priority: Item priority for sorting
dispname: Display name (defaults to name)
"""
def format_sphinx(self) -> bytes:
"""
Format inventory as Sphinx inventory file.
Returns:
Sphinx inventory file content as bytes
"""
@classmethod
def parse_sphinx(
cls,
in_file: BinaryIO,
*,
domain_filter: Collection[str] = ()
) -> Inventory:
"""
Parse Sphinx inventory file.
Args:
in_file: Binary file containing Sphinx inventory
domain_filter: Only include items from these domains
Returns:
Inventory instance with parsed items
"""
project: str
"""Project name."""
version: str
"""Project version."""Usage Examples:
Creating and populating an inventory:
from mkdocstrings import Inventory
# Create new inventory
inventory = Inventory(project="my-project", version="1.0.0")
# Register items
inventory.register(
name="MyClass",
domain="py",
role="class",
uri="#MyClass",
dispname="my_module.MyClass"
)
inventory.register(
name="my_function",
domain="py",
role="function",
uri="#my_function",
priority=2
)
# Save as Sphinx inventory
with open("objects.inv", "wb") as f:
f.write(inventory.format_sphinx())Loading external inventory:
# Load Sphinx inventory from file
with open("external_objects.inv", "rb") as f:
external_inventory = Inventory.parse_sphinx(f, domain_filter=["py"])
# Merge with local inventory
local_inventory.update(external_inventory)Individual item within an inventory, representing a single documentation object.
class InventoryItem:
"""Individual item within an inventory."""
def __init__(
self,
name: str,
domain: str,
role: str,
uri: str,
priority: int = 1,
dispname: str | None = None
) -> None:
"""
Initialize inventory item.
Args:
name: Item name/identifier
domain: Item domain
role: Item role
uri: Item URI/link
priority: Item priority
dispname: Display name
"""
sphinx_item_regex: re.Pattern = re.compile(r"^(.+?)\s+(\S+):(\S+)\s+(-?\d+)\s+(\S+)\s*(.*)$")
"""Regex to parse a Sphinx v2 inventory line."""
def format_sphinx(self) -> str:
"""
Format this item as a Sphinx inventory line.
Returns:
A line formatted for an objects.inv file
"""
@classmethod
def parse_sphinx(
cls,
line: str,
*,
return_none: bool = False
) -> InventoryItem | None:
"""
Parse a line from a Sphinx v2 inventory file and return an InventoryItem from it.
Args:
line: Sphinx inventory line to parse
return_none: Return None instead of raising ValueError on parse failure
Returns:
InventoryItem instance or None if parsing fails and return_none is True
"""
name: str
"""The item name."""
domain: str
"""The item domain."""
role: str
"""The item role."""
uri: str
"""The item URI."""
priority: int
"""The item priority."""
dispname: str
"""The item display name."""Usage Examples:
Creating inventory items:
from mkdocstrings import InventoryItem
# Create item for a Python class
class_item = InventoryItem(
name="MyClass",
domain="py",
role="class",
uri="api.html#MyClass",
priority=1,
dispname="my_module.MyClass"
)
# Create item for a function
func_item = InventoryItem(
name="my_function",
domain="py",
role="function",
uri="api.html#my_function",
priority=2
)
# Format for Sphinx
sphinx_line = class_item.format_sphinx()
print(sphinx_line) # "MyClass py:class 1 api.html#MyClass my_module.MyClass"Parsing Sphinx inventory lines:
sphinx_line = "my_function py:function 1 api.html#my_function -"
item = InventoryItem.parse_sphinx(sphinx_line)
print(item.name) # "my_function"
print(item.domain) # "py"
print(item.role) # "function"
print(item.uri) # "api.html#my_function"Handlers automatically register items in the inventory during rendering:
class MyHandler(BaseHandler):
enable_inventory = True # Enable inventory for this handler
domain = "mylang" # Set domain for items
def render(self, data: CollectorItem, options: HandlerOptions, *, locale: str | None = None) -> str:
# Register in inventory
self.handlers.inventory.register(
name=data.name,
domain=self.domain,
role=data.kind, # "class", "function", etc.
uri=f"#{data.anchor}",
dispname=data.full_name
)
# Render normally
return self.render_template(data, options)Inventories enable cross-project documentation linking:
# In handler initialization
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Load external inventories
self.load_external_inventories()
def load_external_inventories(self):
"""Load inventories from external projects."""
for url, config in self.get_inventory_urls():
try:
with urllib.request.urlopen(f"{url}/objects.inv") as f:
external_inv = Inventory.parse_sphinx(f)
self.handlers.inventory.update(external_inv)
except Exception as e:
self.logger.warning(f"Failed to load inventory from {url}: {e}")The inventory system works with autorefs to provide automatic cross-referencing:
# In template or handler
def resolve_reference(self, identifier: str) -> str | None:
"""Resolve identifier to URL using inventory."""
if identifier in self.handlers.inventory:
item = self.handlers.inventory[identifier]
return item.uri
return None
# Usage in templates
"""
{% if resolve_reference("MyClass") %}
<a href="{{ resolve_reference('MyClass') }}">MyClass</a>
{% else %}
MyClass
{% endif %}
"""mkdocstrings uses the same inventory format as Sphinx:
# Inventory file structure
# Project: my-project
# Version: 1.0.0
MyClass py:class 1 api.html#MyClass my_module.MyClass
my_function py:function 1 api.html#my_function -
MY_CONSTANT py:data 1 api.html#MY_CONSTANT -Common domain mappings:
py (classes: py:class, functions: py:function, modules: py:module)js (functions: js:function, classes: js:class)ts (interfaces: ts:interface, types: ts:type)cpp (classes: cpp:class, functions: cpp:function)Standard role types across domains:
class - Classes and class-like objectsfunction - Functions and methodsmethod - Class methods specificallyattribute - Class/instance attributesmodule - Modules and namespacesconstant - Constants and enumstype - Type definitionsinterface - Interfaces (TypeScript, etc.)Enable inventory in plugin configuration:
plugins:
- mkdocstrings:
enable_inventory: true
handlers:
python:
options:
# Handler optionsConfigure inventory behavior per handler:
class MyHandler(BaseHandler):
enable_inventory = True # Enable for this handler
domain = "mylang" # Set domain
def get_inventory_urls(self) -> list[tuple[str, dict[str, Any]]]:
"""Return external inventory URLs to load."""
return [
("https://docs.python.org/3/", {}),
("https://numpy.org/doc/stable/", {"domain_filter": ["py"]}),
]Load inventories from external projects:
# In handler configuration
inventory_urls = [
"https://docs.python.org/3/objects.inv",
"https://docs.mkdocs.org/objects.inv",
"https://python-markdown.github.io/objects.inv"
]
# Automatic loading during handler initialization
for url in inventory_urls:
try:
external_inventory = self.load_inventory_from_url(url)
self.handlers.inventory.update(external_inventory)
except Exception as e:
self.logger.warning(f"Failed to load {url}: {e}")This enables seamless cross-referencing between projects and integration with the broader documentation ecosystem.
Install with Tessl CLI
npx tessl i tessl/pypi-mkdocstrings