Source code for jsonschema_diff.core.custom_compare.list

import difflib
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any

from ..abstraction import Statuses
from ..parameter_base import Compare

if TYPE_CHECKING:
    from ..config import Config
    from ..parameter_base import LEGEND_RETURN_TYPE


@dataclass
[docs] class CompareListElement:
[docs] config: "Config"
[docs] value: Any
[docs] status: Statuses
[docs] def render(self, tab_level: int = 0) -> str: return f"{self.status.value} {self.config.TAB * tab_level}{self.value}"
[docs] class CompareList(Compare): def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs)
[docs] self.elements: list[CompareListElement] = []
[docs] self.changed_elements: list[CompareListElement] = []
[docs] def compare(self) -> Statuses: super().compare() if self.status == Statuses.NO_DIFF: return self.status elif self.status in [Statuses.ADDED, Statuses.DELETED]: # add for v in self.value: element = CompareListElement(self.config, v, self.status) self.elements.append(element) self.changed_elements.append(element) elif self.status == Statuses.REPLACED: # replace or no-diff sm = difflib.SequenceMatcher(a=self.old_value, b=self.new_value, autojunk=False) for tag, i1, i2, j1, j2 in sm.get_opcodes(): def add_element( source: list[Any], status: Statuses, from_index: int, to_index: int ) -> None: is_change = status != Statuses.NO_DIFF for v in source[from_index:to_index]: element = CompareListElement(self.config, v, status) self.elements.append(element) if is_change: self.changed_elements.append(element) match tag: case "equal": add_element(self.old_value, Statuses.NO_DIFF, i1, i2) case "delete": add_element(self.old_value, Statuses.DELETED, i1, i2) case "insert": add_element(self.new_value, Statuses.ADDED, j1, j2) case "replace": add_element(self.old_value, Statuses.DELETED, i1, i2) add_element(self.new_value, Statuses.ADDED, j1, j2) case _: raise ValueError(f"Unknown tag: {tag}") if len(self.changed_elements) > 0: self.status = Statuses.MODIFIED else: self.status = Statuses.NO_DIFF else: raise ValueError("Unsupported keys combination") return self.status
[docs] def is_for_rendering(self) -> bool: return super().is_for_rendering() or len(self.changed_elements) > 0
[docs] def render(self, tab_level: int = 0, with_path: bool = True) -> str: to_return = self._render_start_line(tab_level=tab_level, with_path=with_path) for i in self.elements: to_return += f"\n{i.render(tab_level + 1)}" return to_return
@staticmethod
[docs] def legend() -> "LEGEND_RETURN_TYPE": return { "element": "Arrays\nLists", "description": ( "Arrays are always displayed fully, with statuses of all elements " "separately (left to them).\nIn example:\n" '["Masha", "Misha", "Vasya"] replace to ["Masha", "Olya", "Misha"]' ), "example": { "old_value": {"some_list": ["Masha", "Misha", "Vasya"]}, "new_value": {"some_list": ["Masha", "Olya", "Misha"]}, }, }