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

Commit eddb597

Browse files
authored
feat: migrate @enum annotations (#1126)
Closes #1125. ### Summary of Changes - fix mypy error (hopefully) - add migration for enum annotations to another version, with the possibilities: - If the annotation is mapped to a parameter and its type is string and the default string value (if any) is a value expected by the annotation, the annotation will be migrated as expected. - If the annotation is mapped to a parameter and its type is known, but not a string, we would assume that the problem of a `@enum` annotation is solved. E.g., an enum type is created for this parameter. - Otherwise, a `@todo` annotation will be crated instead of the `@enum` annotation. ### Testing Instructions run the migrate command or view and run the test_migration.py file
1 parent 2410aa1 commit eddb597

File tree

7 files changed

+595
-199
lines changed

7 files changed

+595
-199
lines changed

.mypy.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ disallow_untyped_calls = true
44
disallow_untyped_decorators = true
55
disallow_untyped_defs = true
66
ignore_missing_imports = true
7-
no_site_packages = true
7+
no_namespace_packages = true

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
AnnotationStore,
66
)
77
from package_parser.processing.api.model import Attribute, Result
8-
from package_parser.processing.migration.annotations import migrate_rename_annotation
8+
from package_parser.processing.migration.annotations import (
9+
migrate_enum_annotation,
10+
migrate_rename_annotation,
11+
)
912
from package_parser.processing.migration.model import Mapping
1013

1114

@@ -27,6 +30,12 @@ def migrate_annotations(
2730
) -> AnnotationStore:
2831
migrated_annotation_store = AnnotationStore()
2932

