Skip to content

Commit 3174add

Browse files
jjerphanlucsorel
andauthored
feature: Add simple CLI (#9)
0.4.0 Add simple CLI, consistency tests, documentation Co-authored-by: Luc Sorel-Giffo <[email protected]>
1 parent b39dee9 commit 3174add

File tree

9 files changed

+178
-40
lines changed

9 files changed

+178
-40
lines changed

README.md

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ If you like tools around PlantUML, you may also be interested in this [lucsorel/
3838

3939
# Install
4040

41-
Install from the github repository:
41+
Install from PyPI:
4242

4343
* with `pip`:
4444

@@ -60,22 +60,16 @@ pipenv install py2puml
6060

6161
# Usage
6262

63-
For example, to create the diagram of the classes used by `py2puml`:
64-
65-
* import the py2puml function in your script (see [py2puml/example.py](py2puml/example.py)):
63+
## CLI
6664

67-
```python
68-
from py2puml.py2puml import py2puml
65+
Once `py2puml` is installed at the system level, an eponymous command is available in your environment shell.
6966

70-
# outputs the PlantUML content in the terminal
71-
print(''.join(py2puml('py2puml/domain', 'py2puml.domain')))
72-
73-
# writes the PlantUML content in a file
74-
with open('py2puml/domain.puml', 'w') as puml_file:
75-
puml_file.writelines(py2puml('py2puml/domain', 'py2puml.domain'))
67+
For example, to create the diagram of the classes used by `py2puml`, one can use:
68+
```sh
69+
py2puml py2puml/domain py2puml.domain
7670
```
7771

78-
* running it (`python3 -m py2puml.example`) will output the PlantUML diagram in the terminal and write it in a file
72+
This will output the following PlantUML script:
7973

8074
```plantuml
8175
@startuml
@@ -114,9 +108,54 @@ py2puml.domain.umlrelation.UmlRelation *-- py2puml.domain.umlrelation.RelType
114108
@enduml
115109
```
116110

117-
Which renders like this:
111+
Using PlantUML, this script renders this diagram:
112+
113+
![py2puml UML Diagram](https://www.plantuml.com/plantuml/png/ZP91IyGm48Nl-HKvBsmF7iiUTbaA1jnMQZs9I7OxIY19Qp8H5jV_xZIse5GsFULrQBvvCozRZz9XC9gTjFIUz-URdhwojZDIsOnah6UFHkyGdJe61Fx9EBVIGCuzEj9uxaVzbSRi1n4HSWBwdDyfZq-_cpnVOIa4Cw04dJCph--jJPa16qns07C4Dxl_8NM0HG1oKD0P2IR2fa5-qCC8mu__t7UW9QhEPZNeXhON6VlgS5yzY4PKPSvNL13bRL6BPbVkYvnlBdC_SnvvgaSTcRuBxWGlSIbJMjAz0SRItm17BzGc6TzglLxqL5WYlCs5GAbkBB5_CdCzuoKk4Y6pPJkFNj9niotObkhi6m00)
114+
115+
For a full overview of the CLI, run:
116+
117+
```sh
118+
py2puml --help
119+
```
120+
121+
The CLI can also be launched as a python module:
122+
123+
```sh
124+
python -m py2puml py2puml/domain py2puml.domain
125+
```
126+
127+
Pipe the result of the CLI with a PlantUML server for instantaneous documentation (rendered by ImageMagick):
128+
129+
```sh
130+
# runs a local PlantUML server from a docker container:
131+
docker run -d -p 1234:8080 --name plantumlserver plantuml/plantuml-server:jetty
132+
133+
py2puml py2puml/domain py2puml.domain | curl -X POST --data-binary @- http://localhost:1234/svg/ --output - | display
134+
135+
# stops the container when you don't need it anymore, restarts it later, removes it
136+
docker stop plantumlserver
137+
docker start plantumlserver
138+
docker rm plantumlserver
139+
```
140+
141+
## Python API
142+
143+
For example, to create the diagram of the classes used by `py2puml`:
144+
145+
* import the py2puml function in your script (see [py2puml/example.py](py2puml/example.py)):
146+
147+
```python
148+
from py2puml.py2puml import py2puml
149+
150+
# outputs the PlantUML content in the terminal
151+
print(''.join(py2puml('py2puml/domain', 'py2puml.domain')))
152+
153+
# writes the PlantUML content in a file
154+
with open('py2puml/domain.puml', 'w') as puml_file:
155+
puml_file.writelines(py2puml('py2puml/domain', 'py2puml.domain'))
156+
```
157+
* running it (`python3 -m py2puml.example`) will output the previous PlantUML diagram in the terminal and write it in a file.
118158

119-
![](https://www.plantuml.com/plantuml/png/ZP91IyGm48Nl-HKvBsmF7iiUTbaA1jnMQZs9I7OxIY19Qp8H5jV_xZIse5GsFULrQBvvCozRZz9XC9gTjFIUz-URdhwojZDIsOnah6UFHkyGdJe61Fx9EBVIGCuzEj9uxaVzbSRi1n4HSWBwdDyfZq-_cpnVOIa4Cw04dJCph--jJPa16qns07C4Dxl_8NM0HG1oKD0P2IR2fa5-qCC8mu__t7UW9QhEPZNeXhON6VlgS5yzY4PKPSvNL13bRL6BPbVkYvnlBdC_SnvvgaSTcRuBxWGlSIbJMjAz0SRItm17BzGc6TzglLxqL5WYlCs5GAbkBB5_CdCzuoKk4Y6pPJkFNj9niotObkhi6m00)
120159

121160
# Tests
122161

@@ -130,6 +169,7 @@ python3 -m pytest -v
130169

131170
# Changelog
132171

172+
* `0.4.0`: add a simple CLI
133173
* `0.3.1`: inspect sub-folders recursively
134174
* `0.3.0`: handle classes derived from namedtuples (attribute types are `any`)
135175
* `0.2.0`: handle inheritance relationships and enums. Unit tested
@@ -143,6 +183,7 @@ Unless stated otherwise all works are licensed under the [MIT license](http://sp
143183

144184
* [Luc Sorel-Giffo](https://github.com/lucsorel)
145185
* [Doyou Jung](https://github.com/doyou89)
186+
* [Julien Jerphanion](https://github.com/jjerphan)
146187

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

py2puml/__init__.py

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +0,0 @@
1-
from os.path import dirname, realpath, join
2-
from re import compile, Match
3-
4-
# exports the version read from the pyproject.toml file
5-
try:
6-
PARENT_DIR = dirname(dirname(realpath(__file__)))
7-
VERSION_PATTERN = compile('^version = "([^"]+)"$')
8-
with open(join(PARENT_DIR, 'pyproject.toml')) as pyproject:
9-
version_match: Match = next((
10-
match for match in (VERSION_PATTERN.search(line) for line in pyproject.readlines())
11-
if match is not None
12-
), None)
13-
14-
__version__ = version_match.group(1)
15-
except:
16-
__version__ = None
17-

py2puml/__main__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from py2puml.cli import run
2+
3+
if __name__ == '__main__':
4+
run()

py2puml/cli.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
from argparse import ArgumentParser
5+
6+
from py2puml.py2puml import py2puml
7+
8+
9+
def run():
10+
argparser = ArgumentParser(description='Generate Plantuml diagrams to document your python code')
11+
12+
argparser.add_argument('-v', '--version', action='version', version='py2puml 0.4.0')
13+
argparser.add_argument('path', metavar='path', type=str, help='the path of the domain')
14+
argparser.add_argument('module', metavar='module', type=str, help='the module of the domain', default=None)
15+
16+
args = argparser.parse_args()
17+
print(''.join(py2puml(args.path, args.module)))

pyproject.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
[tool.poetry]
22
name = "py2puml"
3-
version = "0.3.1"
4-
description = "Generate Plantuml diagrams to document your python code "
3+
version = "0.4.0"
4+
description = "Generate Plantuml diagrams to document your python code"
55
keywords = ["class diagram", "PlantUML", "documentation"]
66
readme = "README.md"
77
repository = "https://github.com/lucsorel/py2puml"
88
authors = ["Luc Sorel-Giffo"]
99
maintainers = ["Luc Sorel-Giffo"]
1010
license = "MIT"
1111

12+
[tool.poetry.scripts]
13+
py2puml = 'py2puml.cli:run'
14+
1215
[tool.poetry.dependencies]
1316
python = "^3.7"
1417

tests/__init__.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from os.path import dirname, realpath, join
2+
from re import compile
3+
from itertools import takewhile
4+
5+
# exports the version and the project description read from the pyproject.toml file
6+
__version__ = None
7+
__description__ = None
8+
9+
PARENT_DIR = dirname(dirname(realpath(__file__)))
10+
VERSION_PATTERN = compile('^version = "([^"]+)"$')
11+
DESCRIPTION_PATTERN = compile('^description = "([^"]+)"$')
12+
13+
def get_from_line_and_pattern(line: str, pattern) -> str:
14+
pattern_match = pattern.search(line)
15+
if pattern_match is None:
16+
return None
17+
else:
18+
return pattern_match.group(1)
19+
20+
with open(join(PARENT_DIR, 'pyproject.toml')) as pyproject:
21+
for line in takewhile(lambda _: __version__ is None or __description__ is None, pyproject):
22+
__version__ = __version__ if __version__ is not None else get_from_line_and_pattern(line, VERSION_PATTERN)
23+
__description__ = __description__ if __description__ is not None else get_from_line_and_pattern(line, DESCRIPTION_PATTERN)

tests/py2puml/test__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from tests import __version__, __description__
2+
3+
# Ensures the library version is modified in the pyproject.toml file when upgrading it (pull request)
4+
def test_version():
5+
assert __version__ == '0.4.0'
6+
7+
# Description also output in the CLI
8+
def test_description():
9+
assert __description__ == 'Generate Plantuml diagrams to document your python code'

tests/py2puml/test_cli.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
from typing import List
2+
3+
from pytest import mark
4+
from subprocess import run, PIPE
5+
6+
from py2puml import py2puml
7+
from tests import __version__, __description__
8+
9+
@mark.parametrize(
10+
'entrypoint', [
11+
['py2puml'],
12+
['python', '-m', 'py2puml']
13+
]
14+
)
15+
def test_cli_consistency_with_the_default_configuration(entrypoint: List[str]):
16+
command = entrypoint + ['py2puml/domain', 'py2puml.domain']
17+
cli_stdout = run(command,
18+
stdout=PIPE, stderr=PIPE,
19+
text=True, check=True
20+
).stdout
21+
22+
puml_content = py2puml.py2puml('py2puml/domain', 'py2puml.domain')
23+
24+
assert ''.join(puml_content).strip() == cli_stdout.strip()
25+
26+
@mark.parametrize(
27+
'version_command', [
28+
['-v'],
29+
['--version']
30+
]
31+
)
32+
def test_cli_version(version_command: List[str]):
33+
'''
34+
Ensures the consistency between the CLI version and the project version set in pyproject.toml
35+
which is not included when the CLI is installed system-wise
36+
'''
37+
command = ['py2puml'] + version_command
38+
cli_version = run(command,
39+
stdout=PIPE, stderr=PIPE,
40+
text=True, check=True
41+
).stdout
42+
43+
assert cli_version == f'py2puml {__version__}\n'
44+
45+
@mark.parametrize(
46+
'help_command', [
47+
['-h'],
48+
['--help']
49+
]
50+
)
51+
def test_cli_help(help_command: List[str]):
52+
'''
53+
Ensures the consistency between the CLI help and the project description set in pyproject.toml
54+
which is not included when the CLI is installed system-wise
55+
'''
56+
command = ['py2puml'] + help_command
57+
help_text = run(command,
58+
stdout=PIPE, stderr=PIPE,
59+
text=True, check=True
60+
).stdout
61+
62+
assert __description__ in help_text

tests/py2puml/test_py2puml.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
1-
from py2puml import __version__, py2puml
21

2+
from py2puml import py2puml
33

4-
def test_version():
5-
assert __version__ == '0.3.1'
64

7-
8-
def test_py2puml_model():
9-
"""test py2puml on py2puml/domain."""
5+
def test_py2puml_model_on_py2uml_domain():
106
expected = """@startuml
117
class py2puml.domain.umlclass.UmlAttribute {
128
name: str

0 commit comments

Comments
 (0)