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

Commit b2cf4bc

Browse files
feat: migrate @rename annotations (#1123)
Closes #1120. ### Summary of Changes The migrate command for rename annotation is now working: If an annotation is mapped via an `One/ManyToOneMapping`, a new remove annotation would be created for this one apiv2 element. Otherwise, there are two options: 1. If the same name is found in the mapping, an unsure `@remove` annotation would be created for this element. 2. Else, all apiv2 elements will be annotated with a `@todo` annotation. ### Testing Instructions run the migrate command or view and run the test_migration.py file Signed-off-by: Aclrian <[email protected]> Co-authored-by: Lars Reimann <[email protected]> Co-authored-by: Aclrian <[email protected]> Co-authored-by: lars-reimann <[email protected]>
1 parent 3cac0fa commit b2cf4bc

File tree

16 files changed

+252758
-73
lines changed

16 files changed

+252758
-73
lines changed

data/api/scikit-learn_v0.24.2_api.json

Lines changed: 252312 additions & 0 deletions
Large diffs are not rendered by default.

package-parser/README.md

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

33
A tool to analyze client and API code written in Python.
44

5-
### Installation
5+
## Installation
66

77
1. Install Python 3.10.
88
2. Install [poetry](https://python-poetry.org/docs/master/#installation).
@@ -15,7 +15,7 @@ A tool to analyze client and API code written in Python.
1515
poetry shell
1616
```
1717

18-
### Example usage
18+
## Example usage
1919

2020
1. Analyze an API:
2121
```shell
@@ -32,5 +32,5 @@ A tool to analyze client and API code written in Python.
3232
```
3333
4. Migrate annotations for a new version of the API:
3434
```shell
35-
parse-package migrate -a1 data/api/sklearn__api.json -a2 data/api/sklearn__apiv2.json -a data/annotations/annotations.json -o out
35+
parse-package migrate -a1 data/api/scikit-learn_v0.24.2_api.json -a2 data/api/sklearn__apiv2.json -a data/annotations/annotations.json -o out
3636
```
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import json
2+
from pathlib import Path
3+
4+
from package_parser.cli._json_encoder import CustomEncoder
5+
from package_parser.processing.annotations.model import AnnotationStore
6+
from package_parser.processing.api.model import API
7+
from package_parser.processing.usages.model import UsageCountStore
8+
from package_parser.utils import ensure_file_exists
9+
10+
11+
def _read_annotations_file(annotations_file_path: Path) -> AnnotationStore:
12+
with open(annotations_file_path, encoding="utf-8") as annotations_file:
13+
annotations_json = json.load(annotations_file)
14+
15+
return AnnotationStore.from_json(annotations_json)
16+
17+
18+
def _write_annotations_file(
19+
annotations: AnnotationStore, annotations_file_path: Path
20+
) -> None:
21+
ensure_file_exists(annotations_file_path)
22+
with annotations_file_path.open("w", encoding="utf-8") as f:
23+
json.dump(annotations.to_json(), f, indent=2)
24+
25+
26+
def _read_api_file(api_file_path: Path) -> API:
27+
with open(api_file_path, encoding="utf-8") as api_file:
28+
api_json = json.load(api_file)
29+
30+
return API.from_json(api_json)
31+
32+
33+
def _read_usages_file(usages_file_path: Path) -> UsageCountStore:
34+
with open(usages_file_path, encoding="utf-8") as usages_file:
35+
usages_json = json.load(usages_file)
36+
37+
return UsageCountStore.from_json(usages_json)
38+
39+
40+
def _write_api_file(api: API, out_dir_path: Path) -> Path:
41+
out_file_api = out_dir_path.joinpath(f"{api.package}__api.json")
42+
ensure_file_exists(out_file_api)
43+
with out_file_api.open("w", encoding="utf-8") as f:
44+
json.dump(api.to_json(), f, indent=2, cls=CustomEncoder)
45+
return out_file_api
46+
47+
48+
def _write_api_dependency_file(api: API, api_dependencies, out):
49+
out_file_api_dependencies = out.joinpath(f"{api.package}__api_dependencies.json")
50+
ensure_file_exists(out_file_api_dependencies)
51+
with out_file_api_dependencies.open("w") as f:
52+
json.dump(api_dependencies.to_json(), f, indent=2, cls=CustomEncoder)
Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import json
21
from pathlib import Path
32

43
from package_parser.processing.annotations import generate_annotations
5-
from package_parser.processing.annotations.model import AnnotationStore
6-
from package_parser.processing.api.model import API
7-
from package_parser.processing.usages.model import UsageCountStore
8-
from package_parser.utils import ensure_file_exists
4+
5+
from ._read_and_write_file import (
6+
_read_api_file,
7+
_read_usages_file,
8+
_write_annotations_file,
9+
)
910

1011

1112
def _run_annotations(
@@ -23,25 +24,3 @@ def _run_annotations(
2324
usages = _read_usages_file(usages_file_path)
2425
annotations = generate_annotations(api, usages)
2526
_write_annotations_file(annotations, annotations_file_path)
26-
27-
28-
def _read_api_file(api_file_path: Path) -> API:
29-
with open(api_file_path) as api_file:
30-
api_json = json.load(api_file)
31-
32-
return API.from_json(api_json)
33-
34-
35-
def _read_usages_file(usages_file_path: Path) -> UsageCountStore:
36-
with open(usages_file_path) as usages_file:
37-
usages_json = json.load(usages_file)
38-
39-
return UsageCountStore.from_json(usages_json)
40-
41-
42-
def _write_annotations_file(
43-
annotations: AnnotationStore, annotations_file_path: Path
44-
) -> None:
45-
ensure_file_exists(annotations_file_path)
46-
with annotations_file_path.open("w") as f:
47-
json.dump(annotations.to_json(), f, indent=2)
Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
import json
21
from pathlib import Path
32
from typing import Optional
43

5-
from package_parser.cli._json_encoder import CustomEncoder
6-
from package_parser.cli._shared_constants import _API_KEY
74
from package_parser.processing.api import get_api
8-
from package_parser.processing.api.model import API
95
from package_parser.processing.dependencies import get_dependencies
10-
from package_parser.utils import ensure_file_exists
6+
7+
from ._read_and_write_file import _write_api_dependency_file, _write_api_file
8+
from ._shared_constants import _API_KEY
119

1210

1311
def _run_api_command(
@@ -24,18 +22,3 @@ def _run_api_command(
2422

2523
if result_dict is not None:
2624
result_dict[_API_KEY] = api_file_path
27-
28-
29-
def _write_api_file(api: API, out_dir_path: Path) -> Path:
30-
out_file_api = out_dir_path.joinpath(f"{api.package}__api.json")
31-
ensure_file_exists(out_file_api)
32-
with out_file_api.open("w") as f:
33-
json.dump(api.to_json(), f, indent=2, cls=CustomEncoder)
34-
return out_file_api
35-
36-
37-
def _write_api_dependency_file(api: API, api_dependencies, out):
38-
out_file_api_dependencies = out.joinpath(f"{api.package}__api_dependencies.json")
39-
ensure_file_exists(out_file_api_dependencies)
40-
with out_file_api_dependencies.open("w") as f:
41-
json.dump(api_dependencies.to_json(), f, indent=2, cls=CustomEncoder)
Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1-
import json
21
from pathlib import Path
32

4-
from package_parser.processing.annotations.model import AnnotationStore
5-
from package_parser.processing.api.model import API
3+
from package_parser.processing.migration import migrate_annotations
4+
from package_parser.processing.migration.model import APIMapping, SimpleDiffer
5+
6+
from ._read_and_write_file import (
7+
_read_annotations_file,
8+
_read_api_file,
9+
_write_annotations_file,
10+
)
611

712

813
def _run_migrate_command(
@@ -11,21 +16,11 @@ def _run_migrate_command(
1116
apiv2_file_path: Path,
1217
out_dir_path: Path,
1318
) -> None:
14-
# pylint: disable=unused-argument
15-
_read_api_file(apiv1_file_path)
16-
_read_api_file(apiv2_file_path)
17-
_read_annotations_file(annotations_file_path)
18-
19-
20-
def _read_annotations_file(annotations_file_path: Path) -> AnnotationStore:
21-
with open(annotations_file_path, encoding="utf-8") as annotations_file:
22-
annotations_json = json.load(annotations_file)
23-
24-
return AnnotationStore.from_json(annotations_json)
25-
26-
27-
def _read_api_file(api_file_path: Path) -> API:
28-
with open(api_file_path, encoding="utf-8") as api_file:
29-
api_json = json.load(api_file)
30-
31-
return API.from_json(api_json)
19+
apiv1 = _read_api_file(apiv1_file_path)
20+
apiv2 = _read_api_file(apiv2_file_path)
21+
annotationsv1 = _read_annotations_file(annotations_file_path)
22+
differ = SimpleDiffer()
23+
api_mapping = APIMapping(apiv1, apiv2, differ)
24+
mappings = api_mapping.map_api()
25+
annotationsv2 = migrate_annotations(annotationsv1, mappings)
26+
_write_annotations_file(annotationsv2, out_dir_path)

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from ._annotations import (
77
ANNOTATION_SCHEMA_VERSION,
8+
AbstractAnnotation,
89
BoundaryAnnotation,
910
CalledAfterAnnotation,
1011
CompleteAnnotation,
@@ -105,6 +106,34 @@ def from_json(json: Any) -> AnnotationStore:
105106
valueAnnotations,
106107
)
107108

109+
def add_annotation(self, annotation: AbstractAnnotation):
110+
if isinstance(annotation, BoundaryAnnotation):
111+
self.boundaryAnnotations.append(annotation)
112+
if isinstance(annotation, BoundaryAnnotation):
113+
self.boundaryAnnotations.append(annotation)
114+
if isinstance(annotation, CalledAfterAnnotation):
115+
self.calledAfterAnnotations.append(annotation)
116+
if isinstance(annotation, CompleteAnnotation):
117+
self.completeAnnotations.append(annotation)
118+
if isinstance(annotation, DescriptionAnnotation):
119+
self.descriptionAnnotations.append(annotation)
120+
if isinstance(annotation, EnumAnnotation):
121+
self.enumAnnotations.append(annotation)
122+
if isinstance(annotation, GroupAnnotation):
123+
self.groupAnnotations.append(annotation)
124+
if isinstance(annotation, MoveAnnotation):
125+
self.moveAnnotations.append(annotation)
126+
if isinstance(annotation, PureAnnotation):
127+
self.pureAnnotations.append(annotation)
128+
if isinstance(annotation, RemoveAnnotation):
129+
self.removeAnnotations.append(annotation)
130+
if isinstance(annotation, RenameAnnotation):
131+
self.renameAnnotations.append(annotation)
132+
if isinstance(annotation, TodoAnnotation):
133+
self.todoAnnotations.append(annotation)
134+
if isinstance(annotation, ValueAnnotation):
135+
self.valueAnnotations.append(annotation)
136+
108137
def to_json(self) -> dict:
109138
return {
110139
"schemaVersion": ANNOTATION_SCHEMA_VERSION,
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
from ._differ import AbstractDiffer, SimpleDiffer
2-
from ._mapping import (
1+
from package_parser.processing.migration.model import (
2+
AbstractDiffer,
33
APIMapping,
44
ManyToManyMapping,
55
ManyToOneMapping,
66
Mapping,
77
OneToManyMapping,
88
OneToOneMapping,
9+
SimpleDiffer,
910
)
11+
12+
from ._migrate import migrate_annotations
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from typing import Optional
2+
3+
from package_parser.processing.annotations.model import (
4+
AbstractAnnotation,
5+
AnnotationStore,
6+
)
7+
from package_parser.processing.api.model import Attribute, Result
8+
from package_parser.processing.migration.annotations import migrate_rename_annotation
9+
from package_parser.processing.migration.model import Mapping
10+
11+
12+
def _get_mapping_from_annotation(
13+
annotation: AbstractAnnotation, mappings: list[Mapping]
14+
) -> Optional[Mapping]:
15+
for mapping in mappings:
16+
for element in mapping.get_apiv1_elements():
17+
if (
18+
not isinstance(element, (Attribute, Result))
19+
and element.id == annotation.target
20+
):
21+
return mapping
22+
return None
23+
24+
25+
def migrate_annotations(
26+
annotationsv1: AnnotationStore, mappings: list[Mapping]
27+
) -> AnnotationStore:
28+
migrated_annotation_store = AnnotationStore()
29+
30+
for rename_annotation in annotationsv1.renameAnnotations:
31+
mapping = _get_mapping_from_annotation(rename_annotation, mappings)
32+
if mapping is not None:
33+
for annotation in migrate_rename_annotation(rename_annotation, mapping):
34+
migrated_annotation_store.add_annotation(annotation)
35+
36+
return migrated_annotation_store
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from ._constants import migration_author
2+
from ._migrate_rename_annotation import migrate_rename_annotation

0 commit comments

Comments
 (0)