Generate PlantUML class diagrams to document your Python application.
Project description
Generate PlantUML class diagrams to document your Python application.
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:
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:
- versioned along your code with a unit-test ensuring its consistency (see the test_py2puml.py's test_assert_domain_documentation example).
You can also use the
assert_py2puml_command_argsutility from py2puml.asserts to check the output of apy2pumlcommand against a versioned file (that you can easily update):
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:
- Luc Sorel-Giffo
- Doyou Jung
- Julien Jerphanion
- Luis Fernando Villanueva Pérez
- Konstantin Zangerle
- Mieszko
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
commitlinthook - code formatting must follow the conventions enforced by the
isortandruff-formathooks - code linting should not detect code smells in your contributions, this is checked by the
ruff-checkhook
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
py2pumldepends on the reliability of the type annotations
The
pythonruntime 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
pythoninterpreter parses your.pyfiles, make sure that your executable code is guarded byif __name__ == '__main__':clauses so that it won't be executed during apy2pumlinspection
- 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
-
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):
- pyreverse, which includes a PlantUML printer since version 2.10.0
- cb109/pyplantuml
- deadbok/py-puml-tools
- caballero/genUML
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file py2puml-0.11.0.tar.gz.
File metadata
- Download URL: py2puml-0.11.0.tar.gz
- Upload date:
- Size: 20.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a08ce1988e163ebcb9e0f2fbc6e6b1251e0abd1f9cf1f2e32a32744cb85a99ea
|
|
| MD5 |
472bc4612f514f422a68bc8a8312ea68
|
|
| BLAKE2b-256 |
876551eb076157c34ca7686726c45a92da1bc2cb72e8d95c292f4cb86f0982ab
|
Provenance
The following attestation bundles were made for py2puml-0.11.0.tar.gz:
Publisher:
release.yml on lucsorel/py2puml
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
py2puml-0.11.0.tar.gz -
Subject digest:
a08ce1988e163ebcb9e0f2fbc6e6b1251e0abd1f9cf1f2e32a32744cb85a99ea - Sigstore transparency entry: 962583347
- Sigstore integration time:
-
Permalink:
lucsorel/py2puml@6109ddea0437dc12aca9e6c463f0f689c8a441d7 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/lucsorel
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6109ddea0437dc12aca9e6c463f0f689c8a441d7 -
Trigger Event:
workflow_run
-
Statement type:
File details
Details for the file py2puml-0.11.0-py3-none-any.whl.
File metadata
- Download URL: py2puml-0.11.0-py3-none-any.whl
- Upload date:
- Size: 27.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a64689d149db7d6d3253a719be4050a2b4e2802758481bd0ca1da2400f8d9f49
|
|
| MD5 |
52a8f86d46c76e4d7ca012904539ebed
|
|
| BLAKE2b-256 |
1e8715d07aabfbd2ff1e6836aa7871c675a1b709d85a1cb46342622efefebdf0
|
Provenance
The following attestation bundles were made for py2puml-0.11.0-py3-none-any.whl:
Publisher:
release.yml on lucsorel/py2puml
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
py2puml-0.11.0-py3-none-any.whl -
Subject digest:
a64689d149db7d6d3253a719be4050a2b4e2802758481bd0ca1da2400f8d9f49 - Sigstore transparency entry: 962583350
- Sigstore integration time:
-
Permalink:
lucsorel/py2puml@6109ddea0437dc12aca9e6c463f0f689c8a441d7 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/lucsorel
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6109ddea0437dc12aca9e6c463f0f689c8a441d7 -
Trigger Event:
workflow_run
-
Statement type: