Skip to content

Commit b80c000

Browse files
committed
style: replace yapf by ruff formatter (#70)
1 parent 4b8f785 commit b80c000

32 files changed

+331
-269
lines changed

.pre-commit-config.yaml

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ repos:
88
- id: trailing-whitespace
99
# ensures that all files end in a newline and only a newline
1010
- id: end-of-file-fixer
11-
# replace "double quotes" by 'single quotes' unless "it's impossible"
12-
- id: double-quote-string-fixer
1311
# prevents large files from being committed (>100kb)
1412
- id: check-added-large-files
1513
args: [--maxkb=100]
@@ -32,14 +30,8 @@ repos:
3230
- id: isort
3331
additional_dependencies: [toml]
3432

35-
- repo: https://github.com/google/yapf
36-
rev: v0.40.2
37-
hooks:
38-
- id: yapf
39-
name: Yapf
40-
additional_dependencies: [toml]
41-
4233
- repo: https://github.com/astral-sh/ruff-pre-commit
43-
rev: v0.1.4
34+
rev: v0.1.5
4435
hooks:
4536
- id: ruff
37+
- id: ruff-format

CONTRIBUTING.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ To save time (my writing time and your reading time), I tried to make it short s
6666

6767
To homogenize code style consistency and enforce code quality, this project uses:
6868

69-
- the `yapf` formatter and the `ruff` linter
69+
- the `ruff` linter and formatter
70+
- the `isort` formatter for imports, because it handles the 'tests' sections in the imports
7071
- `pre-commit` hooks that are triggered on the Github CI (in pull-requests) and should be activated locally when contributing to the project:
7172

7273
```sh
@@ -75,6 +76,9 @@ poetry install
7576

7677
# activates the pre-commit hooks
7778
poetry run pre-commit install --hook-type pre-commit --hook-type commit-msg
79+
80+
# runs the code quality hooks on the codebase
81+
poetry run pre-commit run --all-files
7882
```
7983

8084
## Unit tests

README.md

Lines changed: 3 additions & 2 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+
* `upcoming`: replaced yapf by the ruff formatter
205206
* `0.8.1`: delegated the grouping of nested namespaces (see `0.7.0`) to the PlantUML binary, which handles it natively
206207
* `0.8.0`: added support for union types, and github actions (pre-commit hooks + automated tests)
207208
* `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)
@@ -238,7 +239,7 @@ Pull-requests are welcome and will be processed on a best-effort basis.
238239
Pull requests must follow the guidelines enforced by the `pre-commit` hooks:
239240

240241
- commit messages must follow the Angular conventions enforced by the `commitlint` hook
241-
- code formatting must follow the conventions enforced by the `isort` and `yapf` hooks
242+
- code formatting must follow the conventions enforced by the `isort` and `ruff-format` hooks
242243
- code linting should not detect code smells in your contributions, this is checked by the `ruff` hook
243244

244245
Please also follow the [contributing guide](CONTRIBUTING.md) to ease your contribution.
@@ -274,7 +275,7 @@ When merging your pull-request, the new version of the project will be derived f
274275

275276
### Code formatting
276277

277-
This project uses `isort` and `yapf` to format the code.
278+
This project uses `isort` and `ruff-format` to format the code.
278279
The guidelines are expressed in their respective sections in the [pyproject.toml](pyproject.toml) file.
279280

280281
### Best practices

py2puml/asserts.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ def assert_py2puml_is_file_content(domain_path: str, domain_module: str, diagram
1212

1313

1414
def normalize_lines_with_returns(lines_with_returns: Iterable[str]) -> List[str]:
15-
'''
15+
"""
1616
When comparing contents, each piece of contents can either be:
1717
- a formatted string block output by the py2puml command containg line returns
1818
- a single line of contents read from a file, each line ending with a line return
1919
2020
This function normalizes each sequence of contents as a list of string lines,
2121
each one finishing without a line return to ease comparison.
22-
'''
22+
"""
2323
return ''.join(lines_with_returns).split('\n')
2424

2525

@@ -34,6 +34,8 @@ def assert_multilines(actual_multilines: List[str], expected_multilines: List[st
3434
line_index = 0
3535
for line_index, (actual_line, expected_line) in enumerate(zip(actual_multilines, expected_multilines)):
3636
# print(f'{actual_line=}\n{expected_line=}')
37-
assert actual_line == expected_line, f'actual and expected contents have changed at line {line_index + 1}: {actual_line=}, {expected_line=}'
37+
assert (
38+
actual_line == expected_line
39+
), f'actual and expected contents have changed at line {line_index + 1}: {actual_line=}, {expected_line=}'
3840

3941
assert line_index + 1 == len(actual_multilines), f'actual and expected diagrams have {line_index + 1} lines'

py2puml/export/puml.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,22 @@
55
from py2puml.domain.umlitem import UmlItem
66
from py2puml.domain.umlrelation import UmlRelation
77

8-
PUML_FILE_START = '''@startuml {diagram_name}
8+
PUML_FILE_START = """@startuml {diagram_name}
99
!pragma useIntermediatePackages false
1010
11-
'''
12-
PUML_FILE_FOOTER = '''footer Generated by //py2puml//
13-
'''
14-
PUML_FILE_END = '''@enduml
15-
'''
16-
PUML_ITEM_START_TPL = '''{item_type} {item_fqn} {{
17-
'''
18-
PUML_ATTR_TPL = ''' {attr_name}: {attr_type}{staticity}
19-
'''
20-
PUML_ITEM_END = '''}
21-
'''
22-
PUML_RELATION_TPL = '''{source_fqn} {rel_type}-- {target_fqn}
23-
'''
11+
"""
12+
PUML_FILE_FOOTER = """footer Generated by //py2puml//
13+
"""
14+
PUML_FILE_END = """@enduml
15+
"""
16+
PUML_ITEM_START_TPL = """{item_type} {item_fqn} {{
17+
"""
18+
PUML_ATTR_TPL = """ {attr_name}: {attr_type}{staticity}
19+
"""
20+
PUML_ITEM_END = """}
21+
"""
22+
PUML_RELATION_TPL = """{source_fqn} {rel_type}-- {target_fqn}
23+
"""
2424

2525
FEATURE_STATIC = ' {static}'
2626
FEATURE_INSTANCE = ''
@@ -46,7 +46,7 @@ def to_puml_content(diagram_name: str, uml_items: List[UmlItem], uml_relations:
4646
yield PUML_ATTR_TPL.format(
4747
attr_name=uml_attr.name,
4848
attr_type=uml_attr.type,
49-
staticity=FEATURE_STATIC if uml_attr.static else FEATURE_INSTANCE
49+
staticity=FEATURE_STATIC if uml_attr.static else FEATURE_INSTANCE,
5050
)
5151
yield PUML_ITEM_END
5252
else:

py2puml/inspection/inspectclass.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,17 @@ def handle_inheritance_relation(
2626

2727

2828
def inspect_static_attributes(
29-
class_type: Type, class_type_fqn: str, root_module_name: str, domain_items_by_fqn: Dict[str, UmlItem],
30-
domain_relations: List[UmlRelation]
29+
class_type: Type,
30+
class_type_fqn: str,
31+
root_module_name: str,
32+
domain_items_by_fqn: Dict[str, UmlItem],
33+
domain_relations: List[UmlRelation],
3134
) -> List[UmlAttribute]:
32-
'''
35+
"""
3336
Adds the definitions:
3437
- of the inspected type
3538
- of its static attributes from the class annotations (type and relation)
36-
'''
39+
"""
3740
# defines the class being inspected
3841
definition_attrs: List[UmlAttribute] = []
3942
uml_class = UmlClass(
@@ -85,8 +88,11 @@ def inspect_static_attributes(
8588

8689

8790
def inspect_class_type(
88-
class_type: Type, class_type_fqn: str, root_module_name: str, domain_items_by_fqn: Dict[str, UmlItem],
89-
domain_relations: List[UmlRelation]
91+
class_type: Type,
92+
class_type_fqn: str,
93+
root_module_name: str,
94+
domain_items_by_fqn: Dict[str, UmlItem],
95+
domain_relations: List[UmlRelation],
9096
):
9197
attributes = inspect_static_attributes(
9298
class_type, class_type_fqn, root_module_name, domain_items_by_fqn, domain_relations
@@ -99,11 +105,15 @@ def inspect_class_type(
99105

100106

101107
def inspect_dataclass_type(
102-
class_type: Type[dataclass], class_type_fqn: str, root_module_name: str, domain_items_by_fqn: Dict[str, UmlItem],
103-
domain_relations: List[UmlRelation]
108+
class_type: Type[dataclass],
109+
class_type_fqn: str,
110+
root_module_name: str,
111+
domain_items_by_fqn: Dict[str, UmlItem],
112+
domain_relations: List[UmlRelation],
104113
):
105-
for attribute in inspect_static_attributes(class_type, class_type_fqn, root_module_name, domain_items_by_fqn,
106-
domain_relations):
114+
for attribute in inspect_static_attributes(
115+
class_type, class_type_fqn, root_module_name, domain_items_by_fqn, domain_relations
116+
):
107117
attribute.static = False
108118

109119
handle_inheritance_relation(class_type, class_type_fqn, root_module_name, domain_relations)

py2puml/inspection/inspectenum.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ def inspect_enum_type(enum_type: Type[Enum], enum_type_fqn: str, domain_items_by
99
domain_items_by_fqn[enum_type_fqn] = UmlEnum(
1010
name=enum_type.__name__,
1111
fqn=enum_type_fqn,
12-
members=[Member(name=enum_member.name, value=enum_member.value) for enum_member in enum_type]
12+
members=[Member(name=enum_member.name, value=enum_member.value) for enum_member in enum_type],
1313
)

py2puml/inspection/inspectmodule.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,22 @@ def filter_domain_definitions(module: ModuleType, root_module_name: str) -> Iter
1818
definition_members = getmembers(definition_type)
1919
definition_module_member = next(
2020
(
21-
member for member in definition_members
21+
member
22+
for member in definition_members
2223
# ensures that the type belongs to the module being parsed
2324
if member[0] == '__module__' and member[1].startswith(root_module_name)
2425
),
25-
None
26+
None,
2627
)
2728
if definition_module_member is not None:
2829
yield definition_type
2930

3031

3132
def inspect_domain_definition(
32-
definition_type: Type, root_module_name: str, domain_items_by_fqn: Dict[str, UmlItem],
33-
domain_relations: List[UmlRelation]
33+
definition_type: Type,
34+
root_module_name: str,
35+
domain_items_by_fqn: Dict[str, UmlItem],
36+
domain_relations: List[UmlRelation],
3437
):
3538
definition_type_fqn = f'{definition_type.__module__}.{definition_type.__name__}'
3639
if definition_type_fqn not in domain_items_by_fqn:
@@ -49,8 +52,10 @@ def inspect_domain_definition(
4952

5053

5154
def inspect_module(
52-
domain_item_module: ModuleType, root_module_name: str, domain_items_by_fqn: Dict[str, UmlItem],
53-
domain_relations: List[UmlRelation]
55+
domain_item_module: ModuleType,
56+
root_module_name: str,
57+
domain_items_by_fqn: Dict[str, UmlItem],
58+
domain_relations: List[UmlRelation],
5459
):
5560
# processes only the definitions declared or imported within the given root module
5661
for definition_type in filter_domain_definitions(domain_item_module, root_module_name):

py2puml/inspection/inspectnamedtuple.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ def inspect_namedtuple_type(namedtuple_type: Type, namedtuple_type_fqn: str, dom
88
domain_items_by_fqn[namedtuple_type_fqn] = UmlClass(
99
name=namedtuple_type.__name__,
1010
fqn=namedtuple_type_fqn,
11-
attributes=[UmlAttribute(tuple_field, 'Any', False) for tuple_field in namedtuple_type._fields]
11+
attributes=[UmlAttribute(tuple_field, 'Any', False) for tuple_field in namedtuple_type._fields],
1212
)

py2puml/parsing/astvisitors.py

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
from ast import (
2-
AnnAssign, Assign, Attribute, BinOp, FunctionDef, Name, NodeVisitor, Subscript, arg, expr, get_source_segment
2+
AnnAssign,
3+
Assign,
4+
Attribute,
5+
BinOp,
6+
FunctionDef,
7+
Name,
8+
NodeVisitor,
9+
Subscript,
10+
arg,
11+
expr,
12+
get_source_segment,
313
)
414
from collections import namedtuple
515
from typing import Dict, List, Tuple
@@ -13,9 +23,10 @@
1323

1424

1525
class SignatureVariablesCollector(NodeVisitor):
16-
'''
26+
"""
1727
Collects the variables and their type annotations from the signature of a constructor method
18-
'''
28+
"""
29+
1930
def __init__(self, constructor_source: str, *args, **kwargs):
2031
super().__init__(*args, **kwargs)
2132
self.constructor_source = constructor_source
@@ -34,38 +45,40 @@ def visit_arg(self, node: arg):
3445

3546

3647
class AssignedVariablesCollector(NodeVisitor):
37-
'''Parses the target of an assignment statement to detect whether the value is assigned to a variable or an instance attribute'''
48+
"""Parses the target of an assignment statement to detect whether the value is assigned to a variable or an instance attribute"""
49+
3850
def __init__(self, class_self_id: str, annotation: expr):
3951
self.class_self_id: str = class_self_id
4052
self.annotation: expr = annotation
4153
self.variables: List[Variable] = []
4254
self.self_attributes: List[Variable] = []
4355

4456
def visit_Name(self, node: Name):
45-
'''
57+
"""
4658
Detects declarations of new variables
47-
'''
59+
"""
4860
if node.id != self.class_self_id:
4961
self.variables.append(Variable(node.id, self.annotation))
5062

5163
def visit_Attribute(self, node: Attribute):
52-
'''
64+
"""
5365
Detects declarations of new attributes on 'self'
54-
'''
66+
"""
5567
if isinstance(node.value, Name) and node.value.id == self.class_self_id:
5668
self.self_attributes.append(Variable(node.attr, self.annotation))
5769

5870
def visit_Subscript(self, node: Subscript):
59-
'''
71+
"""
6072
Assigns a value to a subscript of an existing variable: must be skipped
61-
'''
73+
"""
6274
pass
6375

6476

6577
class ConstructorVisitor(NodeVisitor):
66-
'''
78+
"""
6779
Identifies the attributes (and infer their type) assigned to self in the body of a constructor method
68-
'''
80+
"""
81+
6982
def __init__(
7083
self, constructor_source: str, class_name: str, root_fqn: str, module_resolver: ModuleResolver, *args, **kwargs
7184
):
@@ -97,7 +110,7 @@ def get_from_namespace(self, variable_id: str) -> Variable:
97110
for variable in self.variables_namespace[::-1]
98111
if variable.id == variable_id
99112
),
100-
None
113+
None,
101114
)
102115

103116
def generic_visit(self, node):
@@ -154,11 +167,11 @@ def visit_Assign(self, node: Assign):
154167
self.variables_namespace.extend(variables_collector.variables)
155168

156169
def derive_type_annotation_details(self, annotation: expr) -> Tuple[str, List[str]]:
157-
'''
170+
"""
158171
From a type annotation, derives:
159172
- a short version of the type (withenum.TimeUnit -> TimeUnit, Tuple[withenum.TimeUnit] -> Tuple[TimeUnit])
160173
- a list of the full-namespaced definitions involved in the type annotation (in order to build the relationships)
161-
'''
174+
"""
162175
if annotation is None:
163176
return None, []
164177

@@ -182,13 +195,13 @@ def derive_type_annotation_details(self, annotation: expr) -> Tuple[str, List[st
182195

183196

184197
def shorten_compound_type_annotation(type_annotation: str, module_resolver: ModuleResolver) -> Tuple[str, List[str]]:
185-
'''
198+
"""
186199
In the string representation of a compound type annotation, the elementary types can be prefixed by their packages or sub-packages.
187200
Like in 'Dict[datetime.datetime,typing.List[Worker]]'. This function returns a tuple of 2 values:
188201
- a string representation with shortened types for display purposes in the PlantUML documentation: 'Dict[datetime, List[Worker]]'
189202
(note: a space is inserted after each coma for readability sake)
190203
- a list of the fully-qualified types involved in the annotation: ['typing.Dict', 'datetime.datetime', 'typing.List', 'mymodule.Worker']
191-
'''
204+
"""
192205
compound_type_parts: List[str] = CompoundTypeSplitter(type_annotation, module_resolver.module.__name__).get_parts()
193206
compound_short_type_parts: List[str] = []
194207
associated_types: List[str] = []

0 commit comments

Comments
 (0)