Skip to content

Commit 175997a

Browse files
authored
fix(cli): add current working directory to path so that py2puml can import and inspect modules in specific folders (#40)
* build(poetry): make poetry use the current python binary * fix(cli): add current working directory to system path so that its modules can be imported and inspected by poetry
1 parent faa3de0 commit 175997a

File tree

11 files changed

+87
-20
lines changed

11 files changed

+87
-20
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Some parsing features are available only since Python 3.8 (like [ast.get_source_
2020

2121
## Features
2222

23-
From a given path corresponding to a folder containing Python code, `py2puml` processes each file as a module and generates a [PlantUML script](https://plantuml.com/en/class-diagram) of its classe-like definitions using:
23+
From a given path corresponding to a folder containing Python code, `py2puml` processes each Python module and generates a [PlantUML script](https://plantuml.com/en/class-diagram) from the definitions of various data structures using:
2424

2525
* **[inspection](https://docs.python.org/3/library/inspect.html)** and [type annotations](https://docs.python.org/3/library/typing.html) to detect:
2626
* static class attributes and [dataclass](https://docs.python.org/3/library/dataclasses.html) fields
@@ -29,7 +29,7 @@ From a given path corresponding to a folder containing Python code, `py2puml` pr
2929
* composition and inheritance relationships (between your domain classes only, for documentation sake).
3030
The detection of composition relationships relies on type annotations only, assigned values or expressions are never evaluated to prevent unwanted side-effects
3131

32-
* parsing [abstract syntax trees](https://docs.python.org/3/library/ast.html#ast.NodeVisitor) to detect the instance attributes defined in `__init__` constructors
32+
* parsing **[abstract syntax trees](https://docs.python.org/3/library/ast.html#ast.NodeVisitor)** to detect the instance attributes defined in `__init__` constructors
3333

3434
`py2puml` outputs diagrams in PlantUML syntax, which can be:
3535
* versioned along your code with a unit-test ensuring its consistency (see the [test_py2puml.py's test_py2puml_model_on_py2uml_domain](tests/py2puml/test_py2puml.py) example)
@@ -192,11 +192,12 @@ python3 -m pytest -v
192192
Code coverage (with [missed branch statements](https://pytest-cov.readthedocs.io/en/latest/config.html?highlight=--cov-branch)):
193193

194194
```sh
195-
poetry run pytest -v --cov=py2puml --cov-branch --cov-report term-missing --cov-fail-under 90
195+
poetry run pytest -v --cov=py2puml --cov-branch --cov-report term-missing --cov-fail-under 92
196196
```
197197

198198
# Changelog
199199

200+
* `0.7.2`: added the current working directory to the import path to make py2puml work in any directory or in native virtual environment (not handled by poetry)
200201
* `0.7.1`: removed obsolete part of documentation: deeply compound types are now well handled (by version `0.7.0`)
201202
* `0.7.0`: improved the generated PlantUML documentation (added the namespace structure of the code base, homogenized type between inspection and parsing), improved relationships management (handle forward references, deduplicate relationships)
202203
* `0.6.1`: handle class names with digits

poetry.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
[virtualenvs]
22
create = true
33
in-project = true
4+
prefer-active-python = true
45

56
[repositories]
67
[repositories.testpypi]

py2puml/asserts.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from io import StringIO
22
from pathlib import Path
3-
from typing import Union
3+
from typing import Iterable, List, Union
44

55
from py2puml.py2puml import py2puml
66

@@ -13,9 +13,13 @@ def assert_py2puml_is_file_content(domain_path: str, domain_module: str, diagram
1313
def assert_py2puml_is_stringio(domain_path: str, domain_module: str, expected_content_stream: StringIO):
1414
# generates the PlantUML documentation
1515
puml_content = list(py2puml(domain_path, domain_module))
16+
17+
assert_multilines(puml_content, expected_content_stream)
18+
19+
20+
def assert_multilines(actual_multilines: List[str], expected_multilines: Iterable[str]):
1621
line_index = 0
17-
for line_index, (actual_line, expected_line) in enumerate(zip(puml_content, expected_content_stream)):
18-
assert actual_line == expected_line, f'actual and expected contents have changed at line {line_index + 1}'
22+
for line_index, (actual_line, expected_line) in enumerate(zip(actual_multilines, expected_multilines)):
23+
assert actual_line == expected_line, f'actual and expected contents have changed at line {line_index + 1}: {actual_line=}, {expected_line=}'
1924

20-
assert line_index + 1 == len(puml_content), f'actual and expected diagrams have {line_index + 1} lines'
21-
25+
assert line_index + 1 == len(actual_multilines), f'actual and expected diagrams have {line_index + 1} lines'

py2puml/cli.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,21 @@
22
# -*- coding: utf-8 -*-
33

44
from argparse import ArgumentParser
5+
from pathlib import Path
6+
from sys import path
57

68
from py2puml.py2puml import py2puml
79

810

911
def run():
12+
# adds the current working directory to the system path so that py2puml can import them
13+
current_working_directory = str(Path.cwd().resolve())
14+
if current_working_directory not in path:
15+
path.append(current_working_directory)
16+
1017
argparser = ArgumentParser(description='Generate PlantUML class diagrams to document your Python application.')
1118

12-
argparser.add_argument('-v', '--version', action='version', version='py2puml 0.7.1')
19+
argparser.add_argument('-v', '--version', action='version', version='py2puml 0.7.2')
1320
argparser.add_argument('path', metavar='path', type=str, help='the filepath to the domain')
1421
argparser.add_argument('module', metavar='module', type=str, help='the module name of the domain', default=None)
1522

pyproject.toml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "py2puml"
3-
version = "0.7.1"
3+
version = "0.7.2"
44
description = "Generate PlantUML class diagrams to document your Python application."
55
keywords = ["class diagram", "PlantUML", "documentation", "inspection", "AST"]
66
readme = "README.md"
@@ -25,9 +25,7 @@ pytest-cov = "^4.0.0"
2525

2626
[tool.pytest.ini_options]
2727
minversion = "6.2"
28-
testpaths = [
29-
"tests"
30-
]
28+
testpaths = ["tests"]
3129
console_output_style = "count"
3230

3331
[tool.pylint.messages_control]

tests/__init__.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
from os.path import dirname, realpath, join
2-
from re import compile as re_compile
31
from itertools import takewhile
2+
from pathlib import Path
3+
from re import compile as re_compile
4+
45

56
# exports the version and the project description read from the pyproject.toml file
67
__version__ = None
78
__description__ = None
89

9-
PARENT_DIR = dirname(dirname(realpath(__file__)))
10+
TESTS_PATH = Path(__file__).parent
11+
PROJECT_PATH = TESTS_PATH.parent
12+
1013
VERSION_PATTERN = re_compile('^version = "([^"]+)"$')
1114
DESCRIPTION_PATTERN = re_compile('^description = "([^"]+)"$')
1215

@@ -17,7 +20,7 @@ def get_from_line_and_pattern(content_line: str, pattern) -> str:
1720
else:
1821
return pattern_match.group(1)
1922

20-
with open(join(PARENT_DIR, 'pyproject.toml'), encoding='utf8') as pyproject:
21-
for line in takewhile(lambda _: __version__ is None or __description__ is None, pyproject):
23+
with open(PROJECT_PATH / 'pyproject.toml', encoding='utf8') as pyproject_file:
24+
for line in takewhile(lambda _: __version__ is None or __description__ is None, pyproject_file):
2225
__version__ = __version__ if __version__ is not None else get_from_line_and_pattern(line, VERSION_PATTERN)
2326
__description__ = __description__ if __description__ is not None else get_from_line_and_pattern(line, DESCRIPTION_PATTERN)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from dataclasses import dataclass
2+
3+
4+
@dataclass
5+
class Point:
6+
x: float
7+
y: float
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from dataclasses import dataclass
2+
3+
from withrootnotincwd.point import Point
4+
5+
6+
@dataclass
7+
class Segment:
8+
a: Point
9+
b: Point
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
@startuml withrootnotincwd
2+
namespace withrootnotincwd {
3+
namespace point {}
4+
namespace segment {}
5+
}
6+
class withrootnotincwd.point.Point {
7+
x: float
8+
y: float
9+
}
10+
class withrootnotincwd.segment.Segment {
11+
a: Point
12+
b: Point
13+
}
14+
withrootnotincwd.segment.Segment *-- withrootnotincwd.point.Point
15+
footer Generated by //py2puml//
16+
@enduml

tests/py2puml/test__init__.py

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

33
# Ensures the library version is modified in the pyproject.toml file when upgrading it (pull request)
44
def test_version():
5-
assert __version__ == '0.7.1'
5+
assert __version__ == '0.7.2'
66

77
# Description also output in the CLI
88
def test_description():

0 commit comments

Comments
 (0)