Skip to content

Commit a6fa72d

Browse files
committed
🔖 0.2.0 handle inheritance and enums
1 parent 8e4a975 commit a6fa72d

18 files changed

+402
-109
lines changed

README.md

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
<div align="center">
2+
<a href="https://www.python.org/psf-landing/" target="_blank">
3+
<img width="350px" alt="Python logo"
4+
src="https://www.python.org/static/community_logos/python-logo-generic.svg" />
5+
</a>
6+
<a href="http://plantuml.com/" target="_blank">
7+
<img width="116px" height="112px" alt="PlantUML logo" src="http://s.plantuml.com/logoc.png" style="margin-bottom: 40px" vspace="40px" />
8+
</a>
9+
<h1>Python to PlantUML</h1>
10+
</div>
11+
112
Generate Plantuml diagrams to document your python code
213

314
# How it works
@@ -32,20 +43,19 @@ Install from the github repository:
3243
* with `pip`:
3344

3445
```sh
35-
pip3 install git+https://github.com/lucsorel/py2puml.git
46+
pip install py2puml
3647
```
3748

3849
* with [poetry](https://pipenv.readthedocs.io/en/latest/):
3950

4051
```sh
41-
poetry add git+https://github.com/lucsorel/py2puml.git
52+
poetry add py2puml
4253
```
4354

4455
* with [pipenv](https://pipenv.readthedocs.io/en/latest/):
4556

4657
```sh
47-
# should be installed in editable mode to ensure an up-to-date copy of the repository and that it includes all known dependencies -> '-e'
48-
pipenv install -e git+https://github.com/lucsorel/py2puml.git#egg=py2puml
58+
pipenv install py2puml
4959
```
5060

5161
# Usage
@@ -69,33 +79,56 @@ with open('py2puml/domain.puml', 'w') as puml_file:
6979

7080
```plantuml
7181
@startuml
72-
class py2puml.domain.umlattribute.UmlAttribute {
82+
class py2puml.domain.umlclass.UmlAttribute {
7383
name: str
7484
type: str
7585
}
7686
class py2puml.domain.umlclass.UmlClass {
87+
attributes: List[UmlAttribute]
88+
}
89+
class py2puml.domain.umlitem.UmlItem {
7790
name: str
7891
fqdn: str
79-
attributes: List[UmlAttribute]
8092
}
81-
class py2puml.domain.umlcomposition.UmlComposition {
82-
compound_fqdn: str
83-
component_fqdn: str
93+
class py2puml.domain.umlenum.Member {
94+
name: str
95+
value: str
96+
}
97+
class py2puml.domain.umlenum.UmlEnum {
98+
members: List[Member]
99+
}
100+
enum py2puml.domain.umlrelation.RelType {
101+
COMPOSITION: *
102+
INHERITANCE: <|
103+
}
104+
class py2puml.domain.umlrelation.UmlRelation {
105+
source_fqdn: str
106+
target_fqdn: str
107+
type: RelType
84108
}
85-
py2puml.domain.umlclass.UmlClass *-- py2puml.domain.umlattribute.UmlAttribute
109+
py2puml.domain.umlclass.UmlClass *-- py2puml.domain.umlclass.UmlAttribute
110+
py2puml.domain.umlitem.UmlItem <|-- py2puml.domain.umlclass.UmlClass
111+
py2puml.domain.umlenum.UmlEnum *-- py2puml.domain.umlenum.Member
112+
py2puml.domain.umlitem.UmlItem <|-- py2puml.domain.umlenum.UmlEnum
113+
py2puml.domain.umlrelation.UmlRelation *-- py2puml.domain.umlrelation.RelType
86114
@enduml
87115
```
88116

89117
Which renders like this:
90118

91-
![](https://www.plantuml.com/plantuml/png/ZP0_2y8m4CNtV8gRXNPmx5HnTNKIaMWY199Bp5s68ltkfi7KWlXd-xrty7uXFR6Cd9mL5ok980pha5Ehl9C6suoIEPfpOjtkdTtK07S1WDBf3eXZPXx2ayUFKwMVPhOJl4rSRmehprRgO6U83qlvyPl3k-39iF5OJAzOVEMSK9rcMIrH8o_QKVny_wff_lulqMjK-Ve0)
119+
![](https://www.plantuml.com/plantuml/png/ZP91IyGm48Nl-HKvBsmF7iiUTbaA1jnMQZs9I7OxIY19Qp8H5jV_xZIse5GsFULrQBvvCozRZz9XC9gTjFIUz-URdhwojZDIsOnah6UFHkyGdJe61Fx9EBVIGCuzEj9uxaVzbSRi1n4HSWBwdDyfZq-_cpnVOIa4Cw04dJCph--jJPa16qns07C4Dxl_8NM0HG1oKD0P2IR2fa5-qCC8mu__t7UW9QhEPZNeXhON6VlgS5yzY4PKPSvNL13bRL6BPbVkYvnlBdC_SnvvgaSTcRuBxWGlSIbJMjAz0SRItm17BzGc6TzglLxqL5WYlCs5GAbkBB5_CdCzuoKk4Y6pPJkFNj9niotObkhi6m00)
92120

93121
# Tests
94122

95123
```sh
96-
poetry run python3 -W ignore::DeprecationWarning -m pytest -v
124+
python3 -m pytest -v
97125
```
98126

127+
# Changelog
128+
129+
* `0.2.0`: handle inheritance relationships and enums. Unit tested
130+
* `0.1.3`: first release, handle all module of a folder and compositions of domain classes
131+
99132
# Licence
100133

101134
Unless stated otherwise all works are licensed under the [MIT license](http://spdx.org/licenses/MIT.html), a copy of which is included [here](LICENSE).
@@ -109,7 +142,7 @@ Pull-requests are welcome and will be processed on a best-effort basis.
109142

110143
# Alternatives
111144

112-
If `py2uml` 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):
145+
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):
113146

114147
* [cb109/pyplantuml](https://github.com/cb109/pyplantuml)
115148
* [deadbok/py-puml-tools](https://github.com/deadbok/py-puml-tools)

py2puml/__init__.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,17 @@
1-
__version__ = '0.1.3'
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/domain/umlclass.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
from typing import List
22
from dataclasses import dataclass
33

4-
from py2puml.domain.umlattribute import UmlAttribute
4+
from py2puml.domain.umlitem import UmlItem
55

66
@dataclass
7-
class UmlClass(object):
7+
class UmlAttribute(object):
88
name: str
9-
fqdn: str
9+
type: str
10+
11+
@dataclass
12+
class UmlClass(UmlItem):
1013
attributes: List[UmlAttribute]

py2puml/domain/umlcomposition.py

Lines changed: 0 additions & 6 deletions
This file was deleted.

py2puml/domain/umlenum.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from typing import List
2+
from dataclasses import dataclass
3+
4+
from py2puml.domain.umlitem import UmlItem
5+
6+
@dataclass
7+
class Member(object):
8+
name: str
9+
value: str
10+
11+
@dataclass
12+
class UmlEnum(UmlItem):
13+
members: List[Member]
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from dataclasses import dataclass
22

33
@dataclass
4-
class UmlAttribute(object):
4+
class UmlItem(object):
55
name: str
6-
type: str
6+
fqdn: str

py2puml/domain/umlrelation.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from dataclasses import dataclass
2+
from enum import Enum
3+
4+
class RelType(Enum):
5+
COMPOSITION = '*'
6+
INHERITANCE = '<|'
7+
8+
@dataclass
9+
class UmlRelation(object):
10+
source_fqdn: str
11+
target_fqdn: str
12+
type: RelType

py2puml/exportpuml.py

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,44 @@
11
from typing import List, Iterable
22

3+
from py2puml.domain.umlitem import UmlItem
34
from py2puml.domain.umlclass import UmlClass
4-
from py2puml.domain.umlcomposition import UmlComposition
5+
from py2puml.domain.umlenum import UmlEnum
6+
from py2puml.domain.umlrelation import UmlRelation
57

68
PUML_FILE_START = '@startuml\n'
79
PUML_FILE_END = '@enduml\n'
8-
PUML_CLASS_START_TPL = 'class {class_fqdn} {{\n'
10+
PUML_ITEM_START_TPL = '{item_type} {item_fqdn} {{\n'
911
PUML_ATTR_TPL = ' {attr_name}: {attr_type}\n'
10-
PUML_CLASS_END = '}\n'
11-
PUML_COMPOSITION_TPL = '{compound_fqdn} *-- {component_fqdn}\n'
12+
PUML_ITEM_END = '}\n'
13+
PUML_COMPOSITION_TPL = '{source_fqdn} {rel_type}-- {target_fqdn}\n'
1214

1315

14-
def to_puml_content(uml_classes: List[UmlClass], uml_compositions: List[UmlComposition]) -> Iterable[str]:
16+
def to_puml_content(uml_items: List[UmlItem], uml_relations: List[UmlRelation]) -> Iterable[str]:
1517
yield PUML_FILE_START
16-
for uml_class in uml_classes:
17-
yield PUML_CLASS_START_TPL.format(class_fqdn=uml_class.fqdn)
18-
for uml_attr in uml_class.attributes:
19-
yield PUML_ATTR_TPL.format(attr_type=uml_attr.type, attr_name=uml_attr.name)
20-
yield PUML_CLASS_END
21-
for uml_composition in uml_compositions:
22-
yield PUML_COMPOSITION_TPL.format(compound_fqdn=uml_composition.compound_fqdn, component_fqdn=uml_composition.component_fqdn)
18+
19+
# exports the domain classes and enums
20+
for uml_item in uml_items:
21+
if isinstance(uml_item, UmlEnum):
22+
uml_enum: UmlEnum = uml_item
23+
yield PUML_ITEM_START_TPL.format(item_type='enum', item_fqdn=uml_enum.fqdn)
24+
for member in uml_enum.members:
25+
yield PUML_ATTR_TPL.format(attr_name=member.name, attr_type=member.value)
26+
yield PUML_ITEM_END
27+
elif isinstance(uml_item, UmlClass):
28+
uml_class: UmlClass = uml_item
29+
yield PUML_ITEM_START_TPL.format(item_type='class', item_fqdn=uml_class.fqdn)
30+
for uml_attr in uml_class.attributes:
31+
yield PUML_ATTR_TPL.format(attr_name=uml_attr.name, attr_type=uml_attr.type)
32+
yield PUML_ITEM_END
33+
else:
34+
raise TypeError(f'cannot process uml_item of type {uml_item.__class__}')
35+
36+
# exports the domain relationships between classes and enums
37+
for uml_relation in uml_relations:
38+
yield PUML_COMPOSITION_TPL.format(
39+
source_fqdn=uml_relation.source_fqdn,
40+
rel_type=uml_relation.type.value,
41+
target_fqdn=uml_relation.target_fqdn
42+
)
43+
2344
yield PUML_FILE_END

0 commit comments

Comments
 (0)