Skip to main content
Python logo PlantUML logo

Python to PlantUML

Generate PlantUML class diagrams to document your Python application.

pre-commit.ci status

py2puml uses pre-commit hooks and pre-commit.ci Continuous Integration to enforce commit messages, code formatting and linting for quality and consistency sake. See the code conventions section if you would like to contribute to the project.

Installation

py2puml is a command-line interface (CLI) documentation tool that can be installed as a dependency of your project, or installed globally on your system, or even run in an isolated way.

Install as a project dependency

Install py2puml from PyPI with your favorite installation tool:

pip install py2puml

uv add py2puml

poetry add py2puml

pipenv install py2puml

Run as an isolated binary

Uv can download and install py2puml on your system and run it in an isolated way (no influence on your other Python tools):

uvx --isolated py2puml --help

Usage

The primary purpose of py2puml is to document domain models as PlantUML class diagrams, it focuses on data structures attributes and relationships (inheritance and composition/association). Documenting methods may come later.

Once py2puml is installed, an eponymous CLI is available in your environment shell.

Generate documentation in the standard output

Give py2puml a package (a folder) or a module (a .py file) to inspect and it will generate the PlantUML diagram either in the standard output or in a file path:

To document the domain model used by py2puml to model data structures:

# at the root of the py2puml project
py2puml --path py2puml/domain
# short-flag version:
py2puml -p py2puml/domain

This outputs the following PlantUML content:

@startuml py2puml.domain
!pragma useIntermediatePackages false

class py2puml.domain.umlitem.UmlItem {
  name: str
  fqn: str
}
class py2puml.domain.umlrelation.UmlRelation {
  source_fqn: str
  target_fqn: str
  type: RelType
}
class py2puml.domain.inspection.Inspection {
  items_by_fqn: Any
  relations: Any
}
class py2puml.domain.umlclass.UmlAttribute {
  name: str
  type: str
  static: bool
}
class py2puml.domain.umlclass.UmlClass {
  attributes: List[UmlAttribute]
  is_abstract: bool
}
class py2puml.domain.umlenum.Member {
  name: str
  value: str
}
class py2puml.domain.umlenum.UmlEnum {
  members: List[Member]
}
enum py2puml.domain.umlrelation.RelType {
  COMPOSITION: * {static}
  INHERITANCE: <| {static}
}
py2puml.domain.umlrelation.UmlRelation *-- py2puml.domain.umlrelation.RelType
py2puml.domain.umlclass.UmlClass *-- py2puml.domain.umlclass.UmlAttribute
py2puml.domain.umlitem.UmlItem <|-- py2puml.domain.umlclass.UmlClass
py2puml.domain.umlenum.UmlEnum *-- py2puml.domain.umlenum.Member
py2puml.domain.umlitem.UmlItem <|-- py2puml.domain.umlenum.UmlEnum
footer Generated by //py2puml//
@enduml

Using PlantUML (online or with IDE extensions) renders this content as follows:

py2puml domain UML Diagram

Pipe the diagram in a local PlantUML server

Pipe the result of the CLI with a PlantUML server for instantaneous documentation (rendered by ImageMagick):

# runs a local PlantUML server from a docker container:
docker run -d --rm -p 1234:8080 --name plantumlserver plantuml/plantuml-server:jetty

py2puml -p py2puml/domain | curl -X POST --data-binary @- http://localhost:1234/svg/ --output - | display

# stop the container when you don't need it anymore, restart it later
docker stop plantumlserver
docker start plantumlserver

Generate documentation in a file

py2puml --path py2puml/domain --output-file py2puml-domain.puml
# short-flag version:
py2puml -p py2puml/domain -o py2puml-domain.puml

Generate documentation for a specific module

py2puml --path py2puml/domain/umlitem.py
@startuml py2puml.domain.umlitem
!pragma useIntermediatePackages false

class py2puml.domain.umlitem.UmlItem {
  name: str
  fqn: str
}
footer Generated by //py2puml//
@enduml

