Skip to content

Commit cad916a

Browse files
Fix for Issue #123 (Arbitrary-length homogeneous tuple type unsupported) (#141)
* Added '...' to SPLITTING_CHARACTERS This should treat '...' as a character it should split on, as such it is not replaced and passed down to the puml generation for now. An appropriate conversion into pump should be considered. * Removing commented out code used for debugging * Removing commented out code used in debugging * Added more tests for Tuple[T, ...] for some type, T * Improved verbosity for failed assert_attribute fn * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * docs: add Mieszko to contributors, bump version to 0.10.0 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Luc Sorel-Giffo
1 parent c537346 commit cad916a

File tree

10 files changed

+45
-14
lines changed

10 files changed

+45
-14
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ poetry run pytest -v --cov=py2puml --cov-branch --cov-report term-missing --cov-
202202

203203
# Changelog
204204

205+
* `0.10.0`: support ellipsis in type annotations (tuple with arbitrary length)
205206
* `0.9.1`: improved 0.7.2 by adding the current working directory at the beginning of the sys.path to resolve the module paths of the project being inspected.
206207
Fix url to PlantUML logo on the README.md page
207208
* `0.9.0`: add classes defined in `__init__.py` files to plantuml output; replaced yapf by the ruff formatter
@@ -234,6 +235,7 @@ Unless stated otherwise all works are licensed under the [MIT license](http://sp
234235
* [Julien Jerphanion](https://github.com/jjerphan)
235236
* [Luis Fernando Villanueva Pérez](https://github.com/jonykalavera)
236237
* [Konstantin Zangerle](https://github.com/justkiddingcode)
238+
* [Mieszko](https://github.com/0xmzk)
237239

238240
## Pull requests
239241

py2puml/cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def run():
1616

1717
argparser = ArgumentParser(description='Generate PlantUML class diagrams to document your Python application.')
1818

19-
argparser.add_argument('-v', '--version', action='version', version='py2puml 0.9.1')
19+
argparser.add_argument('-v', '--version', action='version', version='py2puml 0.10.0')
2020
argparser.add_argument('path', metavar='path', type=str, help='the filepath to the domain')
2121
argparser.add_argument('module', metavar='module', type=str, help='the module name of the domain', default=None)
2222

py2puml/parsing/astvisitors.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,6 @@ def shorten_compound_type_annotation(type_annotation: str, module_resolver: Modu
206206
compound_short_type_parts: List[str] = []
207207
associated_types: List[str] = []
208208
for compound_type_part in compound_type_parts:
209-
# characters like '[', ']', ',', '|'
210209
if compound_type_part in SPLITTING_CHARACTERS:
211210
if compound_type_part == ',':
212211
compound_short_type_parts.append(', ')

py2puml/parsing/compoundtypesplitter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
IS_COMPOUND_TYPE: Pattern = re_compile(r'^[a-z|A-Z|0-9|\[|\]|\.|,|\s|_|\|]+$')
1010

1111
# characters involved in the build-up of compound types
12-
SPLITTING_CHARACTERS = '[', ']', ',', '|'
12+
SPLITTING_CHARACTERS = '[', ']', ',', '|', '...'
1313

1414
# 'None' in 'Union[str, None]' type signature is changed into 'NoneType' when inspecting a module
1515
LAST_NONETYPE_IN_UNION: Pattern = re_compile(r'Union\[(?:(?:[^\[\]])*NoneType)')

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "py2puml"
3-
version = "0.9.1"
3+
version = "0.10.0"
44
description = "Generate PlantUML class diagrams to document your Python application."
55
keywords = ["class diagram", "PlantUML", "documentation", "inspection", "AST"]
66
readme = "README.md"

tests/asserts/attribute.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22

33

44
def assert_attribute(attribute: UmlAttribute, expected_name: str, expected_type: str, expected_staticity: bool):
5-
assert attribute.name == expected_name
6-
assert attribute.type == expected_type
7-
assert attribute.static == expected_staticity
5+
assert attribute.name == expected_name, f"Got '{attribute.name}' but expected '{expected_name}'"
6+
assert attribute.type == expected_type, f"Got '{attribute.type}' but expected '{expected_type}'"
7+
assert attribute.static == expected_staticity, f"Got '{attribute.static}' but expected '{expected_staticity}'"

tests/modules/withcompoundtypewithdigits.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import List
1+
from typing import List, Tuple
22

33

44
class IPv6:
@@ -12,3 +12,8 @@ class Multicast:
1212
def __init__(self, address: IPv6, repetition: int):
1313
# List[IPv6] must be parsed
1414
self.addresses: List[IPv6] = [address] * repetition
15+
16+
17+
class Network:
18+
def __init__(self, network_devices: Tuple[IPv6, ...]):
19+
self.devices = network_devices

tests/py2puml/inspection/test_inspectclass.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ def test_inspect_module_should_handle_compound_types_with_numbers_in_their_name(
161161
fqdn = 'tests.modules.withcompoundtypewithdigits'
162162
inspect_module(import_module(fqdn), fqdn, domain_items_by_fqn, domain_relations)
163163

164-
assert len(domain_items_by_fqn) == 2, 'two classes must be inspected'
164+
assert len(domain_items_by_fqn) == 3, 'three classes must be inspected'
165165

166166
# IPv6 UmlClass
167167
ipv6_umlitem: UmlClass = domain_items_by_fqn[f'{fqdn}.IPv6']
@@ -174,3 +174,9 @@ def test_inspect_module_should_handle_compound_types_with_numbers_in_their_name(
174174
assert len(multicast_umlitem.attributes) == 1, '1 attributes of Multicast must be inspected'
175175
address_attribute = multicast_umlitem.attributes[0]
176176
assert_attribute(address_attribute, 'addresses', 'List[IPv6]', expected_staticity=False)
177+
178+
# Network UmlClass
179+
network_umlitem: UmlClass = domain_items_by_fqn[f'{fqdn}.Network']
180+
assert len(network_umlitem.attributes) == 1, '1 attributes of Network must be inspected'
181+
devices_attribute = network_umlitem.attributes[0]
182+
assert_attribute(devices_attribute, 'devices', 'Tuple[IPv6, ...]', expected_staticity=False)

tests/py2puml/parsing/test_astvisitors.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ def __init__(
2929
y_a: Union[int, float, None],
3030
x_b: int | float,
3131
y_b: int | float | None,
32+
# arbitrary-length homogenous tuple type
33+
bb: Tuple[int, ...],
3234
# an argument with a default value
3335
a_default_string: str = 'text',
3436
# positional and keyword wildcard arguments
@@ -46,7 +48,7 @@ def test_SignatureVariablesCollector_collect_arguments():
4648
collector.visit(constructor_ast)
4749

4850
assert collector.class_self_id == 'me'
49-
assert len(collector.variables) == 10, 'all the arguments must be detected'
51+
assert len(collector.variables) == 11, 'all the arguments must be detected'
5052
assert_Variable(collector.variables[0], 'an_int', 'int', constructor_source)
5153
assert_Variable(collector.variables[1], 'an_untyped', None, constructor_source)
5254
assert_Variable(
@@ -58,9 +60,10 @@ def test_SignatureVariablesCollector_collect_arguments():
5860
assert_Variable(collector.variables[5], 'x_b', 'int | float', constructor_source)
5961
assert_Variable(collector.variables[6], 'y_b', 'int | float | None', constructor_source)
6062

61-
assert_Variable(collector.variables[7], 'a_default_string', 'str', constructor_source)
62-
assert_Variable(collector.variables[8], 'args', None, constructor_source)
63-
assert_Variable(collector.variables[9], 'kwargs', None, constructor_source)
63+
assert_Variable(collector.variables[7], 'bb', 'Tuple[int, ...]', constructor_source)
64+
assert_Variable(collector.variables[8], 'a_default_string', 'str', constructor_source)
65+
assert_Variable(collector.variables[9], 'args', None, constructor_source)
66+
assert_Variable(collector.variables[10], 'kwargs', None, constructor_source)
6467

6568

6669
@mark.parametrize(
@@ -246,6 +249,22 @@ def test_AssignedVariablesCollector_multiple_assignments_separate_variable_from_
246249
},
247250
},
248251
),
252+
(
253+
# new test case for Tuple[int, ...]
254+
'typing.Tuple[int, ...]',
255+
'Tuple[int, ...]',
256+
['typing.Tuple', 'builtins.int'],
257+
{
258+
'__name__': 'testmodule',
259+
'__builtins__': {
260+
'int': {
261+
'__module__': 'builtins',
262+
'__name__': 'int',
263+
},
264+
},
265+
'Tuple': Tuple,
266+
},
267+
),
249268
],
250269
)
251270
def test_shorten_compound_type_annotation(

tests/py2puml/test__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
# Ensures the library version is modified in the pyproject.toml file when upgrading it (pull request)
55
def test_version():
6-
assert __version__ == '0.9.1'
6+
assert __version__ == '0.10.0'
77

88

99
# Description also output in the CLI

0 commit comments

Comments
 (0)