Skip to content
This repository was archived by the owner on Jan 19, 2025. It is now read-only.

Commit 6f10701

Browse files
authored
feat: improve migration of annotations (#1163)
Closes #1162. ### Summary of Changes some improvements at the migration of annotations: - output the migrated annotations in different files based on the similarity - remove duplicates (same target and same semantic meaning) in both annotation stores - migrate `@value` annotations if type or value is not correct if the annotated apiv1 element has the same type or value - migrate not unsure annotations if both types of the elements are None - do not migrate unsure `@todo` annotations, but with none as review result ### Testing Instructions run `migrate` command or run the tests in `package-parser/tests/processing/migration/test_migration.py`
1 parent ab94562 commit 6f10701

38 files changed

+2352
-520
lines changed

.github/workflows/main.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ jobs:
9797
- name: Smoke test (all)
9898
run: poetry run parse-package all -p package_parser -s package_parser -c package_parser -o out
9999

100+
- name: Smoke test (migration)
101+
run: poetry run parse-package migrate -a1 tests/data/migration/apiv1_data.json -a2 tests/data/migration/apiv2_data.json -a tests/data/migration/annotationv1.json -o out
102+
100103
# Requires installation of pytest and pytest-cov
101104
- name: Test with pytest
102105
run: poetry run pytest --doctest-modules

package-parser/package_parser/cli/_run_migrate.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import os
12
from pathlib import Path
23

3-
from package_parser.processing.migration import migrate_annotations
4+
from package_parser.processing.migration import Migration
45
from package_parser.processing.migration.model import APIMapping, SimpleDiffer
56

67
from ._read_and_write_file import (
@@ -22,5 +23,19 @@ def _run_migrate_command(
2223
differ = SimpleDiffer()
2324
api_mapping = APIMapping(apiv1, apiv2, differ)
2425
mappings = api_mapping.map_api()
25-
annotationsv2 = migrate_annotations(annotationsv1, mappings)
26-
_write_annotations_file(annotationsv2, out_dir_path)
26+
migration = Migration(annotationsv1, mappings)
27+
migration.migrate_annotations()
28+
migrated_annotations_file = Path(
29+
os.path.join(out_dir_path, "migrated_annotationsv" + apiv2.version + ".json")
30+
)
31+
unsure_migrated_annotations_file = Path(
32+
os.path.join(
33+
out_dir_path, "unsure_migrated_annotationsv" + apiv2.version + ".json"
34+
)
35+
)
36+
_write_annotations_file(
37+
migration.migrated_annotation_store, migrated_annotations_file
38+
)
39+
_write_annotations_file(
40+
migration.unsure_migrated_annotation_store, unsure_migrated_annotations_file
41+
)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
from ._are_semantic_equal import are_semantic_equal
12
from ._generate_annotations import generate_annotations
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
from package_parser.processing.annotations.model import (
2+
AbstractAnnotation,
3+
BoundaryAnnotation,
4+
CalledAfterAnnotation,
5+
ConstantAnnotation,
6+
DescriptionAnnotation,
7+
EnumAnnotation,
8+
ExpertAnnotation,
9+
GroupAnnotation,
10+
MoveAnnotation,
11+
OmittedAnnotation,
12+
OptionalAnnotation,
13+
RemoveAnnotation,
14+
RenameAnnotation,
15+
RequiredAnnotation,
16+
TodoAnnotation,
17+
ValueAnnotation,
18+
)
19+
20+
21+
def are_semantic_equal(
22+
annotation_a: AbstractAnnotation, annotation_b: AbstractAnnotation
23+
) -> bool:
24+
if (
25+
annotation_a.target == annotation_b.target
26+
and isinstance(annotation_a, type(annotation_b))
27+
and isinstance(annotation_b, type(annotation_a))
28+
):
29+
if isinstance(annotation_a, BoundaryAnnotation) and isinstance(
30+
annotation_b, BoundaryAnnotation
31+
):
32+
return annotation_a.interval == annotation_b.interval
33+
if isinstance(annotation_a, CalledAfterAnnotation) and isinstance(
34+
annotation_b, CalledAfterAnnotation
35+
):
36+
return annotation_a.calledAfterName == annotation_b.calledAfterName
37+
if isinstance(annotation_a, DescriptionAnnotation) and isinstance(
38+
annotation_b, DescriptionAnnotation
39+
):
40+
return annotation_a.newDescription == annotation_b.newDescription
41+
if (
42+
isinstance(annotation_a, EnumAnnotation)
43+
and isinstance(annotation_b, EnumAnnotation)
44+
and annotation_a.enumName == annotation_b.enumName
45+
and len(annotation_a.pairs) == len(annotation_a.pairs)
46+
):
47+
list_a = sorted(list(annotation_a.pairs), key=lambda x: x.stringValue)
48+
list_b = sorted(list(annotation_b.pairs), key=lambda x: x.stringValue)
49+
for i in range(len(annotation_a.pairs)):
50+
if (
51+
list_a[i].stringValue != list_b[i].stringValue
52+
or list_a[i].instanceName != list_b[i].instanceName
53+
):
54+
return False
55+
return True
56+
if isinstance(annotation_a, ExpertAnnotation) and isinstance(
57+
annotation_b, ExpertAnnotation
58+
):
59+
return True
60+
if isinstance(annotation_a, GroupAnnotation) and isinstance(
61+
annotation_b, GroupAnnotation
62+
):
63+
return annotation_a.groupName == annotation_b.groupName and set(
64+
annotation_a.parameters
65+
) == set(annotation_b.parameters)
66+
if isinstance(annotation_a, MoveAnnotation) and isinstance(
67+
annotation_b, MoveAnnotation
68+
):
69+
return annotation_a.destination == annotation_b.destination
70+
if isinstance(annotation_a, RemoveAnnotation) and isinstance(
71+
annotation_b, RemoveAnnotation
72+
):
73+
return True
74+
if isinstance(annotation_a, RenameAnnotation) and isinstance(
75+
annotation_b, RenameAnnotation
76+
):
77+
return annotation_a.newName == annotation_b.newName
78+
if isinstance(annotation_a, TodoAnnotation) and isinstance(
79+
annotation_b, TodoAnnotation
80+
):
81+
return annotation_a.newTodo == annotation_b.newTodo
82+
if (
83+
isinstance(annotation_a, ValueAnnotation)
84+
and isinstance(annotation_b, ValueAnnotation)
85+
and annotation_a.variant == annotation_b.variant
86+
):
87+
if isinstance(annotation_a, ConstantAnnotation) and isinstance(
88+
annotation_b, ConstantAnnotation
89+
):
90+
return (
91+
annotation_a.defaultValue == annotation_b.defaultValue
92+
and annotation_a.defaultValueType == annotation_b.defaultValueType
93+
)
94+
if isinstance(annotation_a, OptionalAnnotation) and isinstance(
95+
annotation_b, OptionalAnnotation
96+
):
97+
return (
98+
annotation_a.defaultValue == annotation_b.defaultValue
99+
and annotation_a.defaultValueType == annotation_b.defaultValueType
100+
)
101+
if isinstance(annotation_a, OmittedAnnotation) and isinstance(
102+
annotation_b, OmittedAnnotation
103+
):
104+
return True
105+
if isinstance(annotation_a, RequiredAnnotation) and isinstance(
106+
annotation_b, RequiredAnnotation
107+
):
108+
return True
109+
return False

package-parser/package_parser/processing/annotations/model/_annotations.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,18 @@ def from_json(json: Any) -> Interval:
8282
json["upperLimitType"],
8383
)
8484

85+
def __eq__(self, other: Any) -> bool:
86+
return (
87+
isinstance(other, Interval)
88+
and self.isDiscrete == other.isDiscrete
89+
and self.lowerIntervalLimit == other.lowerIntervalLimit
90+
and isinstance(self.lowerIntervalLimit, type(self.lowerIntervalLimit))
91+
and self.lowerLimitType == other.lowerLimitType
92+
and self.upperIntervalLimit == other.upperIntervalLimit
93+
and isinstance(self.upperIntervalLimit, type(self.upperIntervalLimit))
94+
and self.upperLimitType == self.upperLimitType
95+
)
96+
8597

8698
@dataclass
8799
class BoundaryAnnotation(AbstractAnnotation):
@@ -122,6 +134,13 @@ def to_json(self) -> dict:
122134
def from_json(json: Any) -> EnumPair:
123135
return EnumPair(json["stringValue"], json["instanceName"])
124136

137+
def __eq__(self, other: Any) -> bool:
138+
return (
139+
isinstance(other, EnumPair)
140+
and self.stringValue == other.stringValue
141+
and self.instanceName == other.instanceName
142+
)
143+
125144

126145
@dataclass
127146
class EnumAnnotation(AbstractAnnotation):
@@ -311,7 +330,9 @@ class ParameterInfo:
311330
value: str
312331
value_type: str
313332

314-
def __init__(self, parameter_type, value="", value_type=""):
333+
def __init__(
334+
self, parameter_type: ParameterType, value: str = "", value_type: str = ""
335+
) -> None:
315336
self.type = parameter_type
316337
self.value = value
317338
self.value_type = value_type

package-parser/package_parser/processing/migration/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@
99
SimpleDiffer,
1010
)
1111

12-
from ._migrate import migrate_annotations
12+
from ._migrate import Migration

0 commit comments

Comments
 (0)