Generate documentation for a project with a src folder

Use the --path flag to indicate the path to the root namespace of the project and the --namespace flag to indicate that the "src" part should be ignored:

py2puml -p src/project -n project

Note: py2puml won't handle automatically the "src" part if it is in the middle of the path to inspect.

Use py2puml outside the namespace root

By default, py2puml derives the Python namespace from the given path, assuming the command is called from the root namespace:

py2puml --path py2puml/domain
# is equivalent to:
py2puml --path py2puml/domain --namespace py2puml.domain
# short-flag version
py2puml -p py2puml/domain -n py2puml.domain

But sometimes your shell may be positionned out of the namespace folder, or within it. In such cases, it is necessary to specify the namespace of the domain to inspect so that py2puml can inspect it properly and follow the imports in the inspected package or modules:

# from your home folder:
# - for a package
py2puml --path repositories/py2puml/py2puml/domain --namespace py2puml.domain
# -> py2puml will move down its "inspection working directory" to repositories/py2puml

# - for a module
py2puml -p repositories/py2puml/py2puml/domain/item.py -n py2puml.domain.umlitem

# from a sub-package of the project to inspect (in py2puml/domain)
# - for a package
py2puml --path . --namespace py2puml.domain
# -> py2puml will move its "inspection working directory" up 2 folders in order to be at the root namespace
# - for a module
py2puml -p umlitem.py -n py2puml.domain.umlitem

Help commands

For a full overview of the CLI, run:

# documents the available flags and their description
py2puml --help

# displays the installed version
py2puml --version
# -> py2puml 0.11.0

Python API

To programatically create the diagram of the py2puml domain classes, import the py2puml function in your script:

from py2puml.py2puml import py2puml

if __name__ == '__main__':
    # 1. outputs the PlantUML content in the terminal
    print(''.join(py2puml('py2puml/domain', 'py2puml.domain')))

    # 2. or writes the PlantUML content in a file
    with open('py2puml/py2puml.domain.puml', 'w', encoding='utf8') as puml_file:
        puml_file.writelines(py2puml('py2puml/domain', 'py2puml.domain'))

How it works

py2puml internally uses code inspection (also called reflexion in other programming languages) and abstract tree parsing to retrieve relevant information.

Features

From a given path corresponding to a folder containing Python code, py2puml inspects each Python module and generates a PlantUML diagram from the definitions of various data structures using:

  • inspection and type annotations to detect:

    • static class attributes and dataclass fields
    • fields of namedtuples
    • members of enumerations
    • composition and inheritance relationships. The detection of composition relationships relies on type annotations only, assigned values or expressions are never evaluated to prevent unwanted side-effects
  • parsing abstract syntax trees to detect the instance attributes defined in __init__ constructors

py2puml outputs diagrams in PlantUML syntax, which can be:

from py2puml.asserts import assert_py2puml_command_args

def test_assert_domain_documentation():
    assert_py2puml_command_args('-p py2puml/domain', DOCUMENTATION_PATH / 'py2puml.domain.puml')
    # temporarily add the `overwrite_expected_output=True` argument to update the file containing the expected contents
    assert_py2puml_command_args('-p py2puml/domain', DOCUMENTATION_PATH / 'py2puml.domain.puml', overwrite_expected_output=True)
  • generated and hosted along other code documentation (better option: generated documentation should not be versioned with the codebase)

If you like tools related with PlantUML, you may also be interested in this lucsorel/plantuml-file-loader project: a webpack loader which converts PlantUML files into images during the webpack processing (useful to include PlantUML diagrams in your slides with RevealJS or RemarkJS).

Changelog and versions

See CHANGELOG.md.

Licence

Unless stated otherwise all works are licensed under the MIT license, a copy of which is included here.

Contributions

I'm thankful to all the people who have contributed to this project:

Pull requests and code conventions

Pull-requests are welcome and will be processed on a best-effort basis.

