Metadata-Version: 2.5
Name: autodox
Version: 0.2.0
Summary: Tool for generating documentation automatically from code annotations, types, and docstrings.
Project-URL: Homepage, https://github.com/k98kurz/autodox
Project-URL: Repository, https://github.com/k98kurz/autodox
Project-URL: Bug Tracker, https://github.com/k98kurz/autodox/issues
Author-email: k98kurz <k98kurz@gmail.com>
License-Expression: ISC
License-File: license
Classifier: Development Status :: 3 - Alpha
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: 3.15
Classifier: Topic :: Documentation
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# Autodox

Tool for generating documentation automatically from code annotations, types,
and docstrings.

## Status

Primary development is complete. Further development will be tracked in the
[issues](https://github.com/k98kurz/autodox/issues). Historical changes can be
found in the [changelog](https://github.com/k98kurz/autodox/blob/master/changelog.md).

Full API documentation generated by autodox can be found
[here](https://github.com/k98kurz/autodox/blob/master/docs/dox.md).

## Usage

Note that autodox imports the module being documented, so it must be
installed in the same Python environment as the target module. If autodox
is installed at the system or user level while the target module lives in
a virtual environment, the autodox CLI will not be able to import it.
Install autodox inside the target module's environment (e.g. activate the
virtualenv and run `pip install autodox` there) before documenting it.

### Installation

```bash
pip install autodox
```

Note that Protocols do not support `__init__` methods before Python 3.11. To use
`__init__` method signatures for Protocols in Python 3.10, use the
`typing.Protocol` backport from the 3rd-party `typing_extensions` package.
Otherwise, the `__init__` method signature will be overridden with an empty one
named `_no_init_or_replace_init` and will be ignored by this tool.

### Document a module from CLI

Use the following to document a module with default configuration:

```bash
autodox module_name [options] > target_file.md
```

By default, the output ends with a footer identifying the autodox and Python
versions; pass `--no-footer` to omit it.

Run `autodox --help` for the full, up-to-date list. The output can be
configured with the following options:
- `--exclude-name=name` to exclude a specific part of the module by name (or pass
  a comma-separated list; repeatable)
- `--exclude-type=type` to exclude any module parts of the given type (or pass a
  comma-separated list; repeatable)
- `--package=module_name` to scope a relative import
- `--header-level=number` to increase the hashtag count in headers by `number`
- `--function-format=format` - can be one of 'header', 'paragraph', or 'list'
- `--method-format=format` - can be one of 'header', 'paragraph', or 'list'
- `--value-format=format` - can be one of 'header', 'paragraph', or 'list'
- `--line-length=number` - number of chars per line in paragraphs and list items
- `--include-private` to include things prefaced with `_`
- `--include-dunder` to include things prefaced with `__`
- `--include-submodules` to include submodules
- `--document-submodules` to run the module documentation for submodules
- `--no-backtick-names` to omit backticks around names (annotations, values,
  classes, modules, and aliases)
- `--no-backtick-types` to omit backticks around annotation and value types
- `--no-backtick-signatures` to omit backticks around function and method
  signatures
- `--no-backticks` to disable all backticks at once, forcing each granular flag
  off
- `--no-clean-type-aliases` to keep module qualifiers in type alias
  definitions (e.g. `typing.Callable[...]`)
- `--verbose-defaults` to render complex default values in full instead of
  abbreviating them to `...`
- `--sort-default-dicts` to sort dictionary defaults by rendered key instead of
  preserving insertion order
- `--sort-classes` to sort classes by name instead of preserving module
  namespace order
- `--no-footer` to omit the generated-by footer
- `--debug` to increase the level of debug statements printed to stderr (starts
  at 0; levels 1-3 are available, and debug output does not contaminate redirected
  markdown)
- `-v`, `--version` to print the version and exit

For experimentation and to learn how the options work, try running the following:

```bash
autodox autodox [options]
```

### Programmatic access

The autodox package can also be used by importing and running the desired
documentation function(s). The following are included.

- `dox_a_module(module: ModuleType, options: dict | None = None) -> str` produces
  docs for a module
- `dox_a_value(value: Any, options: dict | None = None) -> str` produces docs for
  a value
- `dox_a_function(function: Callable, options: dict | None = None) -> str`
  produces docs for a function
- `dox_a_class(cls: type, options: dict | None = None) -> str` produces docs for
  a class
- `dox_footer() -> str` produces a markdown footer with the autodox and Python
  versions

The valid options for the `dox_a_{thing}` functions are described below. The
`dox_a_*` functions do not append the footer automatically. Additionally, there
is a system for setting up hooks that interact with the doc generation process
to change the inputs or outputs, and that will be described below the options
for the four `dox_a_{thing}` functions.

#### `dox_a_module(module: ModuleType, options: dict | None = None) -> str`

Produces docs for a module. Valid options are the following:

- `exclude_names: list[str]` - names to exclude from docs
- `exclude_types: list[str]` - types to exclude from docs
- `header_level: int` - number of additional hashtags to add to headers
- `function_format: str` - can be one of 'header', 'paragraph', or 'list'
- `method_format: str` - can be one of 'header', 'paragraph', or 'list'
- `value_format: str` - can be one of 'header', 'paragraph', or 'list'
- `include_private: bool` - if True, includes things with names prefaced by `_`
- `include_dunder: bool` - if True, includes things with names prefaced by `__`
- `include_submodules: bool` - if True, notes will be made about any additional
modules encountered when analyzing the specified module
- `document_submodules: bool` - if True, `dox_a_module` will be called
recursively on any additional modules encountered when analyzing the specified
module
- `line_length: int` - number of chars per line in paragraphs and list items
- `backtick_names: bool` - if False, omits backticks from names (annotations,
values, classes, modules, and aliases; defaults to True)
- `backtick_types: bool` - if False, omits backticks from annotation and value
types (defaults to True)
- `backtick_signatures: bool` - if False, omits backticks from function and
method signatures (defaults to True)
- `backticks: bool` - if False, disables all backticks regardless of the
granular options (defaults to True); setting it True does not re-enable a
category disabled by a granular option
- `clean_type_aliases: bool` - if False, keeps module qualifiers in rendered
  type alias definitions (defaults to True)
- `verbose_defaults: bool` - if True, renders complex default values in full
  instead of abbreviating them to `...` (defaults to False)
- `sort_default_dicts: bool` - if True, sorts dictionary defaults by rendered
  key instead of preserving insertion order (defaults to False)
- `sort_classes: bool` - if True, sorts classes by name instead of preserving
  module namespace order; alias entries sort by their binding name (defaults
  to False)

Module-level type aliases (for example `Handler = Callable[[Message], Message]`)
are rendered in the Values section as `Handler = Callable[[Message], Message]`
rather than by their private runtime class name. PEP 585 builtin generics, PEP
604 unions, `typing` aliases, PEP 695 `type` aliases, and `typing.NewType` are
supported. `typing.Optional` and `typing.Union` are rendered as PEP 604 unions,
and `typing.List`, `typing.Dict`, `typing.Set`, `typing.FrozenSet`,
`typing.Tuple`, and `typing.Type` are rendered as their builtin generic
equivalents on every supported Python version, e.g. `Optional[List[int]]`
becomes `list[int] | None`. Unions place `None` last, and runtime forward
references render in quotes, e.g. `Optional['Node']` becomes `'Node' | None`.
By default, `typing.`, `builtins.`, `collections.abc.`, and the documented
module's own qualifier are stripped from the rendered definition; string
literals inside a definition are left untouched. `NewType` values render as
`Name = NewType('Name', Supertype)`.

When the same class or function is bound to more than one name in a module
(e.g. `UsageError = DependencyError`), `dox_a_module` documents the target once
and emits an alias entry (`### \`UsageError = DependencyError\``) for each
additional binding. Alias detection is automatic; no configuration is required,
and a class-body method alias such as `bar = foo` is handled the same way. An
alias is only detected when the target is bound under its `__name__` in the
same namespace: aliases of builtins, inherited methods, renamed imports, and
name-mangled targets are documented under their `__name__` instead. With
`sort_classes`, alias entries sort alongside other classes by their binding
name.

#### `dox_a_value(value: Any, options: dict | None = None) -> str`

Produces docs for a value. Valid options are the following:

- `header_level: int` - number of additional hashtags to add to headers
- `format: str` - format can be one of 'header', 'paragraph', or 'list'
- `name: str` - overrides the name used in the docs (the module binding name)
- `line_length: int` - number of chars per line in paragraphs and list items
- `backtick_names: bool` - if False, omits backticks from the value name
  (defaults to True)
- `backtick_types: bool` - if False, omits backticks from the value type
  (defaults to True)
- `backticks: bool` - if False, disables all backticks (defaults to True; see
  `dox_a_module` for how it interacts with the granular options)
- `clean_type_aliases: bool` - if False, keeps module qualifiers in rendered
  type alias definitions (defaults to True)
- `module_name: str` - module qualifier to strip from type alias definitions
  (usually set automatically by `dox_a_module`)

Runtime type aliases and `NewType` values are rendered as `name = definition`;
other values keep the `name: type` form.

#### `dox_a_function(function: Callable, options: dict | None = None) -> str`

Produces docs for a function. Valid options are the following:

- `header_level: int` - number of additional hashtags to add to headers
- `format: str` - format can be one of 'header', 'paragraph', or 'list'
- `prepend: str` - text prepended to the signature (used for decorators)
- `line_length: int` - number of chars per line in paragraphs and list items
- `backtick_signatures: bool` - if False, omits backticks from the signature
  (defaults to True)
- `backticks: bool` - if False, disables all backticks (defaults to True; see
  `dox_a_module` for how it interacts with the granular options)
- `verbose_defaults: bool` - if True, renders complex default values in full
  instead of abbreviating them to `...` (defaults to False)
- `sort_default_dicts: bool` - if True, sorts dictionary defaults by rendered
  key instead of preserving insertion order (defaults to False)

By default, callable defaults, object instances, nested containers, and
containers with more than three items are rendered as `...`. Scalar and small
container defaults keep their literal form; type aliases render as their
definitions (PEP 695 `type` aliases as their names) using the same canonical
PEP 604/builtin-generic form, and `NewType` values render as their names.
`typing.Any` renders as `Any` on all supported Python versions. Bytes-like
defaults are also elided when they exceed 40 bytes, including when nested
inside a small container. With `verbose_defaults`, callables and callable
instances render as
`Callable[..., ReturnType]`, non-callable object instances use a stable
`TypeName(...)` placeholder, and containers are expanded recursively.
Combined `Flag` defaults render as fully qualified member unions, such as
`Mode.READ | Mode.WRITE`.

Dictionary defaults preserve insertion order by default. With
`sort_default_dicts`, dictionary items are sorted recursively by their rendered
key and value. Verbose output never includes memory addresses; enable
`sort_default_dicts` as well when dictionary insertion order may vary between
runs.

#### `dox_a_class(cls: type, options: dict | None = None) -> str`

Produces docs for a class. Valid options are the following:

- `exclude_names: list[str]` - names to exclude from docs
- `header_level: int` - number of additional hashtags to add to headers
- `include_private: bool` - if True, includes things with names prefaced by '_'
- `include_dunder: bool` - if True, includes things with names prefaced by '__'
- `method_format: str` - can be one of 'header', 'paragraph', or 'list'
- `line_length: int` - number of chars per line in paragraphs and list items
- `backtick_names: bool` - if False, omits backticks from the class header and
  annotation names (defaults to True)
- `backtick_types: bool` - if False, omits backticks from annotation types
  (defaults to True)
- `backtick_signatures: bool` - if False, omits backticks from method
  signatures (defaults to True)
- `backticks: bool` - if False, disables all backticks (defaults to True; see
  `dox_a_module` for how it interacts with the granular options)
- `verbose_defaults: bool` - if True, renders complex default values in full
  instead of abbreviating them to `...` (defaults to False)
- `sort_default_dicts: bool` - if True, sorts method dictionary defaults by
  rendered key instead of preserving insertion order (defaults to False)

Enum subclasses also get a `Members` section immediately after the docstring.
Members are listed in declaration order as `NAME = value`; aliases render as
`ALIAS = TARGET`, and `Flag`/`IntFlag` composites render as
`NAME = A | B = value` when all bits map to visible single-bit members.
`exclude_names`, `include_private`, and `include_dunder` apply to member names.
Complex values follow the same rendering rules as parameter defaults, so they
are abbreviated to `...` unless `verbose_defaults` is enabled.

#### Hooks

There are eleven events where custom functionality can be run, specified in the
`Event` enum:
- `AFTER_HEADER`
- `AFTER_PARAGRAPH`
- `AFTER_LIST`
- `BEFORE_VALUE`
- `AFTER_VALUE`
- `BEFORE_FUNCTION`
- `AFTER_FUNCTION`
- `BEFORE_CLASS`
- `AFTER_CLASS`
- `BEFORE_MODULE`
- `AFTER_MODULE`

Handlers for the `BEFORE_` events can be set using the `set_before_handler`
function. These handlers will receive the item to be documented and a dict
containing any options passed into the `dox_a_{thing}` function, and they should
return a tuple containing those two arguments after any modifications are made
to them. Example:

```python
from autodox import Event, set_before_handler
from typing import Callable


def handle_before_function(function: Callable, options: dict):
    # set an option
    options['header_level'] = 2
    return (function, options)

set_before_handler(Event.BEFORE_FUNCTION, handle_before_function)
```

Handlers for the `AFTER_` events can be set using the `set_after_handler`
function. These handlers will receive the str doc after the `dox_a_{thing}`
function has completed executing, and they should return that document after any
modifications are made. Example:

```python
from autodox import Event, set_after_handler


def handle_after_function(doc: str):
    # do some string manipulation
    return doc + 'But really, why would you call this function anyway?\n\n'

set_after_handler(Event.AFTER_FUNCTION, handle_after_function)
```

Multiple handlers can be set for each event, and they will be executed in order,
passing the output from the first as input to the second, etc. Example:

```python
from autodox import Event, set_after_handler


# add 'hello world' to the end of each list item in two steps
def hello(doc: str):
    return doc + ' hello'

def world(doc: str):
    return doc + ' world'

set_after_handler(Event.AFTER_LIST, hello)
set_after_handler(Event.AFTER_LIST, world)
```


## Testing

The test suite uses the standard library `unittest` module and covers the hook
system and the generated documentation, including signature rendering, class
annotations, protocols, and alias detection.

To run it, clone the repository and run the following:

```bash
# setup
python -m venv venv
source venv/bin/activate
pip install -e .

# then
python tests/test_hooks.py
python tests/test_doxing.py
# or
python -m unittest discover tests/
```

## ISC License

Copyright (c) 2026 k98kurz

Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyleft notice and this permission notice appear in
all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
