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

Commit

Permalink
feat: new additional strict differ (#1190)
Browse files Browse the repository at this point in the history
Closes #1230.

### Summary of Changes
- new additional differ that filters mappings of api elements out whose
parents are not mapped together.
- `__repr__` method for api elements with only the id of the element.

### Testing Instructions

run the `migrate` command before and after applying these improvements
  • Loading branch information
Aclrian authored Feb 13, 2023
1 parent 2975efb commit 7bbafd1
Show file tree
Hide file tree
Showing 13 changed files with 1,055 additions and 320 deletions.
12 changes: 10 additions & 2 deletions package-parser/package_parser/cli/_run_migrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
from pathlib import Path

from package_parser.processing.migration import Migration
from package_parser.processing.migration.model import APIMapping, SimpleDiffer
from package_parser.processing.migration.model import (
APIMapping,
SimpleDiffer,
StrictDiffer,
)

from ._read_and_write_file import (
_read_annotations_file,
Expand All @@ -23,8 +27,12 @@ def _run_migrate_command(
differ = SimpleDiffer()
api_mapping = APIMapping(apiv1, apiv2, differ)
mappings = api_mapping.map_api()
migration = Migration(annotationsv1, mappings)
enhanced_api_mapping = APIMapping(apiv1, apiv2, StrictDiffer(mappings, differ))
enhanced_mappings = enhanced_api_mapping.map_api()

migration = Migration(annotationsv1, enhanced_mappings)
migration.migrate_annotations()
migration.print(apiv1, apiv2)
migrated_annotations_file = Path(
os.path.join(out_dir_path, "migrated_annotationsv" + apiv2.version + ".json")
)
Expand Down
37 changes: 33 additions & 4 deletions package-parser/package_parser/processing/api/model/_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ def from_json(json: Any) -> Class:
),
json.get("code", ""),
[
Attribute.from_json(instance_attribute)
Attribute.from_json(instance_attribute, json["id"])
for instance_attribute in json.get("instance_attributes", [])
],
)
Expand Down Expand Up @@ -267,19 +267,38 @@ def to_json(self) -> Any:
],
}

def __repr__(self) -> str:
return "Class(id=" + self.id + ")"


@dataclass
class Attribute:
name: str
types: Optional[AbstractType]
class_id: Optional[str] = None

def to_json(self) -> dict[str, Any]:
types_json = self.types.to_json() if self.types is not None else None
return {"name": self.name, "types": types_json}

@staticmethod
def from_json(json: Any) -> Attribute:
return Attribute(json["name"], AbstractType.from_json(json.get("types", {})))
def from_json(json: Any, class_id: Optional[str] = None) -> Attribute:
return Attribute(
json["name"], AbstractType.from_json(json.get("types", {})), class_id
)

def __repr__(self) -> str:
type_str = (
" , type=" + str(self.types.to_json()) if self.types is not None else "None"
)
return (
"Attribute(class_id="
+ str(self.class_id)
+ "/"
+ self.name
+ type_str
+ ")"
)


@dataclass
Expand Down Expand Up @@ -333,22 +352,32 @@ def to_json(self) -> Any:
"code": self.code,
}

def __repr__(self) -> str:
return "Function(id=" + self.id + ")"


@dataclass
class Result:
name: str
docstring: ResultDocstring
function_id: Optional[str] = None

@staticmethod
def from_json(json: Any) -> Result:
def from_json(json: Any, function_id: Optional[str] = None) -> Result:
return Result(
json["name"],
ResultDocstring.from_json(json.get("docstring", {})),
function_id,
)

def to_json(self) -> Any:
return {"name": self.name, "docstring": self.docstring.to_json()}

def __repr__(self) -> str:
return (
"Result(function_id=" + str(self.function_id) + ", name=" + self.name + ")"
)


@dataclass
class ResultDocstring:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class ClassDocumentation:
def from_dict(d: dict) -> ClassDocumentation:
return ClassDocumentation(**d)

def to_dict(self):
def to_dict(self) -> dict:
return dataclasses.asdict(self)


Expand All @@ -26,7 +26,7 @@ class FunctionDocumentation:
def from_dict(d: dict) -> FunctionDocumentation:
return FunctionDocumentation(**d)

def to_dict(self):
def to_dict(self) -> dict:
return dataclasses.asdict(self)


Expand All @@ -40,5 +40,5 @@ class ParameterDocumentation:
def from_dict(d: dict) -> ParameterDocumentation:
return ParameterDocumentation(**d)

def to_dict(self):
def to_dict(self) -> dict:
return dataclasses.asdict(self)
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

class Parameter:
@staticmethod
def from_json(json: Any):
def from_json(json: Any) -> Parameter:
return Parameter(
json["id"],
json["name"],
Expand Down Expand Up @@ -57,6 +57,9 @@ def to_json(self) -> Any:
"type": self.type.to_json() if self.type is not None else {},
}

def __repr__(self) -> str:
return "Parameter(id=" + self.id + ")"


