diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2384a7835..96332d731 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -97,6 +97,9 @@ jobs: - name: Smoke test (all) run: poetry run parse-package all -p package_parser -s package_parser -c package_parser -o out + - name: Smoke test (migration) + 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 + # Requires installation of pytest and pytest-cov - name: Test with pytest run: poetry run pytest --doctest-modules diff --git a/package-parser/package_parser/cli/_run_migrate.py b/package-parser/package_parser/cli/_run_migrate.py index 1d7027af2..26be25c5e 100644 --- a/package-parser/package_parser/cli/_run_migrate.py +++ b/package-parser/package_parser/cli/_run_migrate.py @@ -1,6 +1,7 @@ +import os from pathlib import Path -from package_parser.processing.migration import migrate_annotations +from package_parser.processing.migration import Migration from package_parser.processing.migration.model import APIMapping, SimpleDiffer from ._read_and_write_file import ( @@ -22,5 +23,19 @@ def _run_migrate_command( differ = SimpleDiffer() api_mapping = APIMapping(apiv1, apiv2, differ) mappings = api_mapping.map_api() - annotationsv2 = migrate_annotations(annotationsv1, mappings) - _write_annotations_file(annotationsv2, out_dir_path) + migration = Migration(annotationsv1, mappings) + migration.migrate_annotations() + migrated_annotations_file = Path( + os.path.join(out_dir_path, "migrated_annotationsv" + apiv2.version + ".json") + ) + unsure_migrated_annotations_file = Path( + os.path.join( + out_dir_path, "unsure_migrated_annotationsv" + apiv2.version + ".json" + ) + ) + _write_annotations_file( + migration.migrated_annotation_store, migrated_annotations_file + ) + _write_annotations_file( + migration.unsure_migrated_annotation_store, unsure_migrated_annotations_file + ) diff --git a/package-parser/package_parser/processing/annotations/__init__.py b/package-parser/package_parser/processing/annotations/__init__.py index c4bf3b4d8..3cc45dd44 100644 --- a/package-parser/package_parser/processing/annotations/__init__.py +++ b/package-parser/package_parser/processing/annotations/__init__.py @@ -1 +1,2 @@ +from ._are_semantic_equal import are_semantic_equal from ._generate_annotations import generate_annotations diff --git a/package-parser/package_parser/processing/annotations/_are_semantic_equal.py b/package-parser/package_parser/processing/annotations/_are_semantic_equal.py new file mode 100644 index 000000000..54438299e --- /dev/null +++ b/package-parser/package_parser/processing/annotations/_are_semantic_equal.py @@ -0,0 +1,109 @@ +from package_parser.processing.annotations.model import ( + AbstractAnnotation, + BoundaryAnnotation, + CalledAfterAnnotation, + ConstantAnnotation, + DescriptionAnnotation, + EnumAnnotation, + ExpertAnnotation, + GroupAnnotation, + MoveAnnotation, + OmittedAnnotation, + OptionalAnnotation, + RemoveAnnotation, + RenameAnnotation, + RequiredAnnotation, + TodoAnnotation, + ValueAnnotation, +) + + +def are_semantic_equal( + annotation_a: AbstractAnnotation, annotation_b: AbstractAnnotation +) -> bool: + if ( + annotation_a.target == annotation_b.target + and isinstance(annotation_a, type(annotation_b)) + and isinstance(annotation_b, type(annotation_a)) + ): + if isinstance(annotation_a, BoundaryAnnotation) and isinstance( + annotation_b, BoundaryAnnotation + ): + return annotation_a.interval == annotation_b.interval + if isinstance(annotation_a, CalledAfterAnnotation) and isinstance( + annotation_b, CalledAfterAnnotation + ): + return annotation_a.calledAfterName == annotation_b.calledAfterName + if isinstance(annotation_a, DescriptionAnnotation) and isinstance( + annotation_b, DescriptionAnnotation + ): + return annotation_a.newDescription == annotation_b.newDescription + if ( + isinstance(annotation_a, EnumAnnotation) + and isinstance(annotation_b, EnumAnnotation) + and annotation_a.enumName == annotation_b.enumName + and len(annotation_a.pairs) == len(annotation_a.pairs) + ): + list_a = sorted(list(annotation_a.pairs), key=lambda x: x.stringValue) + list_b = sorted(list(annotation_b.pairs), key=lambda x: x.stringValue) + for i in range(len(annotation_a.pairs)): + if ( + list_a[i].stringValue != list_b[i].stringValue + or list_a[i].instanceName != list_b[i].instanceName + ): + return False + return True + if isinstance(annotation_a, ExpertAnnotation) and isinstance( + annotation_b, ExpertAnnotation + ): + return True + if isinstance(annotation_a, GroupAnnotation) and isinstance( + annotation_b, GroupAnnotation + ): + return annotation_a.groupName == annotation_b.groupName and set( + annotation_a.parameters + ) == set(annotation_b.parameters) + if isinstance(annotation_a, MoveAnnotation) and isinstance( + annotation_b, MoveAnnotation + ): + return annotation_a.destination == annotation_b.destination + if isinstance(annotation_a, RemoveAnnotation) and isinstance( + annotation_b, RemoveAnnotation + ): + return True + if isinstance(annotation_a, RenameAnnotation) and isinstance( + annotation_b, RenameAnnotation + ): + return annotation_a.newName == annotation_b.newName + if isinstance(annotation_a, TodoAnnotation) and isinstance( + annotation_b, TodoAnnotation + ): + return annotation_a.newTodo == annotation_b.newTodo + if ( + isinstance(annotation_a, ValueAnnotation) + and isinstance(annotation_b, ValueAnnotation) + and annotation_a.variant == annotation_b.variant + ): + if isinstance(annotation_a, ConstantAnnotation) and isinstance( + annotation_b, ConstantAnnotation + ): + return ( + annotation_a.defaultValue == annotation_b.defaultValue + and annotation_a.defaultValueType == annotation_b.defaultValueType + ) + if isinstance(annotation_a, OptionalAnnotation) and isinstance( + annotation_b, OptionalAnnotation + ): + return ( + annotation_a.defaultValue == annotation_b.defaultValue + and annotation_a.defaultValueType == annotation_b.defaultValueType + ) + if isinstance(annotation_a, OmittedAnnotation) and isinstance( + annotation_b, OmittedAnnotation + ): + return True + if isinstance(annotation_a, RequiredAnnotation) and isinstance( + annotation_b, RequiredAnnotation + ): + return True + return False diff --git a/package-parser/package_parser/processing/annotations/model/_annotations.py b/package-parser/package_parser/processing/annotations/model/_annotations.py index 924e175d3..e778f57f5 100644 --- a/package-parser/package_parser/processing/annotations/model/_annotations.py +++ b/package-parser/package_parser/processing/annotations/model/_annotations.py @@ -82,6 +82,18 @@ def from_json(json: Any) -> Interval: json["upperLimitType"], ) + def __eq__(self, other: Any) -> bool: + return ( + isinstance(other, Interval) + and self.isDiscrete == other.isDiscrete + and self.lowerIntervalLimit == other.lowerIntervalLimit + and isinstance(self.lowerIntervalLimit, type(self.lowerIntervalLimit)) + and self.lowerLimitType == other.lowerLimitType + and self.upperIntervalLimit == other.upperIntervalLimit + and isinstance(self.upperIntervalLimit, type(self.upperIntervalLimit)) + and self.upperLimitType == self.upperLimitType + ) + @dataclass class BoundaryAnnotation(AbstractAnnotation): @@ -122,6 +134,13 @@ def to_json(self) -> dict: def from_json(json: Any) -> EnumPair: return EnumPair(json["stringValue"], json["instanceName"]) + def __eq__(self, other: Any) -> bool: + return ( + isinstance(other, EnumPair) + and self.stringValue == other.stringValue + and self.instanceName == other.instanceName + ) + @dataclass class EnumAnnotation(AbstractAnnotation): @@ -311,7 +330,9 @@ class ParameterInfo: value: str value_type: str - def __init__(self, parameter_type, value="", value_type=""): + def __init__( + self, parameter_type: ParameterType, value: str = "", value_type: str = "" + ) -> None: self.type = parameter_type self.value = value self.value_type = value_type diff --git a/package-parser/package_parser/processing/migration/__init__.py b/package-parser/package_parser/processing/migration/__init__.py index 07d73bd30..c75601aa9 100644 --- a/package-parser/package_parser/processing/migration/__init__.py +++ b/package-parser/package_parser/processing/migration/__init__.py @@ -9,4 +9,4 @@ SimpleDiffer, ) -from ._migrate import migrate_annotations +from ._migrate import Migration diff --git a/package-parser/package_parser/processing/migration/_migrate.py b/package-parser/package_parser/processing/migration/_migrate.py index ca749a95e..b534dbb72 100644 --- a/package-parser/package_parser/processing/migration/_migrate.py +++ b/package-parser/package_parser/processing/migration/_migrate.py @@ -1,8 +1,11 @@ -from typing import Optional +from dataclasses import dataclass, field +from typing import Optional, Tuple +from package_parser.processing.annotations import are_semantic_equal from package_parser.processing.annotations.model import ( AbstractAnnotation, AnnotationStore, + EnumReviewResult, ) from package_parser.processing.api.model import Attribute, Result from package_parser.processing.migration.annotations import ( @@ -21,94 +24,206 @@ from package_parser.processing.migration.model import Mapping -def _get_mapping_from_annotation( - annotation: AbstractAnnotation, mappings: list[Mapping] -) -> Optional[Mapping]: - for mapping in mappings: - for element in mapping.get_apiv1_elements(): - if ( - not isinstance(element, (Attribute, Result)) - and element.id == annotation.target - ): - return mapping - return None - - -def migrate_annotations( - annotationsv1: AnnotationStore, mappings: list[Mapping] -) -> AnnotationStore: - migrated_annotation_store = AnnotationStore() - - for boundary_annotation in annotationsv1.boundaryAnnotations: - mapping = _get_mapping_from_annotation(boundary_annotation, mappings) - if mapping is not None: - for annotation in migrate_boundary_annotation(boundary_annotation, mapping): - migrated_annotation_store.add_annotation(annotation) - - for called_after_annotation in annotationsv1.calledAfterAnnotations: - mapping = _get_mapping_from_annotation(called_after_annotation, mappings) - if mapping is not None: - for annotation in migrate_called_after_annotation( - called_after_annotation, mapping, mappings - ): - migrated_annotation_store.add_annotation(annotation) - - for description_annotation in annotationsv1.descriptionAnnotations: - mapping = _get_mapping_from_annotation(description_annotation, mappings) - if mapping is not None: - for annotation in migrate_description_annotation( - description_annotation, mapping - ): - migrated_annotation_store.add_annotation(annotation) - - for enum_annotation in annotationsv1.enumAnnotations: - mapping = _get_mapping_from_annotation(enum_annotation, mappings) - if mapping is not None: - for annotation in migrate_enum_annotation(enum_annotation, mapping): - migrated_annotation_store.add_annotation(annotation) - - for expert_annotation in annotationsv1.expertAnnotations: - mapping = _get_mapping_from_annotation(expert_annotation, mappings) - if mapping is not None: - for annotation in migrate_expert_annotation(expert_annotation, mapping): - migrated_annotation_store.add_annotation(annotation) - - for group_annotation in annotationsv1.groupAnnotations: - mapping = _get_mapping_from_annotation(group_annotation, mappings) - if mapping is not None: - for annotation in migrate_group_annotation( - group_annotation, mapping, mappings - ): - migrated_annotation_store.add_annotation(annotation) - - for move_annotation in annotationsv1.moveAnnotations: - mapping = _get_mapping_from_annotation(move_annotation, mappings) - if mapping is not None: - for annotation in migrate_move_annotation(move_annotation, mapping): - migrated_annotation_store.add_annotation(annotation) - - for rename_annotation in annotationsv1.renameAnnotations: - mapping = _get_mapping_from_annotation(rename_annotation, mappings) - if mapping is not None: - for annotation in migrate_rename_annotation(rename_annotation, mapping): - migrated_annotation_store.add_annotation(annotation) - - for remove_annotation in annotationsv1.removeAnnotations: - mapping = _get_mapping_from_annotation(remove_annotation, mappings) - if mapping is not None: - for annotation in migrate_remove_annotation(remove_annotation, mapping): - migrated_annotation_store.add_annotation(annotation) - - for todo_annotation in annotationsv1.todoAnnotations: - mapping = _get_mapping_from_annotation(todo_annotation, mappings) - if mapping is not None: - for annotation in migrate_todo_annotation(todo_annotation, mapping): - migrated_annotation_store.add_annotation(annotation) - - for value_annotation in annotationsv1.valueAnnotations: - mapping = _get_mapping_from_annotation(value_annotation, mappings) - if mapping is not None: - for annotation in migrate_value_annotation(value_annotation, mapping): - migrated_annotation_store.add_annotation(annotation) - - return migrated_annotation_store +@dataclass +class Migration: + annotationsv1: AnnotationStore + mappings: list[Mapping] + reliable_similarity: float = 0.9 + unsure_similarity: float = 0.8 + migrated_annotation_store: AnnotationStore = field(init=False) + unsure_migrated_annotation_store: AnnotationStore = field(init=False) + + def __post_init__(self) -> None: + self.migrated_annotation_store = AnnotationStore() + self.unsure_migrated_annotation_store = AnnotationStore() + + def _get_mapping_from_annotation( + self, annotation: AbstractAnnotation + ) -> Optional[Mapping]: + for mapping in self.mappings: + for element in mapping.get_apiv1_elements(): + if ( + not isinstance(element, (Attribute, Result)) + and element.id == annotation.target + ): + return mapping + return None + + def migrate_annotations(self) -> None: + for boundary_annotation in self.annotationsv1.boundaryAnnotations: + mapping = self._get_mapping_from_annotation(boundary_annotation) + if mapping is not None: + for annotation in migrate_boundary_annotation( + boundary_annotation, mapping + ): + self.add_annotations_based_on_similarity( + annotation, mapping.get_similarity() + ) + + for called_after_annotation in self.annotationsv1.calledAfterAnnotations: + mapping = self._get_mapping_from_annotation(called_after_annotation) + if mapping is not None: + for annotation in migrate_called_after_annotation( + called_after_annotation, mapping, self.mappings + ): + self.add_annotations_based_on_similarity( + annotation, mapping.get_similarity() + ) + + for description_annotation in self.annotationsv1.descriptionAnnotations: + mapping = self._get_mapping_from_annotation(description_annotation) + if mapping is not None: + for annotation in migrate_description_annotation( + description_annotation, mapping + ): + self.add_annotations_based_on_similarity( + annotation, mapping.get_similarity() + ) + + for enum_annotation in self.annotationsv1.enumAnnotations: + mapping = self._get_mapping_from_annotation(enum_annotation) + if mapping is not None: + for annotation in migrate_enum_annotation(enum_annotation, mapping): + self.add_annotations_based_on_similarity( + annotation, mapping.get_similarity() + ) + + for expert_annotation in self.annotationsv1.expertAnnotations: + mapping = self._get_mapping_from_annotation(expert_annotation) + if mapping is not None: + for annotation in migrate_expert_annotation(expert_annotation, mapping): + self.add_annotations_based_on_similarity( + annotation, mapping.get_similarity() + ) + + for group_annotation in self.annotationsv1.groupAnnotations: + mapping = self._get_mapping_from_annotation(group_annotation) + if mapping is not None: + for annotation in migrate_group_annotation( + group_annotation, mapping, self.mappings + ): + self.add_annotations_based_on_similarity( + annotation, mapping.get_similarity() + ) + + for move_annotation in self.annotationsv1.moveAnnotations: + mapping = self._get_mapping_from_annotation(move_annotation) + if mapping is not None: + for annotation in migrate_move_annotation(move_annotation, mapping): + self.add_annotations_based_on_similarity( + annotation, mapping.get_similarity() + ) + + for rename_annotation in self.annotationsv1.renameAnnotations: + mapping = self._get_mapping_from_annotation(rename_annotation) + if mapping is not None: + for annotation in migrate_rename_annotation(rename_annotation, mapping): + self.add_annotations_based_on_similarity( + annotation, mapping.get_similarity() + ) + + for remove_annotation in self.annotationsv1.removeAnnotations: + mapping = self._get_mapping_from_annotation(remove_annotation) + if mapping is not None: + for annotation in migrate_remove_annotation(remove_annotation, mapping): + self.add_annotations_based_on_similarity( + annotation, mapping.get_similarity() + ) + + for todo_annotation in self.annotationsv1.todoAnnotations: + mapping = self._get_mapping_from_annotation(todo_annotation) + if mapping is not None: + for annotation in migrate_todo_annotation(todo_annotation, mapping): + self.add_annotations_based_on_similarity( + annotation, mapping.get_similarity() + ) + + for value_annotation in self.annotationsv1.valueAnnotations: + mapping = self._get_mapping_from_annotation(value_annotation) + if mapping is not None: + for annotation in migrate_value_annotation(value_annotation, mapping): + self.add_annotations_based_on_similarity( + annotation, mapping.get_similarity() + ) + self._remove_duplicates() + + def add_annotations_based_on_similarity( + self, annotation: AbstractAnnotation, similarity: float + ) -> None: + if similarity >= self.reliable_similarity: + self.migrated_annotation_store.add_annotation(annotation) + elif similarity >= self.unsure_similarity: + annotation.reviewResult = EnumReviewResult.UNSURE + self.migrated_annotation_store.add_annotation(annotation) + else: + self.unsure_migrated_annotation_store.add_annotation(annotation) + + def _remove_duplicates(self) -> None: + for annotation_type in [ + "boundaryAnnotations", + "calledAfterAnnotations", + "descriptionAnnotations", + "enumAnnotations", + "expertAnnotations", + "groupAnnotations", + "moveAnnotations", + "removeAnnotations", + "renameAnnotations", + "todoAnnotations", + "valueAnnotations", + ]: + duplicates: list[Tuple[AbstractAnnotation, AbstractAnnotation]] = [] + migrated_annotations = [ + annotation + for annotation_store in [ + self.migrated_annotation_store, + self.unsure_migrated_annotation_store, + ] + for annotation in getattr(annotation_store, annotation_type) + ] + + for annotation_a in migrated_annotations: + for annotation_b in migrated_annotations: + if annotation_a is annotation_b: + continue + if ( + are_semantic_equal(annotation_a, annotation_b) + and (annotation_b, annotation_a) not in duplicates + ): + duplicates.append((annotation_a, annotation_b)) + for annotation_a, annotation_b in duplicates: + if ( + annotation_a.reviewResult != annotation_b.reviewResult + and EnumReviewResult.UNSURE + in (annotation_a.reviewResult, annotation_b.reviewResult) + ): + annotation_a.reviewResult = EnumReviewResult.UNSURE + annotation_b.reviewResult = EnumReviewResult.UNSURE + b_in_migrated_annotation_store = annotation_b in getattr( + self.migrated_annotation_store, annotation_type + ) + b_in_unsure_annotation_store = annotation_b in getattr( + self.unsure_migrated_annotation_store, annotation_type + ) + if annotation_a in getattr( + self.migrated_annotation_store, annotation_type + ): + if b_in_migrated_annotation_store: + getattr(self.migrated_annotation_store, annotation_type).remove( + annotation_b + ) + if b_in_unsure_annotation_store: + getattr( + self.unsure_migrated_annotation_store, annotation_type + ).remove(annotation_b) + if annotation_a in getattr( + self.unsure_migrated_annotation_store, annotation_type + ): + if b_in_migrated_annotation_store: + getattr(self.migrated_annotation_store, annotation_type).remove( + annotation_b + ) + if b_in_unsure_annotation_store: + getattr( + self.unsure_migrated_annotation_store, annotation_type + ).remove(annotation_b) diff --git a/package-parser/package_parser/processing/migration/annotations/_get_migration_text.py b/package-parser/package_parser/processing/migration/annotations/_get_migration_text.py index f8b3dca0f..6503cae7a 100644 --- a/package-parser/package_parser/processing/migration/annotations/_get_migration_text.py +++ b/package-parser/package_parser/processing/migration/annotations/_get_migration_text.py @@ -89,14 +89,16 @@ def _get_further_information(annotation: AbstractAnnotation) -> str: def get_migration_text( - annotation: AbstractAnnotation, mapping: Mapping, additional_information: Any = None + annotation: AbstractAnnotation, + mapping: Mapping, + for_todo_annotation: bool = False, + additional_information: Any = None, ) -> str: class_name = str(annotation.__class__.__name__) if class_name.endswith("Annotation"): class_name = class_name[:-10] if issubclass(type(annotation), ValueAnnotation): class_name = "Value" - migrate_text = ( "The @" + class_name + " Annotation" + _get_further_information(annotation) ) @@ -128,8 +130,12 @@ def get_migration_text( + _list_api_elements(parameters) + ")" ) - - return migrate_text + migration_text = migrate_text + if for_todo_annotation: + return migration_text + if len(annotation.comment) == 0: + return migration_text + return annotation.comment + "\n" + migration_text def _list_api_elements( diff --git a/package-parser/package_parser/processing/migration/annotations/_migrate_boundary_annotation.py b/package-parser/package_parser/processing/migration/annotations/_migrate_boundary_annotation.py index a932d7cc5..97c1b3a3a 100644 --- a/package-parser/package_parser/processing/migration/annotations/_migrate_boundary_annotation.py +++ b/package-parser/package_parser/processing/migration/annotations/_migrate_boundary_annotation.py @@ -25,6 +25,7 @@ ) from ._constants import migration_author +from ._get_annotated_api_element import get_annotated_api_element from ._get_migration_text import get_migration_text @@ -81,42 +82,50 @@ def migrate_boundary_annotation( authors.append(migration_author) boundary_annotation.authors = authors - migrate_text = get_migration_text(boundary_annotation, mapping) + annotated_apiv1_element = get_annotated_api_element( + boundary_annotation, mapping.get_apiv1_elements() + ) + if annotated_apiv1_element is None or not isinstance( + annotated_apiv1_element, Parameter + ): + return [] if isinstance(mapping, (OneToOneMapping, ManyToOneMapping)): parameter = mapping.get_apiv2_elements()[0] if isinstance(parameter, (Attribute, Result)): return [] if isinstance(parameter, Parameter): - boundary_annotation.target = parameter.id ( parameter_expects_number, parameter_type_is_discrete, ) = _contains_number_and_is_discrete(parameter.type) - if parameter.type is None: + if parameter.type is None and annotated_apiv1_element.type is not None: boundary_annotation.reviewResult = EnumReviewResult.UNSURE - boundary_annotation.comment = ( - migrate_text - if len(boundary_annotation.comment) == 0 - else boundary_annotation.comment + "\n" + migrate_text + boundary_annotation.comment = get_migration_text( + boundary_annotation, mapping ) + boundary_annotation.target = parameter.id return [boundary_annotation] - if parameter_expects_number: + if parameter_expects_number or ( + parameter.type is None and annotated_apiv1_element.type is None + ): if ( parameter_type_is_discrete - is not boundary_annotation.interval.isDiscrete + != boundary_annotation.interval.isDiscrete + ) and not ( + parameter.type is None and annotated_apiv1_element.type is None ): boundary_annotation.reviewResult = EnumReviewResult.UNSURE - boundary_annotation.comment = ( - migrate_text - if len(boundary_annotation.comment) == 0 - else boundary_annotation.comment + "\n" + migrate_text + boundary_annotation.comment = get_migration_text( + boundary_annotation, mapping ) - boundary_annotation.interval = ( - migrate_interval_to_fit_parameter_type( - boundary_annotation.interval, parameter_type_is_discrete + if parameter_expects_number: + boundary_annotation.interval = ( + migrate_interval_to_fit_parameter_type( + boundary_annotation.interval, parameter_type_is_discrete + ) ) - ) + boundary_annotation.target = parameter.id return [boundary_annotation] return [ TodoAnnotation( @@ -125,7 +134,9 @@ def migrate_boundary_annotation( boundary_annotation.reviewers, boundary_annotation.comment, EnumReviewResult.NONE, - migrate_text, + get_migration_text( + boundary_annotation, mapping, for_todo_annotation=True + ), ) ] migrated_annotations: list[AbstractAnnotation] = [] @@ -138,8 +149,8 @@ def migrate_boundary_annotation( if ( parameter.type is not None and is_number - and is_discrete is boundary_annotation.interval.isDiscrete - ): + and is_discrete == boundary_annotation.interval.isDiscrete + ) or (parameter.type is None and annotated_apiv1_element.type is None): migrated_annotations.append( BoundaryAnnotation( parameter.id, @@ -156,9 +167,7 @@ def migrate_boundary_annotation( parameter.id, authors, boundary_annotation.reviewers, - migrate_text - if len(boundary_annotation.comment) == 0 - else boundary_annotation.comment + "\n" + migrate_text, + get_migration_text(boundary_annotation, mapping), EnumReviewResult.UNSURE, migrate_interval_to_fit_parameter_type( boundary_annotation.interval, @@ -172,9 +181,7 @@ def migrate_boundary_annotation( parameter.id, authors, boundary_annotation.reviewers, - migrate_text - if len(boundary_annotation.comment) == 0 - else boundary_annotation.comment + "\n" + migrate_text, + get_migration_text(boundary_annotation, mapping), EnumReviewResult.UNSURE, boundary_annotation.interval, ) @@ -187,8 +194,10 @@ def migrate_boundary_annotation( authors, boundary_annotation.reviewers, boundary_annotation.comment, - EnumReviewResult.UNSURE, - migrate_text, + EnumReviewResult.NONE, + get_migration_text( + boundary_annotation, mapping, for_todo_annotation=True + ), ) ) return migrated_annotations diff --git a/package-parser/package_parser/processing/migration/annotations/_migrate_called_after_annotation.py b/package-parser/package_parser/processing/migration/annotations/_migrate_called_after_annotation.py index 785f33bbd..97778c22c 100644 --- a/package-parser/package_parser/processing/migration/annotations/_migrate_called_after_annotation.py +++ b/package-parser/package_parser/processing/migration/annotations/_migrate_called_after_annotation.py @@ -33,8 +33,10 @@ def migrate_called_after_annotation( authors, called_after_annotation.reviewers, called_after_annotation.comment, - called_after_annotation.reviewResult, - get_migration_text(called_after_annotation, mapping), + EnumReviewResult.NONE, + get_migration_text( + called_after_annotation, mapping, for_todo_annotation=True + ), ) ) continue @@ -42,18 +44,17 @@ def migrate_called_after_annotation( called_before_functions = _get_function_called_before_replacements( called_after_annotation, mappings, element ) - migrate_text = get_migration_text( - called_after_annotation, - mapping, - additional_information=called_before_functions, - ) if len(called_before_functions) == 0: migrated_annotations.append( CalledAfterAnnotation( element.id, authors, called_after_annotation.reviewers, - called_after_annotation.comment, + get_migration_text( + called_after_annotation, + mapping, + additional_information=called_before_functions, + ), EnumReviewResult.UNSURE, called_after_annotation.calledAfterName, ) @@ -79,7 +80,12 @@ def migrate_called_after_annotation( called_after_annotation.reviewers, called_after_annotation.comment, EnumReviewResult.NONE, - migrate_text, + get_migration_text( + called_after_annotation, + mapping, + for_todo_annotation=True, + additional_information=called_before_functions, + ), ) ) return migrated_annotations diff --git a/package-parser/package_parser/processing/migration/annotations/_migrate_description_annotation.py b/package-parser/package_parser/processing/migration/annotations/_migrate_description_annotation.py index 523aff916..f933d68c2 100644 --- a/package-parser/package_parser/processing/migration/annotations/_migrate_description_annotation.py +++ b/package-parser/package_parser/processing/migration/annotations/_migrate_description_annotation.py @@ -25,7 +25,6 @@ def migrate_description_annotation( authors = description_annotation.authors authors.append(migration_author) description_annotation.authors = authors - migrate_text = get_migration_text(description_annotation, mapping) if isinstance(mapping, (ManyToOneMapping, OneToOneMapping)): element = mapping.get_apiv2_elements()[0] @@ -63,7 +62,9 @@ def migrate_description_annotation( description_annotation.reviewers, description_annotation.comment, EnumReviewResult.NONE, - migrate_text, + get_migration_text( + description_annotation, mapping, for_todo_annotation=True + ), ) ) return description_annotations diff --git a/package-parser/package_parser/processing/migration/annotations/_migrate_enum_annotation.py b/package-parser/package_parser/processing/migration/annotations/_migrate_enum_annotation.py index 0a64be753..07d59bdf0 100644 --- a/package-parser/package_parser/processing/migration/annotations/_migrate_enum_annotation.py +++ b/package-parser/package_parser/processing/migration/annotations/_migrate_enum_annotation.py @@ -25,6 +25,7 @@ ) from ._constants import migration_author +from ._get_annotated_api_element import get_annotated_api_element from ._get_migration_text import get_migration_text @@ -57,34 +58,36 @@ def migrate_enum_annotation( authors.append(migration_author) enum_annotation.authors = authors - migrate_text = get_migration_text(enum_annotation, mapping) + annotated_apiv1_element = get_annotated_api_element( + enum_annotation, mapping.get_apiv1_elements() + ) + if annotated_apiv1_element is None or not isinstance( + annotated_apiv1_element, Parameter + ): + return [] if isinstance(mapping, (OneToOneMapping, ManyToOneMapping)): parameter = mapping.get_apiv2_elements()[0] if isinstance(parameter, (Attribute, Result)): return [] if isinstance(parameter, Parameter): - if parameter.type is not None: - if _contains_string( - parameter.type - ) and _default_value_is_in_instance_values_or_is_empty( + if ( + parameter.type is not None + and _contains_string(parameter.type) + and _default_value_is_in_instance_values_or_is_empty( parameter.default_value, enum_annotation.pairs - ): - enum_annotation.target = parameter.id - return [enum_annotation] - if isinstance(parameter.type, NamedType): - # assuming api has been chanced to an enum type: - # do not migrate annotation - return [] - else: - enum_annotation.reviewResult = EnumReviewResult.UNSURE - enum_annotation.comment = ( - migrate_text - if len(enum_annotation.comment) == 0 - else enum_annotation.comment + "\n" + migrate_text ) + ) or (parameter.type is None and annotated_apiv1_element.type is None): enum_annotation.target = parameter.id return [enum_annotation] + if isinstance(parameter.type, NamedType): + # assuming api has been chanced to an enum type: + # do not migrate annotation + return [] + enum_annotation.reviewResult = EnumReviewResult.UNSURE + enum_annotation.comment = get_migration_text(enum_annotation, mapping) + enum_annotation.target = parameter.id + return [enum_annotation] return [ TodoAnnotation( parameter.id, @@ -92,7 +95,7 @@ def migrate_enum_annotation( enum_annotation.reviewers, enum_annotation.comment, EnumReviewResult.NONE, - migrate_text, + get_migration_text(enum_annotation, mapping, for_todo_annotation=True), ) ] @@ -100,34 +103,49 @@ def migrate_enum_annotation( if isinstance(mapping, (OneToManyMapping, ManyToManyMapping)): for parameter in mapping.get_apiv2_elements(): if isinstance(parameter, Parameter): - if parameter.type is not None: - if _contains_string( - parameter.type - ) and _default_value_is_in_instance_values_or_is_empty( + if ( + parameter.type is not None + and _contains_string(parameter.type) + and _default_value_is_in_instance_values_or_is_empty( parameter.default_value, enum_annotation.pairs - ): - migrated_annotations.append( - EnumAnnotation( - parameter.id, - authors, - enum_annotation.reviewers, - enum_annotation.comment, - EnumReviewResult.NONE, - enum_annotation.enumName, - enum_annotation.pairs, - ) - ) - elif isinstance(parameter.type, NamedType): - continue - else: + ) + ) or (parameter.type is None and annotated_apiv1_element.type is None): migrated_annotations.append( - TodoAnnotation( + EnumAnnotation( parameter.id, authors, enum_annotation.reviewers, enum_annotation.comment, - EnumReviewResult.UNSURE, - migrate_text, + EnumReviewResult.NONE, + enum_annotation.enumName, + enum_annotation.pairs, ) ) + continue + if isinstance(parameter.type, NamedType): + continue + migrated_annotations.append( + EnumAnnotation( + parameter.id, + authors, + enum_annotation.reviewers, + get_migration_text(enum_annotation, mapping), + EnumReviewResult.UNSURE, + enum_annotation.enumName, + enum_annotation.pairs, + ) + ) + elif not isinstance(parameter, (Attribute, Result)): + migrated_annotations.append( + TodoAnnotation( + parameter.id, + authors, + enum_annotation.reviewers, + enum_annotation.comment, + EnumReviewResult.NONE, + get_migration_text( + enum_annotation, mapping, for_todo_annotation=True + ), + ) + ) return migrated_annotations diff --git a/package-parser/package_parser/processing/migration/annotations/_migrate_expert_annotation.py b/package-parser/package_parser/processing/migration/annotations/_migrate_expert_annotation.py index 4ec58076e..6aeb0e914 100644 --- a/package-parser/package_parser/processing/migration/annotations/_migrate_expert_annotation.py +++ b/package-parser/package_parser/processing/migration/annotations/_migrate_expert_annotation.py @@ -25,7 +25,6 @@ def migrate_expert_annotation( authors = expert_annotation.authors authors.append(migration_author) expert_annotation.authors = authors - migrate_text = get_migration_text(expert_annotation, mapping) if isinstance(mapping, (ManyToOneMapping, OneToOneMapping)): element = mapping.get_apiv2_elements()[0] @@ -62,7 +61,9 @@ def migrate_expert_annotation( expert_annotation.reviewers, expert_annotation.comment, EnumReviewResult.NONE, - migrate_text, + get_migration_text( + expert_annotation, mapping, for_todo_annotation=True + ), ) ) return expert_annotations diff --git a/package-parser/package_parser/processing/migration/annotations/_migrate_group_annotation.py b/package-parser/package_parser/processing/migration/annotations/_migrate_group_annotation.py index 70dc3cac1..aff10d3f6 100644 --- a/package-parser/package_parser/processing/migration/annotations/_migrate_group_annotation.py +++ b/package-parser/package_parser/processing/migration/annotations/_migrate_group_annotation.py @@ -33,8 +33,10 @@ def migrate_group_annotation( authors=authors, reviewers=group_annotation.reviewers, comment=group_annotation.comment, - reviewResult=group_annotation.reviewResult, - newTodo=get_migration_text(group_annotation, mapping), + reviewResult=EnumReviewResult.NONE, + newTodo=get_migration_text( + group_annotation, mapping, for_todo_annotation=True + ), ) ) else: @@ -61,10 +63,6 @@ def migrate_group_annotation( ] grouped_parameters = remove_duplicates_and_preserve_order - group_name = group_annotation.groupName - review_result = EnumReviewResult.NONE - migrate_text = get_migration_text(group_annotation, mapping) - if len(grouped_parameters) < 2 < len(group_annotation.parameters): migrated_annotations.append( TodoAnnotation( @@ -72,10 +70,11 @@ def migrate_group_annotation( authors=authors, reviewers=group_annotation.reviewers, comment=group_annotation.comment, - reviewResult=group_annotation.reviewResult, + reviewResult=EnumReviewResult.NONE, newTodo=get_migration_text( group_annotation, mapping, + for_todo_annotation=True, additional_information=grouped_parameters, ), ) @@ -83,26 +82,36 @@ def migrate_group_annotation( continue if len(grouped_parameters) != len(group_annotation.parameters): - group_name += str(int(name_modifier, base=2)) - review_result = EnumReviewResult.UNSURE - if len(group_annotation.comment) != 0: - migrate_text = group_annotation.comment + "\n" + migrate_text - - migrated_annotations.append( - GroupAnnotation( - target=functionv2.id, - authors=authors, - reviewers=group_annotation.reviewers, - comment=( - migrate_text - if review_result is EnumReviewResult.UNSURE - else group_annotation.comment - ), - reviewResult=review_result, - groupName=group_name, - parameters=[parameter.name for parameter in grouped_parameters], + group_name = group_annotation.groupName + str( + int(name_modifier, base=2) + ) + migrated_annotations.append( + GroupAnnotation( + target=functionv2.id, + authors=authors, + reviewers=group_annotation.reviewers, + comment=get_migration_text( + group_annotation, + mapping, + additional_information=grouped_parameters, + ), + reviewResult=EnumReviewResult.UNSURE, + groupName=group_name, + parameters=[parameter.name for parameter in grouped_parameters], + ) + ) + else: + migrated_annotations.append( + GroupAnnotation( + target=functionv2.id, + authors=authors, + reviewers=group_annotation.reviewers, + comment=group_annotation.comment, + reviewResult=EnumReviewResult.NONE, + groupName=group_annotation.groupName, + parameters=[parameter.name for parameter in grouped_parameters], + ) ) - ) return migrated_annotations diff --git a/package-parser/package_parser/processing/migration/annotations/_migrate_move_annotation.py b/package-parser/package_parser/processing/migration/annotations/_migrate_move_annotation.py index d5f264e6a..804e8fe2a 100644 --- a/package-parser/package_parser/processing/migration/annotations/_migrate_move_annotation.py +++ b/package-parser/package_parser/processing/migration/annotations/_migrate_move_annotation.py @@ -42,7 +42,6 @@ def migrate_move_annotation( authors = move_annotation.authors authors.append(migration_author) move_annotation.authors = authors - migrate_text = get_migration_text(move_annotation, mapping) if isinstance(mapping, (ManyToOneMapping, OneToOneMapping)): element = mapping.get_apiv2_elements()[0] @@ -56,7 +55,9 @@ def migrate_move_annotation( move_annotation.reviewers, move_annotation.comment, EnumReviewResult.NONE, - migrate_text, + get_migration_text( + move_annotation, mapping, for_todo_annotation=True + ), ) ] move_annotation.target = element.id @@ -92,8 +93,10 @@ def migrate_move_annotation( authors, move_annotation.reviewers, move_annotation.comment, - EnumReviewResult.UNSURE, - migrate_text, + EnumReviewResult.NONE, + get_migration_text( + move_annotation, mapping, for_todo_annotation=True + ), ) ) return move_annotations diff --git a/package-parser/package_parser/processing/migration/annotations/_migrate_remove_annotation.py b/package-parser/package_parser/processing/migration/annotations/_migrate_remove_annotation.py index 49d935064..18077cca1 100644 --- a/package-parser/package_parser/processing/migration/annotations/_migrate_remove_annotation.py +++ b/package-parser/package_parser/processing/migration/annotations/_migrate_remove_annotation.py @@ -35,7 +35,6 @@ def migrate_remove_annotation( authors = remove_annotation.authors authors.append(migration_author) remove_annotation.authors = authors - migrate_text = get_migration_text(remove_annotation, mapping) if isinstance(mapping, (ManyToOneMapping, OneToOneMapping)): element = mapping.get_apiv2_elements()[0] @@ -49,7 +48,9 @@ def migrate_remove_annotation( remove_annotation.reviewers, remove_annotation.comment, EnumReviewResult.NONE, - migrate_text, + get_migration_text( + remove_annotation, mapping, for_todo_annotation=True + ), ) ] remove_annotation.target = element.id @@ -85,7 +86,9 @@ def migrate_remove_annotation( remove_annotation.reviewers, remove_annotation.comment, EnumReviewResult.NONE, - migrate_text, + get_migration_text( + remove_annotation, mapping, for_todo_annotation=True + ), ) ) return remove_annotations diff --git a/package-parser/package_parser/processing/migration/annotations/_migrate_rename_annotation.py b/package-parser/package_parser/processing/migration/annotations/_migrate_rename_annotation.py index cf26360fe..2380ab775 100644 --- a/package-parser/package_parser/processing/migration/annotations/_migrate_rename_annotation.py +++ b/package-parser/package_parser/processing/migration/annotations/_migrate_rename_annotation.py @@ -14,6 +14,7 @@ ) from ._constants import migration_author +from ._get_annotated_api_element import get_annotated_api_element from ._get_migration_text import get_migration_text @@ -33,31 +34,53 @@ def migrate_rename_annotation( rename_annotation.target = element.id return [rename_annotation] - migrate_text = get_migration_text(rename_annotation, mapping) + annotated_apiv1_element = get_annotated_api_element( + rename_annotation, mapping.get_apiv1_elements() + ) + if annotated_apiv1_element is None: + return [] - todo_annotations: list[AbstractAnnotation] = [] + annotations: list[AbstractAnnotation] = [] for element in mapping.get_apiv2_elements(): - if not isinstance(element, (Attribute, Result)): - if element.name in ( + if isinstance(element, type(annotated_apiv1_element)) and not isinstance( + element, (Attribute, Result) + ): + if element.name not in ( new_name, rename_annotation.target.split(".")[-1], ): - rename_annotation.target = element.id - rename_annotation.reviewResult = EnumReviewResult.UNSURE - rename_annotation.comment = ( - migrate_text - if len(rename_annotation.comment) == 0 - else rename_annotation.comment + "\n" + migrate_text + annotations.append( + RenameAnnotation( + element.id, + authors, + rename_annotation.reviewers, + get_migration_text(rename_annotation, mapping), + EnumReviewResult.UNSURE, + rename_annotation.newName, + ) ) - return [rename_annotation] - todo_annotations.append( + else: + annotations.append( + RenameAnnotation( + element.id, + authors, + rename_annotation.reviewers, + rename_annotation.comment, + EnumReviewResult.NONE, + rename_annotation.newName, + ) + ) + elif not isinstance(element, (Attribute, Result)): + annotations.append( TodoAnnotation( element.id, authors, rename_annotation.reviewers, rename_annotation.comment, EnumReviewResult.NONE, - migrate_text, + get_migration_text( + rename_annotation, mapping, for_todo_annotation=True + ), ) ) - return todo_annotations + return annotations diff --git a/package-parser/package_parser/processing/migration/annotations/_migrate_todo_annotation.py b/package-parser/package_parser/processing/migration/annotations/_migrate_todo_annotation.py index dc27d1b28..b7509cf57 100644 --- a/package-parser/package_parser/processing/migration/annotations/_migrate_todo_annotation.py +++ b/package-parser/package_parser/processing/migration/annotations/_migrate_todo_annotation.py @@ -33,8 +33,6 @@ def migrate_todo_annotation( todo_annotation.target = element.id return [todo_annotation] - migrate_text = get_migration_text(todo_annotation, mapping) - annotated_apiv1_element = get_annotated_api_element( todo_annotation, mapping.get_apiv1_elements() ) @@ -63,8 +61,10 @@ def migrate_todo_annotation( authors, todo_annotation.reviewers, todo_annotation.comment, - EnumReviewResult.UNSURE, - migrate_text, + EnumReviewResult.NONE, + get_migration_text( + todo_annotation, mapping, for_todo_annotation=True + ), ) ) return todo_annotations diff --git a/package-parser/package_parser/processing/migration/annotations/_migrate_value_annotation.py b/package-parser/package_parser/processing/migration/annotations/_migrate_value_annotation.py index 5fa917d50..c18d5df6f 100644 --- a/package-parser/package_parser/processing/migration/annotations/_migrate_value_annotation.py +++ b/package-parser/package_parser/processing/migration/annotations/_migrate_value_annotation.py @@ -1,5 +1,5 @@ from copy import deepcopy -from typing import Optional, Tuple +from typing import Optional from package_parser.processing.annotations.model import ( AbstractAnnotation, @@ -120,7 +120,7 @@ def migrate_value_annotation( authors, value_annotation.reviewers, value_annotation.comment, - EnumReviewResult.UNSURE, + EnumReviewResult.NONE, get_migration_text(value_annotation, mapping), ) ) @@ -128,166 +128,141 @@ def migrate_value_annotation( def _have_same_type( - default_value_typev1: ValueAnnotation.DefaultValueType, - parameterv1: Parameter, - typev2: AbstractType, + typev1: Optional[AbstractType], + typev2: Optional[AbstractType], ) -> bool: + if typev2 is None and typev1 is None: + return True + if typev2 is None or typev1 is None: + return False if isinstance(typev2, NamedType): - if default_value_typev1 is ValueAnnotation.DefaultValueType.NUMBER: - if parameterv1 is not None: - if typev2.name in ("int", "integer") or typev2.name.startswith("int "): - if parameterv1.type is None: - return False - types = [parameterv1.type] - if isinstance(parameterv1.type, UnionType): - types = parameterv1.type.types - for element in types: - if isinstance(element, NamedType) and ( - element.name in ("int", "integer") - or element.name.startswith("int ") - ): - return True - elif typev2.name == "float" or typev2.name.startswith("float "): - if parameterv1.type is None: - return False - types = [parameterv1.type] - if isinstance(parameterv1.type, UnionType): - types = parameterv1.type.types - for element in types: - if isinstance(element, NamedType) and ( - element.name == "float" or element.name.startswith("float ") - ): - return True - return False - return ( - ( - default_value_typev1 is ValueAnnotation.DefaultValueType.BOOLEAN - and typev2.name in ("bool", "boolean") - ) - or ( - default_value_typev1 is ValueAnnotation.DefaultValueType.STRING - and typev2.name in ("str", "string") - ) - or ( - default_value_typev1 is ValueAnnotation.DefaultValueType.NUMBER - and ( - typev2.name in ("int", "integer", "float") - or typev2.name.startswith("int ") - or typev2.name.startswith("float ") - ) - ) - ) - if isinstance(typev2, UnionType): + if typev2.name in ("int", "interger") or typev2.name.startswith("int "): + types = [typev1] + if isinstance(typev1, UnionType): + types = typev1.types + for element in types: + if isinstance(element, NamedType) and ( + element.name in ("int", "integer") + or element.name.startswith("int ") + ): + return True + elif typev2.name == "float" or typev2.name.startswith("float "): + types = [typev1] + if isinstance(typev1, UnionType): + types = typev1.types + for element in types: + if isinstance(element, NamedType) and ( + element.name == "float" or element.name.startswith("float ") + ): + return True + elif typev2.name in ("bool", "boolean"): + types = [typev1] + if isinstance(typev1, UnionType): + types = typev1.types + for element in types: + if isinstance(element, NamedType) and ( + element.name in ("bool", "boolean") + ): + return True + elif typev2.name in ("str", "string"): + types = [typev1] + if isinstance(typev1, UnionType): + types = typev1.types + for element in types: + if isinstance(element, NamedType) and ( + element.name in ("str", "string") + ): + return True + elif isinstance(typev2, UnionType): for element in typev2.types: - if _have_same_type(default_value_typev1, parameterv1, element): + if _have_same_type(typev1, element): return True return False def _have_same_value( - parameterv1: Parameter, - parameterv2: Parameter, -) -> Optional[Tuple[Optional[ValueAnnotation.DefaultValueType], bool]]: - parameterv1_default_value = parameterv1.default_value - parameterv2_default_value = parameterv2.default_value - - if parameterv1_default_value is None: - return None - if parameterv2_default_value is None: - return None - parameterv1_is_in_quotation_marks = ( + parameterv1_default_value: Optional[str], parameterv2_default_value: Optional[str] +) -> bool: + if parameterv1_default_value is None and parameterv2_default_value is None: + return True + if parameterv1_default_value is None or parameterv2_default_value is None: + return False + if parameterv1_default_value == "None" and parameterv2_default_value == "None": + return True + try: + intv1_value = int(parameterv1_default_value) + intv2_value = int(parameterv2_default_value) + return intv1_value == intv2_value + except ValueError: + try: + floatv1_value = float(parameterv1_default_value) + floatv2_value = float(parameterv2_default_value) + return floatv1_value == floatv2_value + except ValueError: + try: + int(parameterv1_default_value) + float(parameterv2_default_value) + return False + except ValueError: + try: + int(parameterv2_default_value) + float(parameterv1_default_value) + return False + except ValueError: + pass + if parameterv1_default_value in ( + "True", + "False", + ) and parameterv2_default_value in ("True", "False"): + return bool(parameterv1_default_value) == bool(parameterv2_default_value) + valuev1_is_in_quotation_marks = ( parameterv1_default_value.startswith("'") and parameterv1_default_value.endswith("'") ) or ( parameterv1_default_value.startswith('"') and parameterv1_default_value.endswith('"') ) - parameterv2_is_in_quotation_marks = ( + valuev2_is_in_quotation_marks = ( parameterv2_default_value.startswith("'") and parameterv2_default_value.endswith("'") ) or ( parameterv2_default_value.startswith('"') and parameterv2_default_value.endswith('"') ) - if parameterv1_default_value == "None" and parameterv2_default_value == "None": - return None, True - if parameterv2.type is None or _have_same_type( - ValueAnnotation.DefaultValueType.NUMBER, parameterv1, parameterv2.type - ): - try: - intv1_value = int(parameterv1_default_value) - intv2_value = int(parameterv2_default_value) - return ValueAnnotation.DefaultValueType.NUMBER, intv1_value == intv2_value - except ValueError: - try: - floatv1_value = float(parameterv1_default_value) - floatv2_value = float(parameterv2_default_value) - return ( - ValueAnnotation.DefaultValueType.NUMBER, - floatv1_value == floatv2_value, - ) - except ValueError: - try: - int(parameterv1_default_value) - float(parameterv2_default_value) - return ValueAnnotation.DefaultValueType.NUMBER, False - except ValueError: - try: - int(parameterv2_default_value) - float(parameterv1_default_value) - return ValueAnnotation.DefaultValueType.NUMBER, False - except ValueError: - pass - if parameterv1_default_value in ( - "True", - "False", - ) and parameterv2_default_value in ("True", "False"): - return ValueAnnotation.DefaultValueType.BOOLEAN, bool( - parameterv1_default_value - ) == bool(parameterv2_default_value) - if parameterv1_is_in_quotation_marks and parameterv2_is_in_quotation_marks: - return ( - ValueAnnotation.DefaultValueType.STRING, - parameterv1_default_value[1:-1] == parameterv2_default_value[1:-1], - ) - return None + if valuev1_is_in_quotation_marks and valuev2_is_in_quotation_marks: + return parameterv1_default_value[1:-1] == parameterv2_default_value[1:-1] + return False def migrate_constant_annotation( constant_annotation: ConstantAnnotation, parameterv2: Parameter, mapping: Mapping ) -> Optional[ConstantAnnotation]: - if parameterv2.type is None: - migrate_text = get_migration_text(constant_annotation, mapping) - return ConstantAnnotation( - parameterv2.id, - constant_annotation.authors, - constant_annotation.reviewers, - migrate_text - if len(constant_annotation.comment) == 0 - else constant_annotation.comment + "\n" + migrate_text, - EnumReviewResult.UNSURE, - constant_annotation.defaultValueType, - constant_annotation.defaultValue, - ) - parameterv1 = get_annotated_api_element_by_type( constant_annotation, mapping.get_apiv1_elements(), Parameter ) if parameterv1 is None: return None - if _have_same_type( - constant_annotation.defaultValueType, parameterv1, parameterv2.type - ): + if not _have_same_type(parameterv1.type, parameterv2.type): + return None + if not _have_same_value(parameterv1.default_value, parameterv2.default_value): return ConstantAnnotation( parameterv2.id, constant_annotation.authors, constant_annotation.reviewers, - constant_annotation.comment, - EnumReviewResult.NONE, + get_migration_text(constant_annotation, mapping), + EnumReviewResult.UNSURE, constant_annotation.defaultValueType, constant_annotation.defaultValue, ) - return None + return ConstantAnnotation( + parameterv2.id, + constant_annotation.authors, + constant_annotation.reviewers, + constant_annotation.comment, + EnumReviewResult.NONE, + constant_annotation.defaultValueType, + constant_annotation.defaultValue, + ) def migrate_omitted_annotation( @@ -298,32 +273,28 @@ def migrate_omitted_annotation( ) if parameterv1 is None: return None - type_and_same_value = _have_same_value(parameterv1, parameterv2) - if type_and_same_value is None: - return None - data_type, are_equal = type_and_same_value - - is_not_unsure = are_equal - if parameterv2.type is not None and data_type is not None: - is_not_unsure = are_equal and _have_same_type( - data_type, parameterv1, parameterv2.type + if _have_same_type(parameterv1.type, parameterv2.type) and _have_same_value( + parameterv1.default_value, parameterv2.default_value + ): + return OmittedAnnotation( + parameterv2.id, + omitted_annotation.authors, + omitted_annotation.reviewers, + omitted_annotation.comment, + EnumReviewResult.NONE, + ) + if _have_same_type(parameterv1.type, parameterv2.type) and not _have_same_value( + parameterv1.default_value, parameterv2.default_value + ): + return OmittedAnnotation( + parameterv2.id, + omitted_annotation.authors, + omitted_annotation.reviewers, + get_migration_text(omitted_annotation, mapping), + EnumReviewResult.UNSURE, ) - review_result = EnumReviewResult.NONE if is_not_unsure else EnumReviewResult.UNSURE - migrate_text = ( - get_migration_text(omitted_annotation, mapping) - if len(omitted_annotation.comment) == 0 - else omitted_annotation.comment - + "\n" - + get_migration_text(omitted_annotation, mapping) - ) - return OmittedAnnotation( - parameterv2.id, - omitted_annotation.authors, - omitted_annotation.reviewers, - omitted_annotation.comment if is_not_unsure else migrate_text, - review_result, - ) + return None def migrate_optional_annotation( @@ -334,8 +305,8 @@ def migrate_optional_annotation( ) if parameterv1 is None: return None - if parameterv2.type is not None and _have_same_type( - optional_annotation.defaultValueType, parameterv1, parameterv2.type + if _have_same_type(parameterv1.type, parameterv2.type) and _have_same_value( + parameterv1.default_value, parameterv2.default_value ): return OptionalAnnotation( parameterv2.id, @@ -346,19 +317,32 @@ def migrate_optional_annotation( optional_annotation.defaultValueType, optional_annotation.defaultValue, ) - if parameterv2.type is None: - migrate_text = get_migration_text(optional_annotation, mapping) + have_implicit_same_value = False + if parameterv1.default_value is not None and parameterv2.default_value is not None: + try: + have_implicit_same_value = float(parameterv1.default_value) == float( + parameterv2.default_value + ) + except ValueError: + pass + if ( + _have_same_type(parameterv1.type, parameterv2.type) + or ( + (parameterv1.default_value is None) + is not (parameterv2.default_value is None) + ) + or have_implicit_same_value + ): return OptionalAnnotation( parameterv2.id, optional_annotation.authors, optional_annotation.reviewers, - migrate_text - if len(optional_annotation.comment) == 0 - else optional_annotation.comment + "\n" + migrate_text, + get_migration_text(optional_annotation, mapping), EnumReviewResult.UNSURE, optional_annotation.defaultValueType, optional_annotation.defaultValue, ) + return None @@ -370,13 +354,18 @@ def migrate_required_annotation( ) if parameterv1 is None: return None - type_and_same_value = _have_same_value(parameterv1, parameterv2) - if type_and_same_value is None: - return None - return RequiredAnnotation( - parameterv2.id, - required_annotation.authors, - required_annotation.reviewers, - required_annotation.comment, - EnumReviewResult.NONE, - ) + if _have_same_type(parameterv1.type, parameterv2.type) and ( + ( + parameterv1.default_value is not None + and parameterv2.default_value is not None + ) + or (parameterv1.default_value is None and parameterv2.default_value is None) + ): + return RequiredAnnotation( + parameterv2.id, + required_annotation.authors, + required_annotation.reviewers, + required_annotation.comment, + EnumReviewResult.NONE, + ) + return None diff --git a/package-parser/package_parser/processing/migration/model/_differ.py b/package-parser/package_parser/processing/migration/model/_differ.py index b0f2e5959..51dab638b 100644 --- a/package-parser/package_parser/processing/migration/model/_differ.py +++ b/package-parser/package_parser/processing/migration/model/_differ.py @@ -1,5 +1,5 @@ from abc import ABC, abstractmethod -from typing import Any, Callable, Optional +from typing import Callable, Optional, TypeVar from Levenshtein import distance from package_parser.processing.api.model import ( @@ -44,10 +44,13 @@ def compute_result_similarity(self, result_a: Result, result_b: Result) -> float pass +X = TypeVar("X") + + def distance_elements( - list_a: list[Any], - list_b: list[Any], - are_similar: Callable[[Any, Any], bool] = lambda x, y: x == y, + list_a: list[X], + list_b: list[X], + are_similar: Callable[[X, X], bool] = lambda x, y: x == y, ) -> float: if len(list_a) == 0: return len(list_b) diff --git a/package-parser/package_parser/processing/migration/model/_mapping.py b/package-parser/package_parser/processing/migration/model/_mapping.py index d50c41f38..b67d2a167 100644 --- a/package-parser/package_parser/processing/migration/model/_mapping.py +++ b/package-parser/package_parser/processing/migration/model/_mapping.py @@ -83,11 +83,11 @@ def get_apiv2_elements(self) -> list[api_element]: def merge_mappings(mapping_a: Mapping, mapping_b: Mapping) -> Mapping: similarity = (mapping_a.similarity + mapping_b.similarity) / 2 - codomain: list[api_element] = list( - set(mapping_a.get_apiv2_elements()) | set(mapping_b.get_apiv2_elements()) + codomain = merge_api_elements_and_remove_duplicates( + mapping_a.get_apiv2_elements(), mapping_b.get_apiv2_elements() ) - domain: list[api_element] = list( - set(mapping_a.get_apiv1_elements()) | set(mapping_b.get_apiv1_elements()) + domain: list[api_element] = merge_api_elements_and_remove_duplicates( + mapping_a.get_apiv1_elements(), mapping_b.get_apiv1_elements() ) if len(domain) == 1 and len(codomain) == 1: return OneToOneMapping(similarity, domain[0], codomain[0]) @@ -98,6 +98,19 @@ def merge_mappings(mapping_a: Mapping, mapping_b: Mapping) -> Mapping: return ManyToManyMapping(similarity, domain, codomain) +def merge_api_elements_and_remove_duplicates( + list_a: list[api_element], list_b: list[api_element] +) -> list[api_element]: + api_elements: list[api_element] = [] + api_elements.extend(list_a) + api_elements.extend(list_b) + api_elements_tmp: list[api_element] = [ + i for n, i in enumerate(api_elements) if i not in api_elements[:n] + ] + api_elements = api_elements_tmp + return api_elements + + class APIMapping: threshold_of_similarity_between_mappings: float threshold_of_similarity_for_creation_of_mappings: float @@ -110,9 +123,9 @@ def __init__( apiv1: API, apiv2: API, differ: AbstractDiffer, - threshold_of_similarity_for_creation_of_mappings=0.5, - threshold_of_similarity_between_mappings=0.05, - ): + threshold_of_similarity_for_creation_of_mappings: float = 0.5, + threshold_of_similarity_between_mappings: float = 0.05, + ) -> None: self.apiv1 = apiv1 self.apiv2 = apiv2 self.differ = differ @@ -227,7 +240,7 @@ def _merge_similar_mappings(self, mappings: List[Mapping]) -> Optional[Mapping]: def _merge_mappings_with_same_elements( self, mapping_to_be_appended: Mapping, mappings: list[Mapping] - ): + ) -> None: """ This method prevents that an element in a mapping appears multiple times in a list of mappings by merging the affected mappings and include the result in the list. If there is no such element, diff --git a/package-parser/tests/data/migration/annotationv1.json b/package-parser/tests/data/migration/annotationv1.json new file mode 100644 index 000000000..633aac45a --- /dev/null +++ b/package-parser/tests/data/migration/annotationv1.json @@ -0,0 +1,59 @@ +{ + "schemaVersion": 2, + "boundaryAnnotations": {}, + "calledAfterAnnotations": {}, + "completeAnnotations": {}, + "descriptionAnnotations": {}, + "enumAnnotations": {}, + "expertAnnotations": {}, + "groupAnnotations": {}, + "moveAnnotations": { + "test/test/complete_different_function": { + "target": "test/test/complete_different_function", + "authors": ["$autogen$"], + "reviewers": [], + "comment": "", + "reviewResult": "", + "destination": "test/test.moved.package" + } + }, + "pureAnnotations": {}, + "removeAnnotations": { + "test/test/UnusedClass": { + "target": "test/test/TestClass", + "authors": ["$autogen$"], + "reviewers": [], + "comment": "I removed this class because it has no known usages.", + "reviewResult": "" + } + }, + "renameAnnotations": { + "test/test/test_function": { + "target": "test/test/test_function", + "authors": ["$autogen$"], + "reviewers": [], + "comment": "", + "reviewResult": "", + "newName": "renamed_test_function" + } + }, + "todoAnnotations": { + "test/test/test_function": { + "target": "test/test/test_function", + "authors": ["$autogen$"], + "reviewers": [], + "comment": "", + "reviewResult": "", + "newTodo": "this is a todo annotation" + }, + "test/test/test_function/test_parameter": { + "target": "test/test/test_function/test_parameter", + "authors": ["$autogen$"], + "reviewers": [], + "comment": "", + "reviewResult": "", + "newTodo": "another todo annotation" + } + }, + "valueAnnotations": {} +} diff --git a/package-parser/tests/data/migration/annotationv2.json b/package-parser/tests/data/migration/annotationv2.json new file mode 100644 index 000000000..e2224edeb --- /dev/null +++ b/package-parser/tests/data/migration/annotationv2.json @@ -0,0 +1,50 @@ +{ + "schemaVersion": 2, + "boundaryAnnotations": {}, + "calledAfterAnnotations": {}, + "completeAnnotations": {}, + "descriptionAnnotations": {}, + "enumAnnotations": {}, + "expertAnnotations": {}, + "groupAnnotations": {}, + "moveAnnotations": {}, + "pureAnnotations": {}, + "removeAnnotations": { + "test/test/TestClass": { + "target": "test/test/TestClass", + "authors": ["$autogen$", "migration"], + "reviewers": [], + "comment": "I removed this class because it has no known usages.", + "reviewResult": "" + } + }, + "renameAnnotations": { + "test/test/test_function": { + "authors": ["$autogen$", "migration"], + "comment": "", + "newName": "renamed_test_function", + "reviewResult": "", + "reviewers": [], + "target": "test/test/test_function" + } + }, + "todoAnnotations": { + "test/test/test_function": { + "authors": ["$autogen$", "migration"], + "comment": "", + "newTodo": "this is a todo annotation", + "reviewResult": "", + "reviewers": [], + "target": "test/test/test_function" + }, + "test/test/test_function/test_parameter": { + "authors": ["$autogen$", "migration"], + "comment": "", + "newTodo": "another todo annotation", + "reviewResult": "unsure", + "reviewers": [], + "target": "test/test/test_function/test_parameter" + } + }, + "valueAnnotations": {} +} diff --git a/package-parser/tests/data/migration/apiv1_data.json b/package-parser/tests/data/migration/apiv1_data.json new file mode 100644 index 000000000..ab0d8321f --- /dev/null +++ b/package-parser/tests/data/migration/apiv1_data.json @@ -0,0 +1,91 @@ +{ + "distribution": "test", + "package": "test", + "version": "0.0.1", + "modules": [ + { + "id": "test/test", + "name": "test", + "classes": ["test/test/TestClass"], + "functions": ["test/test/test_function"] + } + ], + "classes": [ + { + "id": "test/test/TestClass", + "name": "TestClass", + "qname": "test.TestClass", + "methods": [], + "decorators": [], + "superclasses": [], + "is_public": true, + "reexported_by": [], + "documentation": "", + "code": "class TestClass:\n\"\"\" This is a TestClass.\n It has no common use.\"\"\"\n pass", + "instance_attributes": [ + { + "name": "a", + "types": { + "kind": "NamedType", + "name": "str" + } + }, + { + "name": "b", + "types": { + "kind": "NamedType", + "name": "str" + } + }, + { + "name": "c", + "types": { + "kind": "NamedType", + "name": "str" + } + } + ] + } + ], + "functions": [ + { + "id": "test/test/complete_different_function", + "qname": "test.complete_different_function", + "decorators": [], + "parameters": [ + { + "id": "test/test/test_function/test_parameter", + "name": "test_parameter", + "qname": "test.test_function.test_parameter", + "default_value": "", + "assigned_by": "POSITION_OR_NAME", + "is_public": false, + "docstring": { + "type": "", + "default_value": "", + "description": "" + }, + "type": {} + } + ], + "results": [], + "is_public": true, + "reexported_by": [], + "description": "", + "docstring": "", + "code": "def complete_different_function():\n \"\"\"This function's only use is to hold a parameter\"\"\" return None" + }, + { + "id": "test/test/test_function", + "qname": "test_function", + "decorators": [], + "parameters": [], + "results": [], + "is_public": true, + "reexported_by": [], + "description": "", + "docstring": "", + "code": "def test_function():\n pass" + } + ] +} diff --git a/package-parser/tests/data/migration/apiv2_data.json b/package-parser/tests/data/migration/apiv2_data.json new file mode 100644 index 000000000..1c3da0274 --- /dev/null +++ b/package-parser/tests/data/migration/apiv2_data.json @@ -0,0 +1,103 @@ +{ + "distribution": "test", + "package": "test", + "version": "0.0.1", + "modules": [ + { + "id": "test/test", + "name": "test", + "classes": ["test/test/NewTestClass"], + "functions": [""] + } + ], + "classes": [ + { + "id": "test/test/TestClass", + "name": "TestClass", + "qname": "test.TestClass", + "methods": [], + "decorators": [], + "superclasses": [], + "is_public": true, + "reexported_by": [], + "documentation": "", + "code": "class TestClass:\n\"\"\" This is a TestClass.\n It has no common use.\"\"\"\n pass", + "instance_attributes": [ + { + "name": "a", + "types": { + "kind": "NamedType", + "name": "str" + } + }, + { + "name": "b", + "types": { + "kind": "NamedType", + "name": "str" + } + }, + { + "name": "c", + "types": { + "kind": "NamedType", + "name": "str" + } + } + ] + } + ], + "functions": [ + { + "id": "test/test/complete_different_function", + "qname": "test.complete_different_function", + "decorators": [], + "parameters": [ + { + "id": "test/test/test_function/test_parameter", + "name": "test_parameter", + "qname": "test.test_function.test_parameter", + "default_value": "'new_optional_value'", + "assigned_by": "POSITIONAL_VARARG", + "is_public": false, + "docstring": { + "type": "", + "default_value": "", + "description": "" + }, + "type": {} + } + ], + "results": [], + "is_public": true, + "reexported_by": [], + "description": "", + "docstring": "", + "code": "def complete_different_function():\n \"\"\"This function's only use is to hold a parameter\"\"\" return None" + }, + { + "id": "test/test/test_function", + "qname": "test_function", + "decorators": [], + "parameters": [], + "results": [], + "is_public": true, + "reexported_by": [], + "description": "", + "docstring": "", + "code": "def test_function():\n pass" + }, + { + "id": "test/test/other_test_function", + "qname": "other_test_function", + "decorators": [], + "parameters": [], + "results": [], + "is_public": true, + "reexported_by": [], + "description": "", + "docstring": "", + "code": "def other_test_function():\n \"\"\"This function is longer than the other function\n but have some attributes in common.\n Therfore, they should be in the unsure annotationstore\"\"\"" + } + ] +} diff --git a/package-parser/tests/data/migration/unsure_annotationv2.json b/package-parser/tests/data/migration/unsure_annotationv2.json new file mode 100644 index 000000000..d5e776fee --- /dev/null +++ b/package-parser/tests/data/migration/unsure_annotationv2.json @@ -0,0 +1,25 @@ +{ + "schemaVersion": 2, + "boundaryAnnotations": {}, + "calledAfterAnnotations": {}, + "completeAnnotations": {}, + "descriptionAnnotations": {}, + "enumAnnotations": {}, + "expertAnnotations": {}, + "groupAnnotations": {}, + "moveAnnotations": { + "test/test/complete_different_function": { + "authors": ["$autogen$", "migration"], + "comment": "", + "destination": "test/test.moved.package", + "reviewResult": "", + "reviewers": [], + "target": "test/test/complete_different_function" + } + }, + "pureAnnotations": {}, + "removeAnnotations": {}, + "renameAnnotations": {}, + "todoAnnotations": {}, + "valueAnnotations": {} +} diff --git a/package-parser/tests/processing/migration/annotations/test_boundary_migration.py b/package-parser/tests/processing/migration/annotations/test_boundary_migration.py index 4bbf8fc5d..c200422d8 100644 --- a/package-parser/tests/processing/migration/annotations/test_boundary_migration.py +++ b/package-parser/tests/processing/migration/annotations/test_boundary_migration.py @@ -12,6 +12,7 @@ ParameterDocumentation, ) from package_parser.processing.migration import ( + ManyToOneMapping, Mapping, OneToManyMapping, OneToOneMapping, @@ -207,6 +208,7 @@ def migrate_boundary_annotation_data_one_to_one_mapping_float_to_int() -> Tuple[ ) +# pylint: disable=duplicate-code def migrate_boundary_annotation_data_one_to_many_mapping() -> Tuple[ Mapping, AbstractAnnotation, @@ -245,7 +247,7 @@ def migrate_boundary_annotation_data_one_to_many_mapping() -> Tuple[ id_="test/test.boundary.test4.testC", name="testC", qname="test.boundary.test4.testC", - default_value="", + default_value=None, assigned_by=ParameterAssignment.POSITION_OR_NAME, is_public=True, documentation=ParameterDocumentation("", "", ""), @@ -320,3 +322,84 @@ def migrate_boundary_annotation_data_one_to_many_mapping() -> Tuple[ migrated_boundary_annotation_c, ], ) + + +def migrate_boundary_annotation_data_duplicated() -> Tuple[ + Mapping, + list[AbstractAnnotation], + list[AbstractAnnotation], +]: + parameterv1 = Parameter( + id_="test/test.boundary.duplicate.testA", + name="testA", + qname="test.boundary.duplicate.testA", + default_value="1", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("int", "1", ""), + ) + parameterv1_2 = Parameter( + id_="test/test.boundary.duplicate.testA_2", + name="testA_2", + qname="test.boundary.duplicate.testA_2", + default_value="1", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("int", "1", ""), + ) + parameterv2 = Parameter( + id_="test/test.boundary.duplicate.testB", + name="testB", + qname="test.boundary.duplicate.testB", + default_value="1", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("int", "1", ""), + ) + boundary_annotation = BoundaryAnnotation( + target="test/test.boundary.duplicate.testA", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + interval=Interval( + isDiscrete=True, + lowerIntervalLimit=0, + lowerLimitType=1, + upperIntervalLimit=10, + upperLimitType=1, + ), + ) + boundary_annotation_2 = BoundaryAnnotation( + target="test/test.boundary.duplicate.testA_2", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + interval=Interval( + isDiscrete=True, + lowerIntervalLimit=0, + lowerLimitType=1, + upperIntervalLimit=10, + upperLimitType=1, + ), + ) + migrated_boundary_annotation = BoundaryAnnotation( + target="test/test.boundary.duplicate.testB", + authors=["testauthor", migration_author], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + interval=Interval( + isDiscrete=True, + lowerIntervalLimit=0, + lowerLimitType=1, + upperIntervalLimit=10, + upperLimitType=1, + ), + ) + return ( + ManyToOneMapping(1.0, [parameterv1, parameterv1_2], parameterv2), + [boundary_annotation, boundary_annotation_2], + [migrated_boundary_annotation], + ) diff --git a/package-parser/tests/processing/migration/annotations/test_called_after_migration.py b/package-parser/tests/processing/migration/annotations/test_called_after_migration.py index 091286830..ba12ead35 100644 --- a/package-parser/tests/processing/migration/annotations/test_called_after_migration.py +++ b/package-parser/tests/processing/migration/annotations/test_called_after_migration.py @@ -8,6 +8,7 @@ ) from package_parser.processing.api.model import Function, FunctionDocumentation from package_parser.processing.migration import ( + ManyToOneMapping, Mapping, OneToManyMapping, OneToOneMapping, @@ -18,6 +19,7 @@ ) +# pylint: disable=duplicate-code def migrate_called_after_annotation_data_one_to_one_mapping() -> Tuple[ list[Mapping], AbstractAnnotation, @@ -223,7 +225,7 @@ def migrate_called_after_annotation_data_one_to_one_mapping__no_mapping_found() target="test/test.called_after.test3.test/NewClass/new_test_after", authors=["testauthor", migration_author], reviewers=[], - comment="", + comment=get_migration_text(annotationv1, mapping_after), reviewResult=EnumReviewResult.UNSURE, calledAfterName="test_before", ) @@ -423,3 +425,98 @@ def migrate_called_after_annotation_data_one_to_many_mapping__two_classes() -> T annotationv1, [annotationv2_a, annotationv2_b], ) + + +def migrate_called_after_annotation_data_duplicated() -> Tuple[ + list[Mapping], + list[AbstractAnnotation], + list[AbstractAnnotation], +]: + functionv1_after = Function( + id="test/test.called_after.duplicate.test/OldClass/test_after", + qname="test.called_after.duplicate.test.OldClass.test_after", + decorators=[], + parameters=[], + results=[], + is_public=True, + reexported_by=[], + documentation=FunctionDocumentation("", ""), + code="", + ) + functionv1_after_2 = Function( + id="test/test.called_after.duplicate.test/OldClass/test_after_2", + qname="test.called_after.duplicate.test.OldClass.test_after_2", + decorators=[], + parameters=[], + results=[], + is_public=True, + reexported_by=[], + documentation=FunctionDocumentation("", ""), + code="", + ) + functionv1_before = Function( + id="test/test.called_after.duplicate.test/OldClass/test_before", + qname="test.called_after.duplicate.test.OldClass.test_before", + decorators=[], + parameters=[], + results=[], + is_public=True, + reexported_by=[], + documentation=FunctionDocumentation("", ""), + code="", + ) + functionv2_after = Function( + id="test/test.called_after.duplicate.test/NewClass/new_test_after", + qname="test.called_after.duplicate.test.NewClass.new_test_after", + decorators=[], + parameters=[], + results=[], + is_public=True, + reexported_by=[], + documentation=FunctionDocumentation("", ""), + code="", + ) + functionv2_before = Function( + id="test/test.called_after.duplicate.test/NewClass/new_test_before", + qname="test.called_after.duplicate.test.NewClass.new_test_before", + decorators=[], + parameters=[], + results=[], + is_public=True, + reexported_by=[], + documentation=FunctionDocumentation("", ""), + code="", + ) + mapping_after = ManyToOneMapping( + 1.0, [functionv1_after, functionv1_after_2], functionv2_after + ) + mapping_before = OneToManyMapping(1.0, functionv1_before, [functionv2_before]) + annotationv1 = CalledAfterAnnotation( + target="test/test.called_after.duplicate.test/OldClass/test_after", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + calledAfterName="test_before", + ) + annotationv1_2 = CalledAfterAnnotation( + target="test/test.called_after.duplicate.test/OldClass/test_after_2", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + calledAfterName="test_before", + ) + annotationv2 = CalledAfterAnnotation( + target="test/test.called_after.duplicate.test/NewClass/new_test_after", + authors=["testauthor", migration_author], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + calledAfterName="new_test_before", + ) + return ( + [mapping_after, mapping_before], + [annotationv1, annotationv1_2], + [annotationv2], + ) diff --git a/package-parser/tests/processing/migration/annotations/test_description_migration.py b/package-parser/tests/processing/migration/annotations/test_description_migration.py index 76ced6a95..9de9836d8 100644 --- a/package-parser/tests/processing/migration/annotations/test_description_migration.py +++ b/package-parser/tests/processing/migration/annotations/test_description_migration.py @@ -16,6 +16,7 @@ ParameterDocumentation, ) from package_parser.processing.migration import ( + ManyToOneMapping, Mapping, OneToManyMapping, OneToOneMapping, @@ -161,7 +162,7 @@ def migrate_description_annotation_data_one_to_many_mapping__class() -> Tuple[ reviewers=[], comment="", reviewResult=EnumReviewResult.NONE, - newTodo=get_migration_text(annotationv1, mapping), + newTodo=get_migration_text(annotationv1, mapping, for_todo_annotation=True), ) return mapping, annotationv1, [annotationv2_a, annotationv2_b, annotationv2_c] @@ -210,3 +211,72 @@ def migrate_description_annotation_data_one_to_one_mapping__parameter() -> Tuple newDescription="test description", ) return mapping, annotationv1, [annotationv2] + + +def migrate_description_annotation_data_duplicated() -> Tuple[ + Mapping, + list[AbstractAnnotation], + list[AbstractAnnotation], +]: + functionv1 = Function( + id="test/test.description.duplicate.test/test", + qname="test.description.duplicate.test.test", + decorators=[], + parameters=[], + results=[], + is_public=True, + reexported_by=[], + documentation=FunctionDocumentation("", ""), + code="", + ) + functionv1_2 = Function( + id="test/test.description.duplicate.test/test_2", + qname="test.description.duplicate.test.test_2", + decorators=[], + parameters=[], + results=[], + is_public=True, + reexported_by=[], + documentation=FunctionDocumentation("", ""), + code="", + ) + + functionv2 = Function( + id="test/test.description.duplicate.test/new_test", + qname="test.description.duplicate.test.new_test", + decorators=[], + parameters=[], + results=[], + is_public=True, + reexported_by=[], + documentation=FunctionDocumentation("", ""), + code="", + ) + + mapping = ManyToOneMapping(1.0, [functionv1, functionv1_2], functionv2) + + annotationv1 = DescriptionAnnotation( + target="test/test.description.duplicate.test/test", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + newDescription="lightbringer", + ) + annotationv1_2 = DescriptionAnnotation( + target="test/test.description.duplicate.test/test_2", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + newDescription="lightbringer", + ) + annotationv2 = DescriptionAnnotation( + target="test/test.description.duplicate.test/new_test", + authors=["testauthor", migration_author], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + newDescription="lightbringer", + ) + return mapping, [annotationv1, annotationv1_2], [annotationv2] diff --git a/package-parser/tests/processing/migration/annotations/test_enum_migration.py b/package-parser/tests/processing/migration/annotations/test_enum_migration.py index fa24b7822..63896bfb4 100644 --- a/package-parser/tests/processing/migration/annotations/test_enum_migration.py +++ b/package-parser/tests/processing/migration/annotations/test_enum_migration.py @@ -5,7 +5,6 @@ EnumAnnotation, EnumPair, EnumReviewResult, - TodoAnnotation, ) from package_parser.processing.api.model import ( Parameter, @@ -13,6 +12,7 @@ ParameterDocumentation, ) from package_parser.processing.migration import ( + ManyToOneMapping, Mapping, OneToManyMapping, OneToOneMapping, @@ -135,6 +135,7 @@ def migrate_enum_annotation_data_one_to_many_mapping() -> Tuple[ ) +# pylint: disable=duplicate-code def migrate_enum_annotation_data_one_to_many_mapping__only_one_relevant_mapping() -> Tuple[ Mapping, AbstractAnnotation, @@ -153,7 +154,7 @@ def migrate_enum_annotation_data_one_to_many_mapping__only_one_relevant_mapping( id_="test/test.enum.test3.TestA", name="TestA", qname="test.enum.test3.TestA", - default_value="", + default_value=None, assigned_by=ParameterAssignment.POSITION_OR_NAME, is_public=True, documentation=ParameterDocumentation("", "", ""), @@ -188,7 +189,7 @@ def migrate_enum_annotation_data_one_to_many_mapping__only_one_relevant_mapping( enumName="EnumName", pairs=[EnumPair("value", "name")], ) - migrated_enum_annotation = EnumAnnotation( + migrated_enum_annotation_b = EnumAnnotation( target="test/test.enum.test3.TestB", authors=["testauthor", migration_author], reviewers=[], @@ -197,16 +198,80 @@ def migrate_enum_annotation_data_one_to_many_mapping__only_one_relevant_mapping( enumName="EnumName", pairs=[EnumPair("value", "name")], ) - migrated_todo_annotation = TodoAnnotation( + migrated_enum_annotation_a = EnumAnnotation( target="test/test.enum.test3.TestA", authors=["testauthor", migration_author], reviewers=[], reviewResult=EnumReviewResult.UNSURE, - comment="", - newTodo=get_migration_text(enum_annotation, mapping), + comment=get_migration_text(enum_annotation, mapping), + enumName="EnumName", + pairs=[EnumPair("value", "name")], ) return ( mapping, enum_annotation, - [migrated_enum_annotation, migrated_todo_annotation], + [migrated_enum_annotation_b, migrated_enum_annotation_a], + ) + + +def migrate_enum_annotation_data_duplicated() -> Tuple[ + Mapping, + list[AbstractAnnotation], + list[AbstractAnnotation], +]: + parameterv1 = Parameter( + id_="test/test.enum.duplicate.TestA", + name="TestA", + qname="test.enum.duplicate.TestA", + default_value="value", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("str", "value", "docstring"), + ) + parameterv1_2 = Parameter( + id_="test/test.enum.duplicate.TestA_2", + name="TestA_2", + qname="test.enum.duplicate.TestA_2", + default_value="value", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("str", "value", "docstring"), + ) + parameterv2 = Parameter( + id_="test/test.enum.duplicate.TestB", + name="TestB", + qname="test.enum.duplicate.TestB", + default_value="value", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("str", "value", "docstring"), + ) + mapping = ManyToOneMapping(1.0, [parameterv1, parameterv1_2], parameterv2) + enum_annotation = EnumAnnotation( + target="test/test.enum.duplicate.TestA", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + enumName="EnumName", + pairs=[EnumPair("value", "name")], + ) + enum_annotation_2 = EnumAnnotation( + target="test/test.enum.duplicate.TestA_2", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + enumName="EnumName", + pairs=[EnumPair("value", "name")], + ) + migrated_enum_annotation = EnumAnnotation( + target="test/test.enum.duplicate.TestB", + authors=["testauthor", migration_author], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + enumName="EnumName", + pairs=[EnumPair("value", "name")], ) + return mapping, [enum_annotation, enum_annotation_2], [migrated_enum_annotation] diff --git a/package-parser/tests/processing/migration/annotations/test_expert_migration.py b/package-parser/tests/processing/migration/annotations/test_expert_migration.py index 14f289b6c..bd47fbc31 100644 --- a/package-parser/tests/processing/migration/annotations/test_expert_migration.py +++ b/package-parser/tests/processing/migration/annotations/test_expert_migration.py @@ -16,6 +16,7 @@ ParameterDocumentation, ) from package_parser.processing.migration import ( + ManyToOneMapping, Mapping, OneToManyMapping, OneToOneMapping, @@ -26,6 +27,7 @@ ) +# pylint: disable=duplicate-code def migrate_expert_annotation_data__function() -> Tuple[ Mapping, AbstractAnnotation, @@ -136,7 +138,7 @@ def migrate_expert_annotation_data__class() -> Tuple[ reviewers=[], comment="", reviewResult=EnumReviewResult.NONE, - newTodo=get_migration_text(annotationv1, mapping), + newTodo=get_migration_text(annotationv1, mapping, for_todo_annotation=True), ) return mapping, annotationv1, [annotationv2, annotationv2_function] @@ -180,3 +182,69 @@ def migrate_expert_annotation_data__parameter() -> Tuple[ reviewResult=EnumReviewResult.NONE, ) return mapping, annotationv1, [annotationv2] + + +def migrate_expert_annotation_data_duplicated() -> Tuple[ + Mapping, + list[AbstractAnnotation], + list[AbstractAnnotation], +]: + functionv1 = Function( + id="test/test.expert.duplicate.test/test", + qname="test.expert.duplicate.test.test", + decorators=[], + parameters=[], + results=[], + is_public=True, + reexported_by=[], + documentation=FunctionDocumentation("", ""), + code="", + ) + functionv1_2 = Function( + id="test/test.expert.duplicate.test/test_2", + qname="test.expert.duplicate.test.test_2", + decorators=[], + parameters=[], + results=[], + is_public=True, + reexported_by=[], + documentation=FunctionDocumentation("", ""), + code="", + ) + + functionv2 = Function( + id="test/test.expert.duplicate.test/new_test", + qname="test.expert.duplicate.test.new_test", + decorators=[], + parameters=[], + results=[], + is_public=True, + reexported_by=[], + documentation=FunctionDocumentation("", ""), + code="", + ) + + mapping = ManyToOneMapping(1.0, [functionv1, functionv1_2], functionv2) + + annotationv1 = ExpertAnnotation( + target="test/test.expert.duplicate.test/test", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + ) + annotationv1_2 = ExpertAnnotation( + target="test/test.expert.duplicate.test/test_2", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + ) + annotationv2 = ExpertAnnotation( + target="test/test.expert.duplicate.test/new_test", + authors=["testauthor", migration_author], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + ) + return mapping, [annotationv1, annotationv1_2], [annotationv2] diff --git a/package-parser/tests/processing/migration/annotations/test_group_annotation.py b/package-parser/tests/processing/migration/annotations/test_group_annotation.py index b866bea5d..65ac4022f 100644 --- a/package-parser/tests/processing/migration/annotations/test_group_annotation.py +++ b/package-parser/tests/processing/migration/annotations/test_group_annotation.py @@ -17,6 +17,7 @@ ) from package_parser.processing.migration import ( ManyToManyMapping, + ManyToOneMapping, Mapping, OneToManyMapping, OneToOneMapping, @@ -249,7 +250,11 @@ def migrate_group_annotation_data_one_to_many_mapping() -> Tuple[ target="test/test.group.test2.test/NewTestClass/test", authors=["testauthor", migration_author], reviewers=[], - comment=get_migration_text(annotation, mapping_function), + comment=get_migration_text( + annotation, + mapping_function, + additional_information=[parameterv2_2_a, parameterv2_2_c], + ), reviewResult=EnumReviewResult.UNSURE, groupName="GroupName5", parameters=["new_parameter_a", "new_parameter_c"], @@ -258,7 +263,11 @@ def migrate_group_annotation_data_one_to_many_mapping() -> Tuple[ target="test/test.group.test3.test/NewTestClass/test", authors=["testauthor", migration_author], reviewers=[], - comment=get_migration_text(annotation, mapping_function), + comment=get_migration_text( + annotation, + mapping_function, + additional_information=[parameterv2_3_b, parameterv2_3_c], + ), reviewResult=EnumReviewResult.UNSURE, groupName="GroupName6", parameters=["new_parameter_b", "new_parameter_c"], @@ -270,7 +279,10 @@ def migrate_group_annotation_data_one_to_many_mapping() -> Tuple[ comment="", reviewResult=EnumReviewResult.NONE, newTodo=get_migration_text( - annotation, mapping_function, additional_information=[parameterv2_4_b] + annotation, + mapping_function, + for_todo_annotation=True, + additional_information=[parameterv2_4_b], ), ) migrated_annotation_5 = TodoAnnotation( @@ -279,7 +291,9 @@ def migrate_group_annotation_data_one_to_many_mapping() -> Tuple[ reviewers=[], comment="", reviewResult=EnumReviewResult.NONE, - newTodo=get_migration_text(annotation, mapping_function), + newTodo=get_migration_text( + annotation, mapping_function, for_todo_annotation=True + ), ) migrated_annotation_6 = TodoAnnotation( target="test/test.group.test6.test/NewClass", @@ -287,7 +301,9 @@ def migrate_group_annotation_data_one_to_many_mapping() -> Tuple[ reviewers=[], comment="", reviewResult=EnumReviewResult.NONE, - newTodo=get_migration_text(annotation, mapping_function), + newTodo=get_migration_text( + annotation, mapping_function, for_todo_annotation=True + ), ) return ( [ @@ -493,3 +509,177 @@ def migrate_group_annotation_data_one_to_one_mapping__one_mapping_for_parameters annotation, [migrated_annotation], ) + + +def migrate_group_annotation_data_duplicated() -> Tuple[ + list[Mapping], + list[AbstractAnnotation], + list[AbstractAnnotation], +]: + parameterv1_a = Parameter( + id_="test/test.group.duplicate.test/TestClass/test/parameter_a", + name="parameter_a", + qname="test.group.duplicate.test.TestClass.test.parameter_a", + default_value="1", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("int", "1", "int in the range of (0, 10)"), + ) + parameterv1_b = Parameter( + id_="test/test.group.duplicate.test/TestClass/test/parameter_b", + name="parameter_b", + qname="test.group.duplicate.test.TestClass.test.parameter_b", + default_value="'test'", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("str", "'test'", "str"), + ) + parameterv1_c = Parameter( + id_="test/test.group.duplicate.test/TestClass/test/parameter_c", + name="parameter_c", + qname="test.group.duplicate.test.TestClass.test.parameter_c", + default_value="'test_c'", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("str", "'test_c'", "str"), + ) + parameterv1_a_2 = Parameter( + id_="test/test.group.duplicate.test/TestClass/test_2/parameter_a_2", + name="parameter_a_2", + qname="test.group.duplicate.test.TestClass.test_2.parameter_a_2", + default_value="1", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("int", "1", "int in the range of (0, 10)"), + ) + parameterv1_b_2 = Parameter( + id_="test/test.group.duplicate.test/TestClass/test_2/parameter_b_2", + name="parameter_b_2", + qname="test.group.duplicate.test.TestClass.test_2.parameter_b_2", + default_value="'test'", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("str", "'test'", "str"), + ) + parameterv1_c_2 = Parameter( + id_="test/test.group.duplicate.test/TestClass/test_2/parameter_c_2", + name="parameter_c_2", + qname="test.group.duplicate.test.TestClass.test_2.parameter_c_2", + default_value="'test_c'", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("str", "'test_c'", "str"), + ) + functionv1 = Function( + id="test/test.group.duplicate.test/TestClass/test", + qname="test.group.duplicate.test.TestClass.test", + decorators=[], + parameters=[parameterv1_a, parameterv1_b, parameterv1_c], + results=[], + is_public=True, + reexported_by=[], + documentation=FunctionDocumentation("", ""), + code="", + ) + functionv1_2 = Function( + id="test/test.group.duplicate.test/TestClass/test_2", + qname="test.group.duplicate.test.TestClass.test_2", + decorators=[], + parameters=[parameterv1_a_2, parameterv1_b_2, parameterv1_c_2], + results=[], + is_public=True, + reexported_by=[], + documentation=FunctionDocumentation("", ""), + code="", + ) + + parameterv2_a = Parameter( + id_="test/test.group.duplicate.test/NewTestClass/test/new_parameter_a", + name="new_parameter_a", + qname="test.group.duplicate.test.NewTestClass.test.new_parameter_a", + default_value="1", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("int", "1", "int in the range of (0, 10)"), + ) + parameterv2_b = Parameter( + id_="test/test.group.duplicate.test/NewTestClass/test/new_parameter_b", + name="new_parameter_b", + qname="test.group.duplicate.test.NewTestClass.test.new_parameter_b", + default_value="'test'", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("str", "'test'", "str"), + ) + parameterv2_c = Parameter( + id_="test/test.group.duplicate.test/NewTestClass/test/new_parameter_c", + name="new_parameter_c", + qname="test.group.duplicate.test.NewTestClass.test.new_parameter_c", + default_value="'test_c'", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("str", "'test_c'", "str"), + ) + functionv2 = Function( + id="test/test.group.duplicate.test/NewTestClass/test", + qname="test.group.duplicate.test.NewTestClass.test", + decorators=[], + parameters=[parameterv2_a, parameterv2_b, parameterv2_c], + results=[], + is_public=True, + reexported_by=[], + documentation=FunctionDocumentation("", ""), + code="", + ) + + mapping_function = ManyToOneMapping( + 1.0, + [functionv1, functionv1_2], + functionv2, + ) + mapping_parameter_a = ManyToOneMapping( + 1.0, [parameterv1_a, parameterv1_a_2], parameterv2_a + ) + mapping_parameter_b = ManyToOneMapping( + 1.0, [parameterv1_b, parameterv1_b_2], parameterv2_b + ) + mapping_parameter_c = ManyToOneMapping( + 1.0, [parameterv1_c, parameterv1_c_2], parameterv2_c + ) + annotation = GroupAnnotation( + target="test/test.group.duplicate.test/TestClass/test", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + groupName="GroupName", + parameters=["parameter_a", "parameter_b", "parameter_c"], + ) + annotation_2 = GroupAnnotation( + target="test/test.group.duplicate.test/TestClass/test_2", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + groupName="GroupName", + parameters=["parameter_a_2", "parameter_b_2", "parameter_c_2"], + ) + migrated_annotation_1 = GroupAnnotation( + target="test/test.group.duplicate.test/NewTestClass/test", + authors=["testauthor", migration_author], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + groupName="GroupName", + parameters=["new_parameter_a", "new_parameter_b", "new_parameter_c"], + ) + return ( + [ + mapping_function, + mapping_parameter_a, + mapping_parameter_b, + mapping_parameter_c, + ], + [annotation, annotation_2], + [migrated_annotation_1], + ) diff --git a/package-parser/tests/processing/migration/annotations/test_move_migration.py b/package-parser/tests/processing/migration/annotations/test_move_migration.py index ed0860178..f6284aae5 100644 --- a/package-parser/tests/processing/migration/annotations/test_move_migration.py +++ b/package-parser/tests/processing/migration/annotations/test_move_migration.py @@ -13,6 +13,7 @@ FunctionDocumentation, ) from package_parser.processing.migration import ( + ManyToOneMapping, Mapping, OneToManyMapping, OneToOneMapping, @@ -189,7 +190,76 @@ def migrate_move_annotation_data_one_to_many_mapping() -> Tuple[ authors=["testauthor", migration_author], reviewers=[], comment="", - reviewResult=EnumReviewResult.UNSURE, - newTodo=get_migration_text(annotationv1, mapping), + reviewResult=EnumReviewResult.NONE, + newTodo=get_migration_text(annotationv1, mapping, for_todo_annotation=True), ) return mapping, annotationv1, [annotationv2_a, annotationv2_b] + + +def migrate_move_annotation_data_one_to_one_mapping_duplicated() -> Tuple[ + Mapping, + list[AbstractAnnotation], + list[AbstractAnnotation], +]: + functionv1 = Function( + id="test/test.move.duplicate.test/test", + qname="test.move.duplicate.test.test", + decorators=[], + parameters=[], + results=[], + is_public=True, + reexported_by=[], + documentation=FunctionDocumentation("", ""), + code="", + ) + functionv1_2 = Function( + id="test/test.move.duplicate.test/test_2", + qname="test.move.duplicate.test.test_2", + decorators=[], + parameters=[], + results=[], + is_public=True, + reexported_by=[], + documentation=FunctionDocumentation("", ""), + code="", + ) + + functionv2 = Function( + id="test/test.move.duplicate.test/new_test", + qname="test.move.duplicate.test.new_test", + decorators=[], + parameters=[], + results=[], + is_public=True, + reexported_by=[], + documentation=FunctionDocumentation("", ""), + code="", + ) + + mapping = ManyToOneMapping(1.0, [functionv1, functionv1_2], functionv2) + + annotationv1 = MoveAnnotation( + target="test/test.move.duplicate.test/test", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + destination="test.move.duplicate.destination", + ) + annotationv1_2 = MoveAnnotation( + target="test/test.move.duplicate.test/test_2", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + destination="test.move.duplicate.destination", + ) + annotationv2 = MoveAnnotation( + target="test/test.move.duplicate.test/new_test", + authors=["testauthor", migration_author], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + destination="test.move.duplicate.destination", + ) + return mapping, [annotationv1, annotationv1_2], [annotationv2] diff --git a/package-parser/tests/processing/migration/annotations/test_remove_migration.py b/package-parser/tests/processing/migration/annotations/test_remove_migration.py index f8699a33d..b81ac23b7 100644 --- a/package-parser/tests/processing/migration/annotations/test_remove_migration.py +++ b/package-parser/tests/processing/migration/annotations/test_remove_migration.py @@ -13,6 +13,7 @@ FunctionDocumentation, ) from package_parser.processing.migration import ( + ManyToOneMapping, Mapping, OneToManyMapping, OneToOneMapping, @@ -153,6 +154,72 @@ def migrate_remove_annotation_data_one_to_many_mapping() -> Tuple[ reviewers=[], comment="", reviewResult=EnumReviewResult.NONE, - newTodo=get_migration_text(annotationv1, mapping), + newTodo=get_migration_text(annotationv1, mapping, for_todo_annotation=True), ) return mapping, annotationv1, [annotationv2_a, annotationv2_b, annotationv2_c] + + +def migrate_remove_annotation_data_duplicated() -> Tuple[ + Mapping, + list[AbstractAnnotation], + list[AbstractAnnotation], +]: + functionv1 = Function( + id="test/test.remove.duplicate.test/test", + qname="test.remove.duplicate.test.test", + decorators=[], + parameters=[], + results=[], + is_public=True, + reexported_by=[], + documentation=FunctionDocumentation("", ""), + code="", + ) + functionv1_2 = Function( + id="test/test.remove.duplicate.test/test_2", + qname="test.remove.duplicate.test.test_2", + decorators=[], + parameters=[], + results=[], + is_public=True, + reexported_by=[], + documentation=FunctionDocumentation("", ""), + code="", + ) + + functionv2 = Function( + id="test/test.remove.duplicate.test/new_test", + qname="test.remove.duplicate.test.new_test", + decorators=[], + parameters=[], + results=[], + is_public=True, + reexported_by=[], + documentation=FunctionDocumentation("", ""), + code="", + ) + + mapping = ManyToOneMapping(1.0, [functionv1, functionv1_2], functionv2) + + annotationv1 = RemoveAnnotation( + target="test/test.remove.duplicate.test/test", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + ) + annotationv1_2 = RemoveAnnotation( + target="test/test.remove.duplicate.test/test_2", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + ) + annotationv2 = RemoveAnnotation( + target="test/test.remove.duplicate.test/new_test", + authors=["testauthor", migration_author], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + ) + return mapping, [annotationv1, annotationv1_2], [annotationv2] diff --git a/package-parser/tests/processing/migration/annotations/test_rename_migration.py b/package-parser/tests/processing/migration/annotations/test_rename_migration.py index e63ea7ed2..374b5eeb1 100644 --- a/package-parser/tests/processing/migration/annotations/test_rename_migration.py +++ b/package-parser/tests/processing/migration/annotations/test_rename_migration.py @@ -7,6 +7,8 @@ TodoAnnotation, ) from package_parser.processing.api.model import ( + Class, + ClassDocumentation, Parameter, ParameterAssignment, ParameterDocumentation, @@ -16,6 +18,7 @@ migration_author, ) from package_parser.processing.migration.model import ( + ManyToOneMapping, Mapping, OneToManyMapping, OneToOneMapping, @@ -65,117 +68,147 @@ def migrate_rename_annotation_data_one_to_one_mapping() -> Tuple[ return mappings, annotationv1, [annotationv2] -def migrate_rename_annotation_data_one_to_many_mapping__with_changed_new_name() -> Tuple[ +# pylint: disable=duplicate-code +def migrate_rename_annotation_data_one_to_many_mapping() -> Tuple[ Mapping, AbstractAnnotation, list[AbstractAnnotation], ]: parameterv1 = Parameter( - id_="test/test.rename.test2.Test", + id_="test/test.rename.test3.Test", name="Test", - qname="test.rename.test2.Test", + qname="test.rename.test3.Test", default_value=None, assigned_by=ParameterAssignment.POSITION_OR_NAME, is_public=True, documentation=ParameterDocumentation("", "", ""), ) parameterv2_a = Parameter( - id_="test/test.rename.test2.TestA", + id_="test/test.rename.test3.TestA", name="TestA", - qname="test.rename.test2.TestA", + qname="test.rename.test3.TestA", default_value=None, assigned_by=ParameterAssignment.POSITION_OR_NAME, is_public=True, documentation=ParameterDocumentation("", "", ""), ) parameterv2_b = Parameter( - id_="test/test.rename.test2.TestB", - name="TestB", - qname="test.rename.test2.TestB", + id_="test/test.rename.test3.Test", + name="Test", + qname="test.rename.test3.Test", default_value=None, assigned_by=ParameterAssignment.POSITION_OR_NAME, is_public=True, documentation=ParameterDocumentation("", "", ""), ) - mappings = OneToManyMapping(1.0, parameterv1, [parameterv2_a, parameterv2_b]) + classv2 = Class( + id_="test/test.rename.test3/NewClass", + qname="test.rename.test3.NewClass", + decorators=[], + superclasses=[], + is_public=True, + reexported_by=[], + documentation=ClassDocumentation("", ""), + code="class NewClass:\n pass", + instance_attributes=[], + ) + mappings = OneToManyMapping( + 1.0, parameterv1, [parameterv2_a, parameterv2_b, classv2] + ) annotationv1 = RenameAnnotation( - target="test/test.rename.test2.Test", + target="test/test.rename.test3.Test", authors=["testauthor"], reviewers=[], comment="", reviewResult=EnumReviewResult.NONE, - newName="TestA", + newName="TestZ", ) - annotationv2 = RenameAnnotation( - target="test/test.rename.test2.TestA", + annotationv2_a = RenameAnnotation( + target="test/test.rename.test3.TestA", authors=["testauthor", migration_author], reviewers=[], comment=get_migration_text(annotationv1, mappings), reviewResult=EnumReviewResult.UNSURE, - newName="TestA", + newName="TestZ", + ) + annotationv2_b = RenameAnnotation( + target="test/test.rename.test3.Test", + authors=["testauthor", migration_author], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + newName="TestZ", + ) + annotationv2_c = TodoAnnotation( + target="test/test.rename.test3/NewClass", + authors=["testauthor", migration_author], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + newTodo=get_migration_text(annotationv1, mappings, for_todo_annotation=True), + ) + return ( + mappings, + annotationv1, + [annotationv2_a, annotationv2_b, annotationv2_c], ) - return mappings, annotationv1, [annotationv2] -def migrate_rename_annotation_data_one_to_many_mapping() -> Tuple[ +def migrate_rename_annotation_data_duplicated() -> Tuple[ Mapping, - AbstractAnnotation, + list[AbstractAnnotation], list[AbstractAnnotation], ]: parameterv1 = Parameter( - id_="test/test.rename.test3.Test", + id_="test/test.rename.duplicate.Test_", name="Test", - qname="test.rename.test3.Test", + qname="test.rename.duplicate.Test_", default_value=None, assigned_by=ParameterAssignment.POSITION_OR_NAME, is_public=True, documentation=ParameterDocumentation("", "", ""), ) - parameterv2_a = Parameter( - id_="test/test.rename.test3.TestA", - name="TestA", - qname="test.rename.test3.TestA", + parameterv1_2 = Parameter( + id_="test/test.rename.duplicate.Test_2", + name="Test", + qname="test.rename.duplicate.Test_2", default_value=None, assigned_by=ParameterAssignment.POSITION_OR_NAME, is_public=True, documentation=ParameterDocumentation("", "", ""), ) - parameterv2_b = Parameter( - id_="test/test.rename.test3.TestB", + parameterv2 = Parameter( + id_="test/test.rename.duplicate.TestB", name="TestB", - qname="test.rename.test3.TestB", + qname="test.rename.duplicate.TestB", default_value=None, assigned_by=ParameterAssignment.POSITION_OR_NAME, is_public=True, documentation=ParameterDocumentation("", "", ""), ) - mappings = OneToManyMapping(1.0, parameterv1, [parameterv2_a, parameterv2_b]) + mappings = ManyToOneMapping(1.0, [parameterv1, parameterv1_2], parameterv2) annotationv1 = RenameAnnotation( - target="test/test.rename.test3.Test", + target="test/test.rename.duplicate.Test_", authors=["testauthor"], reviewers=[], comment="", reviewResult=EnumReviewResult.NONE, - newName="TestZ", + newName="TestE", ) - annotationv2_a = TodoAnnotation( - target="test/test.rename.test3.TestA", - authors=["testauthor", migration_author], + annotationv1_2 = RenameAnnotation( + target="test/test.rename.duplicate.Test_2", + authors=["testauthor"], reviewers=[], comment="", reviewResult=EnumReviewResult.NONE, - newTodo=get_migration_text(annotationv1, mappings), + newName="TestE", ) - annotationv2_b = TodoAnnotation( - target="test/test.rename.test3.TestB", + annotationv2 = RenameAnnotation( + target="test/test.rename.duplicate.TestB", authors=["testauthor", migration_author], reviewers=[], comment="", reviewResult=EnumReviewResult.NONE, - newTodo=get_migration_text(annotationv1, mappings), - ) - return ( - mappings, - annotationv1, - [annotationv2_a, annotationv2_b], + newName="TestE", ) + return mappings, [annotationv1, annotationv1_2], [annotationv2] diff --git a/package-parser/tests/processing/migration/annotations/test_todo_migration.py b/package-parser/tests/processing/migration/annotations/test_todo_migration.py index 871d34bcf..d7bc41427 100644 --- a/package-parser/tests/processing/migration/annotations/test_todo_migration.py +++ b/package-parser/tests/processing/migration/annotations/test_todo_migration.py @@ -12,12 +12,13 @@ ParameterAssignment, ParameterDocumentation, ) -from package_parser.processing.migration import ManyToManyMapping from package_parser.processing.migration.annotations import ( get_migration_text, migration_author, ) from package_parser.processing.migration.model import ( + ManyToManyMapping, + ManyToOneMapping, Mapping, OneToManyMapping, OneToOneMapping, @@ -29,7 +30,7 @@ def migrate_todo_annotation_data_one_to_one_mapping() -> Tuple[ ]: parameterv1 = Parameter( id_="test/test.todo.test1.Test", - name="Test1", + name="Test", qname="test.todo.test1.Test", default_value=None, assigned_by=ParameterAssignment.POSITION_OR_NAME, @@ -208,11 +209,69 @@ def migrate_todo_annotation_data_many_to_many_mapping() -> Tuple[ authors=["testauthor", migration_author], reviewers=[], comment="", - reviewResult=EnumReviewResult.UNSURE, - newTodo=get_migration_text(annotationv1, mappings), + reviewResult=EnumReviewResult.NONE, + newTodo=get_migration_text(annotationv1, mappings, for_todo_annotation=True), ) return ( mappings, annotationv1, [annotationv2_a, annotationv2_b, annotationv2_class], ) + + +def migrate_todo_annotation_data_duplicated() -> Tuple[ + Mapping, list[AbstractAnnotation], list[AbstractAnnotation] +]: + parameterv1 = Parameter( + id_="test/test.todo.duplicate.Test", + name="Test", + qname="test.todo.duplicate.Test", + default_value=None, + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("str", "", ""), + ) + parameterv1_2 = Parameter( + id_="test/test.todo.duplicate.Test_2", + name="Test_2", + qname="test.todo.duplicate.Test_2", + default_value=None, + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("str", "", ""), + ) + parameterv2 = Parameter( + id_="test/test.todo.duplicate.Test", + name="Test", + qname="test.todo.duplicate.Test", + default_value=None, + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("str", "", ""), + ) + mappings = ManyToOneMapping(1.0, [parameterv1, parameterv1_2], parameterv2) + annotationsv1 = TodoAnnotation( + target="test/test.todo.duplicate.Test", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + newTodo="todo", + ) + annotationsv1_2 = TodoAnnotation( + target="test/test.todo.duplicate.Test_2", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + newTodo="todo", + ) + annotationsv2 = TodoAnnotation( + target="test/test.todo.duplicate.Test", + authors=["testauthor", migration_author], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + newTodo="todo", + ) + return mappings, [annotationsv1, annotationsv1_2], [annotationsv2] diff --git a/package-parser/tests/processing/migration/annotations/test_value_migration.py b/package-parser/tests/processing/migration/annotations/test_value_migration.py index 6c1b32912..259f212bd 100644 --- a/package-parser/tests/processing/migration/annotations/test_value_migration.py +++ b/package-parser/tests/processing/migration/annotations/test_value_migration.py @@ -18,6 +18,7 @@ ParameterDocumentation, ) from package_parser.processing.migration import ( + ManyToOneMapping, Mapping, OneToManyMapping, OneToOneMapping, @@ -51,6 +52,7 @@ def migrate_constant_annotation_data_one_to_one_mapping() -> Tuple[ is_public=True, documentation=ParameterDocumentation("str", "'test string'", ""), ) + mapping = OneToOneMapping(1.0, parameterv1, parameterv2) annotation = ConstantAnnotation( target="test/test.value.test1.testA", authors=["testauthor"], @@ -64,12 +66,12 @@ def migrate_constant_annotation_data_one_to_one_mapping() -> Tuple[ target="test/test.value.test1.testB", authors=["testauthor", migration_author], reviewers=[], - comment="", - reviewResult=EnumReviewResult.NONE, + comment=get_migration_text(annotation, mapping), + reviewResult=EnumReviewResult.UNSURE, defaultValueType=ValueAnnotation.DefaultValueType.STRING, defaultValue="This is a string", ) - return OneToOneMapping(1.0, parameterv1, parameterv2), annotation, [annotationv2] + return mapping, annotation, [annotationv2] def migrate_omitted_annotation_data_one_to_one_mapping() -> Tuple[ @@ -224,7 +226,7 @@ def migrate_constant_annotation_data_one_to_many_mapping() -> Tuple[ id_="test/test.value.test5.testB", name="testB", qname="test.value.test5.testB", - default_value="", + default_value=None, assigned_by=ParameterAssignment.POSITION_OR_NAME, is_public=True, documentation=ParameterDocumentation("", "", ""), @@ -269,32 +271,31 @@ def migrate_constant_annotation_data_one_to_many_mapping() -> Tuple[ authors=["testauthor", "migration"], reviewers=[], comment="", - reviewResult=EnumReviewResult.UNSURE, + reviewResult=EnumReviewResult.NONE, newTodo=get_migration_text(annotation, mapping), ) - annotationv2_b = ConstantAnnotation( + annotationv2_b = TodoAnnotation( target="test/test.value.test5.testB", authors=["testauthor", migration_author], reviewers=[], - comment=get_migration_text(annotation, mapping), - reviewResult=EnumReviewResult.UNSURE, - defaultValueType=ValueAnnotation.DefaultValueType.NUMBER, - defaultValue="2.0", + comment="", + reviewResult=EnumReviewResult.NONE, + newTodo=get_migration_text(annotation, mapping, for_todo_annotation=True), ) annotationv2_c = TodoAnnotation( target="test/test.value.test5.testC", authors=["testauthor", migration_author], reviewers=[], comment="", - reviewResult=EnumReviewResult.UNSURE, - newTodo=get_migration_text(annotation, mapping), + reviewResult=EnumReviewResult.NONE, + newTodo=get_migration_text(annotation, mapping, for_todo_annotation=True), ) annotationv2_d = ConstantAnnotation( target="test/test.value.test5.testD", authors=["testauthor", migration_author], reviewers=[], - comment="", - reviewResult=EnumReviewResult.NONE, + comment=get_migration_text(annotation, mapping), + reviewResult=EnumReviewResult.UNSURE, defaultValueType=ValueAnnotation.DefaultValueType.NUMBER, defaultValue="2.0", ) @@ -334,7 +335,7 @@ def migrate_optional_annotation_data_one_to_many_mapping() -> Tuple[ id_="test/test.value.test6.testB", name="testB", qname="test.value.test6.testB", - default_value="", + default_value=None, assigned_by=ParameterAssignment.POSITION_OR_NAME, is_public=True, documentation=ParameterDocumentation("", "", ""), @@ -376,8 +377,8 @@ def migrate_optional_annotation_data_one_to_many_mapping() -> Tuple[ authors=["testauthor", migration_author], reviewers=[], comment="", - newTodo=get_migration_text(annotation, mapping), - reviewResult=EnumReviewResult.UNSURE, + newTodo=get_migration_text(annotation, mapping, for_todo_annotation=True), + reviewResult=EnumReviewResult.NONE, ) annotationv2_b = OptionalAnnotation( target="test/test.value.test6.testB", @@ -393,15 +394,15 @@ def migrate_optional_annotation_data_one_to_many_mapping() -> Tuple[ authors=["testauthor", migration_author], reviewers=[], comment="", - reviewResult=EnumReviewResult.UNSURE, - newTodo=get_migration_text(annotation, mapping), + reviewResult=EnumReviewResult.NONE, + newTodo=get_migration_text(annotation, mapping, for_todo_annotation=True), ) annotationv2_d = OptionalAnnotation( target="test/test.value.test6.testD", authors=["testauthor", migration_author], reviewers=[], - comment="", - reviewResult=EnumReviewResult.NONE, + comment=get_migration_text(annotation, mapping), + reviewResult=EnumReviewResult.UNSURE, defaultValueType=ValueAnnotation.DefaultValueType.NUMBER, defaultValue="2", ) @@ -459,10 +460,10 @@ def migrate_required_annotation_data_one_to_many_mapping() -> Tuple[ id_="test/test.value.test7.testD", name="testD", qname="test.value.test7.testD", - default_value="None", + default_value=None, assigned_by=ParameterAssignment.POSITION_OR_NAME, is_public=True, - documentation=ParameterDocumentation("", "None", ""), + documentation=ParameterDocumentation("", "", ""), ) parameterv2_e = Parameter( id_="test/test.value.test7.testE", @@ -508,8 +509,8 @@ def migrate_required_annotation_data_one_to_many_mapping() -> Tuple[ authors=["testauthor", "migration"], reviewers=[], comment="", - reviewResult=EnumReviewResult.UNSURE, - newTodo=get_migration_text(annotation, mapping), + reviewResult=EnumReviewResult.NONE, + newTodo=get_migration_text(annotation, mapping, for_todo_annotation=True), ) annotationv2_b = RequiredAnnotation( target="test/test.value.test7.testB", @@ -523,8 +524,8 @@ def migrate_required_annotation_data_one_to_many_mapping() -> Tuple[ authors=["testauthor", "migration"], reviewers=[], comment="", - reviewResult=EnumReviewResult.UNSURE, - newTodo=get_migration_text(annotation, mapping), + reviewResult=EnumReviewResult.NONE, + newTodo=get_migration_text(annotation, mapping, for_todo_annotation=True), ) annotationv2_d = TodoAnnotation( @@ -532,16 +533,16 @@ def migrate_required_annotation_data_one_to_many_mapping() -> Tuple[ authors=["testauthor", "migration"], reviewers=[], comment="", - reviewResult=EnumReviewResult.UNSURE, - newTodo=get_migration_text(annotation, mapping), + reviewResult=EnumReviewResult.NONE, + newTodo=get_migration_text(annotation, mapping, for_todo_annotation=True), ) annotationv2_e = TodoAnnotation( target="test/test.value.test7.testE", authors=["testauthor", "migration"], reviewers=[], comment="", - reviewResult=EnumReviewResult.UNSURE, - newTodo=get_migration_text(annotation, mapping), + reviewResult=EnumReviewResult.NONE, + newTodo=get_migration_text(annotation, mapping, for_todo_annotation=True), ) annotationv2_f = RequiredAnnotation( @@ -566,6 +567,7 @@ def migrate_required_annotation_data_one_to_many_mapping() -> Tuple[ ) +# pylint: disable=duplicate-code def migrate_omitted_annotation_data_one_to_many_mapping() -> Tuple[ Mapping, AbstractAnnotation, @@ -652,16 +654,16 @@ def migrate_omitted_annotation_data_one_to_many_mapping() -> Tuple[ authors=["testauthor", "migration"], reviewers=[], comment="", - reviewResult=EnumReviewResult.UNSURE, - newTodo=get_migration_text(annotation, mapping), + reviewResult=EnumReviewResult.NONE, + newTodo=get_migration_text(annotation, mapping, for_todo_annotation=True), ) annotationv2_c = TodoAnnotation( target="test/test.value.test8.testC", authors=["testauthor", "migration"], reviewers=[], comment="", - reviewResult=EnumReviewResult.UNSURE, - newTodo=get_migration_text(annotation, mapping), + reviewResult=EnumReviewResult.NONE, + newTodo=get_migration_text(annotation, mapping, for_todo_annotation=True), ) annotationv2_d = TodoAnnotation( @@ -669,16 +671,16 @@ def migrate_omitted_annotation_data_one_to_many_mapping() -> Tuple[ authors=["testauthor", "migration"], reviewers=[], comment="", - reviewResult=EnumReviewResult.UNSURE, - newTodo=get_migration_text(annotation, mapping), + reviewResult=EnumReviewResult.NONE, + newTodo=get_migration_text(annotation, mapping, for_todo_annotation=True), ) annotationv2_e = TodoAnnotation( target="test/test.value.test8.testE", authors=["testauthor", "migration"], reviewers=[], comment="", - reviewResult=EnumReviewResult.UNSURE, - newTodo=get_migration_text(annotation, mapping), + reviewResult=EnumReviewResult.NONE, + newTodo=get_migration_text(annotation, mapping, for_todo_annotation=True), ) return ( @@ -692,3 +694,252 @@ def migrate_omitted_annotation_data_one_to_many_mapping() -> Tuple[ annotationv2_e, ], ) + + +def migrate_constant_annotation_data_duplicated() -> Tuple[ + Mapping, + list[AbstractAnnotation], + list[AbstractAnnotation], +]: + parameterv1 = Parameter( + id_="test/test.value.duplicate.testA", + name="testA", + qname="test.value.duplicate.testA", + default_value="'this is a string'", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("str", "this is a string", ""), + ) + parameterv1_2 = Parameter( + id_="test/test.value.duplicate.testA_2", + name="testA_2", + qname="test.value.duplicate.testA_2", + default_value="'this is a string'", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("str", "this is a string", ""), + ) + parameterv2 = Parameter( + id_="test/test.value.duplicate.testB", + name="testB", + qname="test.value.duplicate.testB", + default_value="'test string'", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("str", "'test string'", ""), + ) + mapping = ManyToOneMapping(1.0, [parameterv1, parameterv1_2], parameterv2) + annotation = ConstantAnnotation( + target="test/test.value.duplicate.testA", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + defaultValueType=ValueAnnotation.DefaultValueType.STRING, + defaultValue="This is a string", + ) + annotation_2 = ConstantAnnotation( + target="test/test.value.duplicate.testA_2", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + defaultValueType=ValueAnnotation.DefaultValueType.STRING, + defaultValue="This is a string", + ) + annotationv2 = ConstantAnnotation( + target="test/test.value.duplicate.testB", + authors=["testauthor", migration_author], + reviewers=[], + comment=get_migration_text(annotation, mapping), + reviewResult=EnumReviewResult.UNSURE, + defaultValueType=ValueAnnotation.DefaultValueType.STRING, + defaultValue="This is a string", + ) + return mapping, [annotation, annotation_2], [annotationv2] + + +def migrate_omitted_annotation_data_duplicated() -> Tuple[ + Mapping, + list[AbstractAnnotation], + list[AbstractAnnotation], +]: + parameterv1 = Parameter( + id_="test/test.value.duplicate2.testA", + name="testA", + qname="test.value.duplicate2.testA", + default_value="True", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("bool", "True", ""), + ) + parameterv1_2 = Parameter( + id_="test/test.value.duplicate2.testA_2", + name="testA_2", + qname="test.value.duplicate2.testA_2", + default_value="True", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("bool", "True", ""), + ) + parameterv2 = Parameter( + id_="test/test.value.duplicate2.testB", + name="testB", + qname="test.value.duplicate2.testB", + default_value="True", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("bool", "True", ""), + ) + annotation = OmittedAnnotation( + target="test/test.value.duplicate2.testA", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + ) + annotation_2 = OmittedAnnotation( + target="test/test.value.duplicate2.testA_2", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + ) + annotationv2 = OmittedAnnotation( + target="test/test.value.duplicate2.testB", + authors=["testauthor", migration_author], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + ) + return ( + ManyToOneMapping(1.0, [parameterv1, parameterv1_2], parameterv2), + [annotation, annotation_2], + [annotationv2], + ) + + +def migrate_optional_annotation_data_duplicated() -> Tuple[ + Mapping, + list[AbstractAnnotation], + list[AbstractAnnotation], +]: + parameterv1 = Parameter( + id_="test/test.value.duplicate3.testA", + name="testA", + qname="test.value.duplicate3.testA", + default_value="True", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("bool", "True", ""), + ) + parameterv1_2 = Parameter( + id_="test/test.value.duplicate3.testA_2", + name="testA_2", + qname="test.value.duplicate3.testA_2", + default_value="True", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("bool", "True", ""), + ) + parameterv2 = Parameter( + id_="test/test.value.duplicate3.testB", + name="testB", + qname="test.value.duplicate3.testB", + default_value="False", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("bool", "False", ""), + ) + annotation = OptionalAnnotation( + target="test/test.value.duplicate3.testA", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + defaultValueType=ValueAnnotation.DefaultValueType.BOOLEAN, + defaultValue="True", + ) + annotation_2 = OptionalAnnotation( + target="test/test.value.duplicate3.testA_2", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + defaultValueType=ValueAnnotation.DefaultValueType.BOOLEAN, + defaultValue="True", + ) + annotationv2 = OptionalAnnotation( + target="test/test.value.duplicate3.testB", + authors=["testauthor", migration_author], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + defaultValueType=ValueAnnotation.DefaultValueType.BOOLEAN, + defaultValue="True", + ) + return ( + ManyToOneMapping(1.0, [parameterv1, parameterv1_2], parameterv2), + [annotation, annotation_2], + [annotationv2], + ) + + +def migrate_required_annotation_data_duplicated() -> Tuple[ + Mapping, + list[AbstractAnnotation], + list[AbstractAnnotation], +]: + parameterv1 = Parameter( + id_="test/test.value.duplicate4.testA", + name="testA", + qname="test.value.duplicate4.testA", + default_value="'test'", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("str", "'test'", ""), + ) + parameterv1_2 = Parameter( + id_="test/test.value.duplicate4.testA_2", + name="testA_2", + qname="test.value.duplicate4.testA_2", + default_value="'test'", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("str", "'test'", ""), + ) + parameterv2 = Parameter( + id_="test/test.value.duplicate4.testB", + name="testB", + qname="test.value.duplicate4.testB", + default_value="'test_string'", + assigned_by=ParameterAssignment.POSITION_OR_NAME, + is_public=True, + documentation=ParameterDocumentation("str", "'test_string'", ""), + ) + annotation = RequiredAnnotation( + target="test/test.value.duplicate4.testA", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + ) + annotation_2 = RequiredAnnotation( + target="test/test.value.duplicate4.testA_2", + authors=["testauthor"], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + ) + annotationv2 = RequiredAnnotation( + target="test/test.value.duplicate4.testB", + authors=["testauthor", migration_author], + reviewers=[], + comment="", + reviewResult=EnumReviewResult.NONE, + ) + return ( + ManyToOneMapping(1.0, [parameterv1, parameterv1_2], parameterv2), + [annotation, annotation_2], + [annotationv2], + ) diff --git a/package-parser/tests/processing/migration/test_migration.py b/package-parser/tests/processing/migration/test_migration.py index c50362dc4..e85e58176 100644 --- a/package-parser/tests/processing/migration/test_migration.py +++ b/package-parser/tests/processing/migration/test_migration.py @@ -1,16 +1,22 @@ +import json +import os + from package_parser.processing.annotations.model import ( AbstractAnnotation, AnnotationStore, ) -from package_parser.processing.migration import migrate_annotations +from package_parser.processing.api.model import API +from package_parser.processing.migration import APIMapping, Migration, SimpleDiffer from package_parser.processing.migration.model import Mapping from tests.processing.migration.annotations.test_boundary_migration import ( + migrate_boundary_annotation_data_duplicated, migrate_boundary_annotation_data_one_to_many_mapping, migrate_boundary_annotation_data_one_to_one_mapping, migrate_boundary_annotation_data_one_to_one_mapping_float_to_int, migrate_boundary_annotation_data_one_to_one_mapping_int_to_float, ) from tests.processing.migration.annotations.test_called_after_migration import ( + migrate_called_after_annotation_data_duplicated, migrate_called_after_annotation_data_one_to_many_mapping, migrate_called_after_annotation_data_one_to_many_mapping__two_classes, migrate_called_after_annotation_data_one_to_one_mapping, @@ -18,11 +24,13 @@ migrate_called_after_annotation_data_one_to_one_mapping__no_mapping_found, ) from tests.processing.migration.annotations.test_description_migration import ( + migrate_description_annotation_data_duplicated, migrate_description_annotation_data_one_to_many_mapping__class, migrate_description_annotation_data_one_to_one_mapping__function, migrate_description_annotation_data_one_to_one_mapping__parameter, ) from tests.processing.migration.annotations.test_enum_migration import ( + migrate_enum_annotation_data_duplicated, migrate_enum_annotation_data_one_to_many_mapping, migrate_enum_annotation_data_one_to_many_mapping__only_one_relevant_mapping, migrate_enum_annotation_data_one_to_one_mapping, @@ -31,8 +39,10 @@ migrate_expert_annotation_data__class, migrate_expert_annotation_data__function, migrate_expert_annotation_data__parameter, + migrate_expert_annotation_data_duplicated, ) from tests.processing.migration.annotations.test_group_annotation import ( + migrate_group_annotation_data_duplicated, migrate_group_annotation_data_one_to_many_mapping, migrate_group_annotation_data_one_to_one_mapping, migrate_group_annotation_data_one_to_one_mapping__one_mapping_for_parameters, @@ -41,28 +51,35 @@ migrate_move_annotation_data_one_to_many_mapping, migrate_move_annotation_data_one_to_one_mapping__class, migrate_move_annotation_data_one_to_one_mapping__global_function, + migrate_move_annotation_data_one_to_one_mapping_duplicated, ) from tests.processing.migration.annotations.test_remove_migration import ( + migrate_remove_annotation_data_duplicated, migrate_remove_annotation_data_one_to_many_mapping, migrate_remove_annotation_data_one_to_one_mapping, ) from tests.processing.migration.annotations.test_rename_migration import ( + migrate_rename_annotation_data_duplicated, migrate_rename_annotation_data_one_to_many_mapping, - migrate_rename_annotation_data_one_to_many_mapping__with_changed_new_name, migrate_rename_annotation_data_one_to_one_mapping, ) from tests.processing.migration.annotations.test_todo_migration import ( + migrate_todo_annotation_data_duplicated, migrate_todo_annotation_data_many_to_many_mapping, migrate_todo_annotation_data_one_to_many_mapping, migrate_todo_annotation_data_one_to_one_mapping, ) from tests.processing.migration.annotations.test_value_migration import ( + migrate_constant_annotation_data_duplicated, migrate_constant_annotation_data_one_to_many_mapping, migrate_constant_annotation_data_one_to_one_mapping, + migrate_omitted_annotation_data_duplicated, migrate_omitted_annotation_data_one_to_many_mapping, migrate_omitted_annotation_data_one_to_one_mapping, + migrate_optional_annotation_data_duplicated, migrate_optional_annotation_data_one_to_many_mapping, migrate_optional_annotation_data_one_to_one_mapping, + migrate_required_annotation_data_duplicated, migrate_required_annotation_data_one_to_many_mapping, migrate_required_annotation_data_one_to_one_mapping, ) @@ -73,43 +90,52 @@ migrate_boundary_annotation_data_one_to_one_mapping_int_to_float(), migrate_boundary_annotation_data_one_to_one_mapping_float_to_int(), migrate_boundary_annotation_data_one_to_many_mapping(), + migrate_boundary_annotation_data_duplicated(), # called after annotation migrate_called_after_annotation_data_one_to_one_mapping(), migrate_called_after_annotation_data_one_to_many_mapping(), migrate_called_after_annotation_data_one_to_one_mapping__no_mapping_found(), migrate_called_after_annotation_data_one_to_one_mapping__before_splits(), migrate_called_after_annotation_data_one_to_many_mapping__two_classes(), + migrate_called_after_annotation_data_duplicated(), # description annotation migrate_description_annotation_data_one_to_one_mapping__function(), migrate_description_annotation_data_one_to_many_mapping__class(), migrate_description_annotation_data_one_to_one_mapping__parameter(), + migrate_description_annotation_data_duplicated(), # enum annotation migrate_enum_annotation_data_one_to_one_mapping(), migrate_enum_annotation_data_one_to_many_mapping(), migrate_enum_annotation_data_one_to_many_mapping__only_one_relevant_mapping(), + migrate_enum_annotation_data_duplicated(), # expert annotation migrate_expert_annotation_data__function(), migrate_expert_annotation_data__class(), migrate_expert_annotation_data__parameter(), + migrate_expert_annotation_data_duplicated(), # group annotation migrate_group_annotation_data_one_to_one_mapping(), migrate_group_annotation_data_one_to_many_mapping(), migrate_group_annotation_data_one_to_one_mapping__one_mapping_for_parameters(), + migrate_group_annotation_data_duplicated(), # move annotation migrate_move_annotation_data_one_to_one_mapping__class(), migrate_move_annotation_data_one_to_one_mapping__global_function(), migrate_move_annotation_data_one_to_many_mapping(), + migrate_move_annotation_data_one_to_one_mapping_duplicated(), # remove annotation migrate_remove_annotation_data_one_to_one_mapping(), migrate_remove_annotation_data_one_to_many_mapping(), + migrate_remove_annotation_data_duplicated(), # rename annotation - migrate_rename_annotation_data_one_to_many_mapping__with_changed_new_name(), migrate_rename_annotation_data_one_to_one_mapping(), migrate_rename_annotation_data_one_to_many_mapping(), + migrate_rename_annotation_data_duplicated(), # to-do annotation migrate_todo_annotation_data_one_to_one_mapping(), migrate_todo_annotation_data_one_to_many_mapping(), migrate_todo_annotation_data_many_to_many_mapping(), + migrate_todo_annotation_data_duplicated(), # value annotation migrate_constant_annotation_data_one_to_one_mapping(), migrate_omitted_annotation_data_one_to_one_mapping(), @@ -119,6 +145,10 @@ migrate_optional_annotation_data_one_to_many_mapping(), migrate_required_annotation_data_one_to_many_mapping(), migrate_omitted_annotation_data_one_to_many_mapping(), + migrate_constant_annotation_data_duplicated(), + migrate_omitted_annotation_data_duplicated(), + migrate_required_annotation_data_duplicated(), + migrate_optional_annotation_data_duplicated(), ] @@ -132,12 +162,85 @@ def test_migrate_all_annotations() -> None: mappings.extend(mapping) else: mappings.append(mapping) - annotation_store.add_annotation(annotationv1) + if isinstance(annotationv1, list): + for annotationv1_ in annotationv1: + annotation_store.add_annotation(annotationv1_) + else: + annotation_store.add_annotation(annotationv1) for expected_annotation in annotationsv2: expected_annotation_store.add_annotation(expected_annotation) - actual_annotations = migrate_annotations(annotation_store, mappings) + migration = Migration(annotation_store, mappings) + migration.migrate_annotations() + + for value in migration.unsure_migrated_annotation_store.to_json().values(): + if isinstance(value, dict): + assert len(value) == 0 + + _assert_annotation_stores_are_equal( + migration.migrated_annotation_store, expected_annotation_store + ) + + +def test_migrate_command_and_both_annotation_stores() -> None: + apiv1_json_path = os.path.join( + os.getcwd(), "tests", "data", "migration", "apiv1_data.json" + ) + apiv2_json_path = os.path.join( + os.getcwd(), "tests", "data", "migration", "apiv2_data.json" + ) + annotationsv1_json_path = os.path.join( + os.getcwd(), "tests", "data", "migration", "annotationv1.json" + ) + annotationsv2_json_path = os.path.join( + os.getcwd(), "tests", "data", "migration", "annotationv2.json" + ) + unsure_annotationsv2_json_path = os.path.join( + os.getcwd(), "tests", "data", "migration", "unsure_annotationv2.json" + ) + with open(apiv1_json_path, "r", encoding="utf-8") as apiv1_file, open( + apiv2_json_path, "r", encoding="utf-8" + ) as apiv2_file, open( + annotationsv1_json_path, "r", encoding="utf-8" + ) as annotationsv1_file, open( + annotationsv2_json_path, "r", encoding="utf-8" + ) as annotationsv2_file, open( + unsure_annotationsv2_json_path, "r", encoding="utf-8" + ) as unsure_annotationsv2_file: + apiv1_json = json.load(apiv1_file) + apiv1 = API.from_json(apiv1_json) + apiv2_json = json.load(apiv2_file) + apiv2 = API.from_json(apiv2_json) + annotationsv1_json = json.load(annotationsv1_file) + annotationsv1 = AnnotationStore.from_json(annotationsv1_json) + expected_annotationsv2_json = json.load(annotationsv2_file) + annotationsv2 = AnnotationStore.from_json(expected_annotationsv2_json) + expected_unsure_annotationsv2_json = json.load(unsure_annotationsv2_file) + unsure_annotationsv2 = AnnotationStore.from_json( + expected_unsure_annotationsv2_json + ) + + differ = SimpleDiffer() + api_mapping = APIMapping( + apiv1, apiv2, differ, threshold_of_similarity_between_mappings=0.3 + ) + mappings = api_mapping.map_api() + migration = Migration( + annotationsv1, mappings, reliable_similarity=0.9, unsure_similarity=0.75 + ) + migration.migrate_annotations() + + _assert_annotation_stores_are_equal( + migration.migrated_annotation_store, annotationsv2 + ) + _assert_annotation_stores_are_equal( + migration.unsure_migrated_annotation_store, unsure_annotationsv2 + ) + +def _assert_annotation_stores_are_equal( + actual_annotations: AnnotationStore, expected_annotation_store: AnnotationStore +) -> None: def get_key(annotation: AbstractAnnotation) -> str: return annotation.target