Source code for jsonschema_diff.pypi_interface

"""
Thin wrapper that exposes a simpler, Pandas-free API for PyPI users.

It delegates heavy lifting to :class:`jsonschema_diff.core.Property` and
applies optional ANSI-color highlighting.
"""

from json import loads
from typing import Optional

from rich.table import Table
from rich.text import Text

from jsonschema_diff.color import HighlighterPipeline
from jsonschema_diff.core import Compare, Config, Property
from jsonschema_diff.table_render import LegendRenderer, make_standard_renderer


[docs] class JsonSchemaDiff: """ Facade around the low-level diff engine. Call sequence ------------- 1. :meth:`compare` or :meth:`compare_from_files` 2. :meth:`render` → string (kept in *last_render_output*) 3. :meth:`legend` → legend table (uses *last_compare_list*) """ def __init__( self, config: Config, colorize_pipeline: HighlighterPipeline, legend_ignore: list[type[Compare]] | None = None, ):
[docs] self.config: Config = config
[docs] self.colorize_pipeline: HighlighterPipeline = colorize_pipeline
[docs] self.table_maker: LegendRenderer = make_standard_renderer( example_processor=self._example_processor, table_width=90 )
[docs] self.legend_ignore: list[type[Compare]] = legend_ignore or []
[docs] self.last_render_output: str = ""
[docs] self.last_compare_list: list[type[Compare]] = []
# ------------------------------------------------------------------ # # Static helpers # ------------------------------------------------------------------ # @staticmethod def _schema_resolver(schema: str | dict) -> dict: if isinstance(schema, dict): return schema else: with open(schema, "r", encoding="utf-8") as fp: return dict(loads(fp.read())) @staticmethod
[docs] def fast_pipeline( config: "Config", old_schema: dict | str, new_schema: dict | str, colorize_pipeline: Optional["HighlighterPipeline"], ) -> tuple[str, list[type[Compare]]]: """ One-shot utility: compare *old_schema* vs *new_schema* and return ``(rendered_text, compare_list)``. Accepted formats: dict or path to JSON file. """ prop = Property( config=config, name=None, schema_path=[], json_path=[], old_schema=JsonSchemaDiff._schema_resolver(old_schema), new_schema=JsonSchemaDiff._schema_resolver(new_schema), ) prop.compare() output_text, compare_list = prop.render() rendered_text = "\n\n".join(output_text) if colorize_pipeline is not None: rendered_text = colorize_pipeline.colorize_and_render(rendered_text) return rendered_text, compare_list
# ------------------------------------------------------------------ # # Public API # ------------------------------------------------------------------ #
[docs] def compare(self, old_schema: dict | str, new_schema: dict | str) -> "JsonSchemaDiff": """Populate internal :class:`Property` tree and perform comparison. Accepted formats: dict or path to JSON file.""" self.property = Property( config=self.config, name=None, schema_path=[], json_path=[], old_schema=JsonSchemaDiff._schema_resolver(old_schema), new_schema=JsonSchemaDiff._schema_resolver(new_schema), ) self.property.compare() return self
[docs] def rich_render(self) -> Text: """ Return the diff body ANSI-colored. Side effects ------------ * ``self.last_render_output`` – cached rendered text. * ``self.last_compare_list`` – list of Compare subclasses encountered. """ body, compare_list = self.property.render() self.last_render_output = "\n\n".join(body) self.last_compare_list = compare_list return self.colorize_pipeline.colorize(self.last_render_output)
[docs] def render(self) -> str: """ Return the diff body ANSI-colored. Side effects ------------ * ``self.last_render_output`` – cached rendered text. * ``self.last_compare_list`` – list of Compare subclasses encountered. """ body, compare_list = self.property.render() self.last_render_output = "\n\n".join(body) self.last_compare_list = compare_list return self.colorize_pipeline.colorize_and_render(self.last_render_output)
# ------------------------------------------------------------------ # # Internal helpers # ------------------------------------------------------------------ # def _example_processor(self, old_value: dict, new_value: dict) -> Text: """ Callback for :pyfunc:`~jsonschema_diff.table_render.make_standard_renderer` that renders inline examples. """ output, _ = JsonSchemaDiff.fast_pipeline(self.config, old_value, new_value, None) return self.colorize_pipeline.colorize(output) # ------------------------------------------------------------------ # # Legend & printing # ------------------------------------------------------------------ #
[docs] def rich_legend(self, comparators: list[type[Compare]]) -> Table: """Return a legend table filtered by *self.legend_ignore*.""" real = [c for c in comparators if c not in self.legend_ignore] return self.table_maker.rich_render(real)
[docs] def legend(self, comparators: list[type[Compare]]) -> str: """Return a legend table filtered by *self.legend_ignore*.""" real = [c for c in comparators if c not in self.legend_ignore] return self.table_maker.render(real)
[docs] def print( self, *, with_body: bool = True, with_legend: bool = True, ) -> None: """ Pretty-print the diff and/or the legend. Parameters ---------- colorized : bool Apply ANSI colors to both body and legend. with_body, with_legend : bool Toggle respective sections. """ if with_body: print(self.render()) if with_body and with_legend: print() if with_legend: print(self.legend(self.last_compare_list))