class ParameterAssignment(Enum):
"""
Expand Down
147 changes: 145 additions & 2 deletions package-parser/package_parser/processing/migration/_migrate.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
from dataclasses import dataclass, field
from typing import Optional, Tuple
from typing import Optional, Tuple, Union

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.api.model import (
API,
Attribute,
Class,
Function,
Parameter,
Result,
)
from package_parser.processing.migration.annotations import (
migrate_boundary_annotation,
migrate_called_after_annotation,
Expand Down Expand Up @@ -158,6 +165,142 @@ def add_annotations_based_on_similarity(
else:
self.unsure_migrated_annotation_store.add_annotation(annotation)

def _get_mappings_for_table(self) -> list[str]:
table_rows: list[str] = []
for mapping in self.mappings:

def print_api_element(
api_element: Union[Attribute, Class, Function, Parameter, Result]
) -> str:
if isinstance(api_element, Result):
return api_element.name
if isinstance(api_element, Attribute):
return str(api_element.class_id) + "/" + api_element.name
return "/".join(api_element.id.split("/")[1:])

apiv1_elements = ", ".join(
[
print_api_element(api_element)
for api_element in mapping.get_apiv1_elements()
]
)
apiv2_elements = ", ".join(
[
print_api_element(api_element)
for api_element in mapping.get_apiv2_elements()
]
)
apiv1_elements = "`" + apiv1_elements + "`"
apiv2_elements = "`" + apiv2_elements + "`"
table_rows.append(
f"{mapping.similarity:.4}|{apiv1_elements}|{apiv2_elements}|"
)
return table_rows

def _get_not_mapped_api_elements_for_table(
self, apiv1: API, apiv2: API
) -> list[str]:
not_mapped_api_elements: list[str] = []
not_mapped_apiv1_elements = self._get_not_mapped_api_elements_as_string(apiv1)
for element_id in not_mapped_apiv1_elements:
not_mapped_api_elements.append(f"\u200B|{element_id}||")
not_mapped_apiv2_elements = self._get_not_mapped_api_elements_as_string(
apiv2, print_for_apiv2=True
)
for element_id in not_mapped_apiv2_elements:
not_mapped_api_elements.append(f"\u200B||`{element_id}`|")
return not_mapped_api_elements

def _get_not_mapped_api_elements_as_string(
self, api: API, print_for_apiv2: bool = False
) -> list[str]:
not_mapped_api_elements: list[str] = []

def is_included(
api_element: Union[Attribute, Class, Function, Parameter, Result]
) -> bool:
if not print_for_apiv2:
for mapping in self.mappings:
for element in mapping.get_apiv1_elements():
if isinstance(api_element, Attribute) and isinstance(
element, Attribute
):
if element.name == api_element.name and isinstance(
element.types, type(api_element.types)
):
return True
if isinstance(api_element, Result) and isinstance(
element, Result
):
if (
element.name == api_element.name
and element.docstring == api_element.docstring
):
return True
if not isinstance(
api_element, (Attribute, Result)
) and not isinstance(element, (Attribute, Result)):
if element.id == api_element.id:
return True
return False
for mapping in self.mappings:
for element in mapping.get_apiv2_elements():
if isinstance(api_element, Attribute) and isinstance(
element, Attribute
):
if element.name == api_element.name and isinstance(
element.types, type(api_element.types)
):
return True
if isinstance(api_element, Result) and isinstance(element, Result):
if (
element.name == api_element.name
and element.docstring == api_element.docstring
):
return True
if not isinstance(
api_element, (Attribute, Result)
) and not isinstance(element, (Attribute, Result)):
if element.id == api_element.id:
return True
return False

for class_ in api.classes.values():
if not is_included(class_):
not_mapped_api_elements.append(class_.id)
for function in api.functions.values():
if not is_included(function):
not_mapped_api_elements.append(function.id)
for parameter in api.parameters().values():
if not is_included(parameter):
not_mapped_api_elements.append(parameter.id)
for attribute, class_ in [
(attribute, class_)
for class_ in api.classes.values()
for attribute in class_.instance_attributes
]:
if not is_included(attribute):
not_mapped_api_elements.append(class_.id + "/" + attribute.name)
for result, function in [
(result, function)
for function in api.functions.values()
for result in function.results
]:
if not is_included(result):
not_mapped_api_elements.append(function.id + "/" + result.name)
return not_mapped_api_elements

def print(self, apiv1: API, apiv2: API) -> None:
print("**Similarity**|**APIV1**|**APIV2**|**comment**")
print(":-----:|:-----:|:-----:|:----:|")
table_body = self._get_mappings_for_table()
table_body.extend(self._get_not_mapped_api_elements_for_table(apiv1, apiv2))
table_body.sort(
key=lambda row: max(len(cell.split("/")) for cell in row.split("|")[:-1])
)
for row in table_body:
print(row)

def _remove_duplicates(self) -> None:
for annotation_type in [
"boundaryAnnotations",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from ._api_mapping import APIMapping
from ._differ import AbstractDiffer, SimpleDiffer
from ._mapping import (
APIMapping,
ManyToManyMapping,
ManyToOneMapping,
Mapping,
OneToManyMapping,
OneToOneMapping,
)
from ._strict_differ import StrictDiffer
Loading

0 comments on commit 7bbafd1

Please sign in to comment.