Pull requests must follow the guidelines enforced by pre-commit hooks (see the .pre-commit-config.yaml configuration file):

  • commit messages must follow the the conventional-commit rules enforced by the commitlint hook
  • code formatting must follow the conventions enforced by the isort and ruff-format hooks
  • code linting should not detect code smells in your contributions, this is checked by the ruff-check hook

Please also follow the contributing guide to ease your contribution.

Pre-commit hooks

Activate the git hooks

Set the git hooks (pre-commit and commit-msg types):

uv run pre-commit install

Run the hooks locally

Before committing, you can check your changes with:

# all hooks on the staged files
uv run pre-commit run

# all hooks on all files
uv run pre-commit run --all-files

# a specific hook on all files
uv run pre-commit run ruff-format --all-files

Code formatting

This project uses isort and ruff-format to format the code. The guidelines are expressed in their respective sections in the pyproject.toml file.

Static analysis and best practices

This project uses the ruff-check linter, which is configured in its section in the pyproject.toml file.

Commit messages

Please, follow the conventional commit guidelines for commit messages. When merging your pull-request, the new version of the project will be derived from the commit messages.

Tests

Add automated tests on your contributions, which can be run with the vollowing commands:

# directly with poetry
uv run pytest -v

# in a virtual environment
python3 -m pytest -v

# a specific test suite file or a given test
uv run pytest -v tests/py2puml/test_cli_controller.py
uv run pytest -v -k test_controller_stdout_and_in_file

Code coverage (with missed branch statements):

uv run pytest -v --cov=src/py2puml --cov-branch --cov-report term-missing --cov-fail-under 93

Current limitations

  • regarding inspection

    • type hinting is optional when writing Python code and discarded when it is executed, as mentionned in the typing official documentation. The quality of the diagram output by py2puml depends on the reliability of the type annotations

    The python runtime does not enforce function and variable type annotations. They can be used by third party tools such as type checkers, IDEs, linters, etc.

    • inspection implies that the python interpreter parses your .py files, make sure that your executable code is guarded by if __name__ == '__main__': clauses so that it won't be executed during a py2puml inspection
  • regarding the detection of instance attributes with AST parsing:

    • only constructors are visited, attributes assigned in other functions won't be documented
    • attribute types are inferred from type annotations:
      • of the attribute itself
      • of the variable assigned to the attribute: a signature parameter or a locale variable
      • to avoid side-effects, no code is executed nor interpreted

Alternatives

If py2puml does not meet your needs (suggestions and pull-requests are welcome), you can have a look at these projects which follow other approaches (AST, linting, modeling):

Metadata

Release files for py2puml 0.11.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for py2puml 0.11.0
File Size Uploaded Provenance
py2puml-0.11.0.tar.gz 20.0 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for py2puml 0.11.0
File Interpreter ABI Platform
py2puml-0.11.0-py3-none-any.whl Python 3 none any Details

Total release size: 47.1 kB

Release files / py2puml-0.11.0.tar.gz

Download URL py2puml-0.11.0.tar.gz
Size 20.0 kB
Tags Source
SHA-256 checksum
How to use checksums
a08ce1988e163ebcb9e0f2fbc6e6b1251e0abd1f9cf1f2e32a32744cb85a99ea
BLAKE2b-256 checksum
How to use checksums
876551eb076157c34ca7686726c45a92da1bc2cb72e8d95c292f4cb86f0982ab
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Feb 18, 2026.

Transparency log

Release files / py2puml-0.11.0-py3-none-any.whl

Download URL py2puml-0.11.0-py3-none-any.whl
Size 27.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a64689d149db7d6d3253a719be4050a2b4e2802758481bd0ca1da2400f8d9f49
BLAKE2b-256 checksum
How to use checksums
1e8715d07aabfbd2ff1e6836aa7871c675a1b709d85a1cb46342622efefebdf0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Feb 18, 2026.

Transparency log

Security for py2puml 0.11.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for py2puml-0.11.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Feb 18, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page