33+
for enum_annotation in annotationsv1.enumAnnotations:
34+
mapping = _get_mapping_from_annotation(enum_annotation, mappings)
35+
if mapping is not None:
36+
for annotation in migrate_enum_annotation(enum_annotation, mapping):
37+
migrated_annotation_store.add_annotation(annotation)
38+
3039
for rename_annotation in annotationsv1.renameAnnotations:
3140
mapping = _get_mapping_from_annotation(rename_annotation, mappings)
3241
if mapping is not None:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
from ._constants import migration_author
2+
from ._migrate_enum_annotation import migrate_enum_annotation
23
from ._migrate_rename_annotation import migrate_rename_annotation
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
from copy import deepcopy
2+
from typing import List, Optional
3+
4+
from package_parser.processing.annotations.model import (
5+
AbstractAnnotation,
6+
EnumAnnotation,
7+
EnumPair,
8+
EnumReviewResult,
9+
TodoAnnotation,
10+
)
11+
from package_parser.processing.api.model import (
12+
AbstractType,
13+
Attribute,
14+
NamedType,
15+
Parameter,
16+
Result,
17+
UnionType,
18+
)
19+
from package_parser.processing.migration.model import (
20+
ManyToManyMapping,
21+
ManyToOneMapping,
22+
Mapping,
23+
OneToManyMapping,
24+
OneToOneMapping,
25+
)
26+
27+
from ._constants import migration_author
28+
29+
30+
def _contains_string(type_: AbstractType) -> bool:
31+
if isinstance(type_, NamedType):
32+
return type_.name == "str"
33+
if isinstance(type_, UnionType):
34+
for element in type_.types:
35+
if _contains_string(element):
36+
return True
37+
return False
38+
39+
40+
def _default_value_is_in_instance_values_or_is_empty(
41+
default_value: Optional[str], pairs: List[EnumPair]
42+
) -> bool:
43+
return (
44+
default_value is None
45+
or default_value in map(lambda pair: pair.stringValue, pairs)
46+
or len(default_value) == 0
47+
)
48+
49+
50+
def migrate_enum_annotation(
51+
enum_annotation: EnumAnnotation, mapping: Mapping
52+
) -> list[AbstractAnnotation]:
53+
enum_annotation = deepcopy(enum_annotation)
54+
authors = enum_annotation.authors
55+
authors.append(migration_author)
56+
enum_annotation.authors = authors
57+
58+
migrate_text = (
59+
"The @Enum Annotation with the new name '"
60+
+ enum_annotation.enumName
61+
+ " ("
62+
+ ", ".join(
63+
map(
64+
lambda enum_pair: enum_pair.stringValue + ", " + enum_pair.instanceName,
65+
enum_annotation.pairs,
66+
)
67+
)
68+
+ ")' from the previous version was at '"
69+
+ enum_annotation.target
70+
+ "' and the possible alternatives in the new version of the api are: "
71+
+ ", ".join(
72+
map(lambda api_element: api_element.name, mapping.get_apiv2_elements())
73+
)
74+
)
75+
76+
if isinstance(mapping, (OneToOneMapping, ManyToOneMapping)):
77+
parameter = mapping.get_apiv2_elements()[0]
78+
if isinstance(parameter, (Attribute, Result)):
79+
return []
80+
if isinstance(parameter, Parameter):
81+
if parameter.type is not None:
82+
if _contains_string(
83+
parameter.type
84+
) and _default_value_is_in_instance_values_or_is_empty(
85+
parameter.default_value, enum_annotation.pairs
86+
):
87+
enum_annotation.target = parameter.id
88+
return [enum_annotation]
89+
if isinstance(parameter.type, NamedType):
90+
# assuming api has been chanced to an enum type:
91+
# do not migrate annotation
92+
return []
93+
else:
94+
enum_annotation.reviewResult = EnumReviewResult.UNSURE
95+
return [enum_annotation]
96+
return [
97+
TodoAnnotation(
98+
parameter.id, authors, [], "", EnumReviewResult.NONE, migrate_text
99+
)
100+
]
101+
102+
migrated_annotations: list[AbstractAnnotation] = []
103+
if isinstance(mapping, (OneToManyMapping, ManyToManyMapping)):
104+
for parameter in mapping.get_apiv2_elements():
105+
if isinstance(parameter, Parameter):
106+
if parameter.type is not None:
107+
if _contains_string(
108+
parameter.type
109+
) and _default_value_is_in_instance_values_or_is_empty(
110+
parameter.default_value, enum_annotation.pairs
111+
):
112+
migrated_annotations.append(
113+
EnumAnnotation(
114+
parameter.id,
115+
authors,
116+
[],
117+
"",
118+
EnumReviewResult.NONE,
119+
enum_annotation.enumName,
120+
enum_annotation.pairs,
121+
)
122+
)
123+
elif isinstance(parameter.type, NamedType):
124+
continue
125+
else:
126+
migrated_annotations.append(
127+
TodoAnnotation(
128+
parameter.id,
129+
authors,
130+
[],
131+
"",
132+
EnumReviewResult.UNSURE,
133+
migrate_text,
134+
)
135+
)
136+
return migrated_annotations
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
from typing import Tuple
2+
3+
from package_parser.processing.annotations.model import (
4+
AbstractAnnotation,
5+
EnumAnnotation,
6+
EnumPair,
7+
EnumReviewResult,
8+
TodoAnnotation,
9+
)
10+
from package_parser.processing.api.model import (
11+
Parameter,
12+
ParameterAssignment,
13+
ParameterDocumentation,
14+
)
15+
from package_parser.processing.migration import (
16+
Mapping,
17+
OneToManyMapping,
18+
OneToOneMapping,
19+
)
20+
from package_parser.processing.migration.annotations import migration_author
21+
22+
23+
def migrate_enum_annotation_data_one_to_one_mapping() -> Tuple[
24+
Mapping,
25+
AbstractAnnotation,
26+
list[AbstractAnnotation],
27+
]:
28+
parameterv1 = Parameter(
29+
id_="test/test.enum.test1.TestA",
30+
name="TestA",
31+
qname="test.enum.test1.TestA",
32+
default_value="value",
33+
assigned_by=ParameterAssignment.POSITION_OR_NAME,
34+
is_public=True,
35+
documentation=ParameterDocumentation("str", "value", "docstring"),
36+
)
37+
parameterv2 = Parameter(
38+
id_="test/test.enum.test1.TestB",
39+
name="TestB",
40+
qname="test.enum.test1.TestB",
41+
default_value="value",
42+
assigned_by=ParameterAssignment.POSITION_OR_NAME,
43+
is_public=True,
44+
documentation=ParameterDocumentation("str", "value", "docstring"),
45+
)
46+
mapping = OneToOneMapping(1.0, parameterv1, parameterv2)
47+
enum_annotation = EnumAnnotation(
48+
target="test/test.enum.test1.TestA",
49+
authors=["testauthor"],
50+
reviewers=[],
51+
comment="",
52+
reviewResult=EnumReviewResult.NONE,
53+
enumName="EnumName",
54+
pairs=[EnumPair("value", "name")],
55+
)
56+
migrated_enum_annotation = EnumAnnotation(
57+
target="test/test.enum.test1.TestB",
58+
authors=["testauthor", migration_author],
59+
reviewers=[],
60+
comment="",
61+
reviewResult=EnumReviewResult.NONE,
62+
enumName="EnumName",
63+
pairs=[EnumPair("value", "name")],
64+
)
65+
return mapping, enum_annotation, [migrated_enum_annotation]
66+
67+
68+
def migrate_enum_annotation_data_one_to_many_mapping() -> Tuple[
69+
Mapping,
70+
AbstractAnnotation,
71+
list[AbstractAnnotation],
72+
]:
73+
parameterv1 = Parameter(
74+
id_="test/test.enum.test2.Test",
75+
name="Test",
76+
qname="test.enum.test2.Test",
77+
default_value="value",
78+
assigned_by=ParameterAssignment.POSITION_OR_NAME,
79+
is_public=True,
80+
documentation=ParameterDocumentation("str", "value", "docstring"),
81+
)
82+
parameterv2_a = Parameter(
83+
id_="test/test.enum.test2.TestA",
84+
name="TestA",
85+
qname="test.enum.test2.TestA",
86+
default_value="value",
87+
assigned_by=ParameterAssignment.POSITION_OR_NAME,
88+
is_public=True,
89+
documentation=ParameterDocumentation("str", "value", "docstring"),
90+
)
91+
parameterv2_b = Parameter(
92+
id_="test/test.enum.test2.TestB",
93+
name="TestB",
94+
qname="test.enum.test2.TestB",
95+
default_value="value",
96+
assigned_by=ParameterAssignment.POSITION_OR_NAME,
97+
is_public=True,
98+
documentation=ParameterDocumentation("str", "value", "docstring"),
99+
)
100+
mapping = OneToManyMapping(1.0, parameterv1, [parameterv2_a, parameterv2_b])
101+
enum_annotation = EnumAnnotation(
102+
target="test/test.enum.test2.Test",
103+
authors=["testauthor"],
104+
reviewers=[],
105+
comment="",
106+
reviewResult=EnumReviewResult.NONE,
107+
enumName="EnumName",
108+
pairs=[EnumPair("value", "name")],
109+
)
110+
migrated_enum_annotation = EnumAnnotation(
111+
target="test/test.enum.test2.TestA",
112+
authors=["testauthor", migration_author],
113+
reviewers=[],
114+
comment="",
115+
reviewResult=EnumReviewResult.NONE,
116+
enumName="EnumName",
117+
pairs=[EnumPair("value", "name")],
118+
)
119+
migrated_enum_annotation_2 = EnumAnnotation(
120+
target="test/test.enum.test2.TestB",
121+
authors=["testauthor", migration_author],
122+
reviewers=[],
123+
comment="",
124+
reviewResult=EnumReviewResult.NONE,
125+
enumName="EnumName",
126+
pairs=[EnumPair("value", "name")],
127+
)
128+
return (
129+
mapping,
130+
enum_annotation,
131+
[migrated_enum_annotation, migrated_enum_annotation_2],
132+
)
133+
134+
135+
def migrate_enum_annotation_data_one_to_many_mapping__only_one_relevant_mapping() -> Tuple[
136+
Mapping,
137+
AbstractAnnotation,
138+
list[AbstractAnnotation],
139+
]:
140+
parameterv1 = Parameter(
141+
id_="test/test.enum.test3.Test",
142+
name="Test",
143+
qname="test.enum.test3.Test",
144+
default_value="value",
145+
assigned_by=ParameterAssignment.POSITION_OR_NAME,
146+
is_public=True,
147+
documentation=ParameterDocumentation("str", "value", "docstring"),
148+
)
149+
parameterv2_a = Parameter(
150+
id_="test/test.enum.test3.TestA",
151+
name="TestA",
152+
qname="test.enum.test3.TestA",
153+
default_value="",
154+
assigned_by=ParameterAssignment.POSITION_OR_NAME,
155+
is_public=True,
156+
documentation=ParameterDocumentation("", "", ""),
157+
)
158+
parameterv2_b = Parameter(
159+
id_="test/test.enum.test3.TestB",
160+
name="TestB",
161+
qname="test.enum.test3.TestB",
162+
default_value="value",
163+
assigned_by=ParameterAssignment.POSITION_OR_NAME,
164+
is_public=True,
165+
documentation=ParameterDocumentation("str", "value", "docstring"),
166+
)
167+
parameterv2_c = Parameter(
168+
id_="test/test.enum.test3.TestC",
169+
name="TestC",
170+
qname="test.enum.test3.TestC",
171+
default_value="0",
172+
assigned_by=ParameterAssignment.POSITION_OR_NAME,
173+
is_public=True,
174+
documentation=ParameterDocumentation("int", "0", "docstring"),
175+
)
176+
mapping = OneToManyMapping(
177+
1.0, parameterv1, [parameterv2_a, parameterv2_b, parameterv2_c]
178+
)
179+
enum_annotation = EnumAnnotation(
180+
target="test/test.enum.test3.Test",
181+
authors=["testauthor"],
182+
reviewers=[],
183+
comment="",
184+
reviewResult=EnumReviewResult.NONE,
185+
enumName="EnumName",
186+
pairs=[EnumPair("value", "name")],
187+
)
188+
migrated_enum_annotation = EnumAnnotation(
189+
target="test/test.enum.test3.TestB",
190+
authors=["testauthor", migration_author],
191+
reviewers=[],
192+
reviewResult=EnumReviewResult.NONE,
193+
comment="",
194+
enumName="EnumName",
195+
pairs=[EnumPair("value", "name")],
196+
)
197+
migrated_todo_annotation = TodoAnnotation(
198+
target="test/test.enum.test3.TestA",
199+
authors=["testauthor", migration_author],
200+
reviewers=[],
201+
reviewResult=EnumReviewResult.UNSURE,
202+
comment="",
203+
newTodo="The @Enum Annotation with the new name 'EnumName "
204+
"(value, name)' from the previous version was at "
205+
"'test/test.enum.test3.Test' and the possible "
206+
"alternatives in the new version of the api are: "
207+
"TestA, TestB, TestC",
208+
)
209+
return (
210+
mapping,
211+
enum_annotation,
212+
[migrated_enum_annotation, migrated_todo_annotation],
213+
)

0 commit comments

Comments
 (0)