orchestrata_ultimate.refactor.codemod.analysis.encode

Reference › orchestrata_ultimate › refactor › codemod › analysis › encode

AnalysisResultEncoder.encode(result) turns any analysis result into a dictionary that json.dumps accepts: {"type": <result class name>, "data": <JSON value>}. A dataclass is encoded field by field, a frozenset as a sorted list and a mapping with string keys; a list result, such as the BreakingChange records, carries the type list. It is what the --json option of scripts.codemod analyze uses, and is re-exported by the analysis facade.

At a glance

Source src/orchestrata_ultimate/refactor/codemod/analysis/encode.py
Lines of code 47
Classes 1
Functions 1
Imports (internal) orchestrata_ultimate.core.base.traces
Imported by orchestrata_ultimate.refactor.codemod.analysis
Tests that import it none

Class diagram

Class diagram orchestrata_ultimate.refactor.codemod.analysis.encode

Dependency diagram

Dependency diagram orchestrata_ultimate.refactor.codemod.analysis.encode

API

Classes

AnalysisResultEncoder

Decorators: traces(spec='110', story='US-110-006', reqs=['REQ-110-032']) · Line 33

Encode any analysis result so json.dumps accepts it.

Method Signature Summary
encode staticmethod encode(result: object) -> dict[str, Any] {"type": &lt;result class&gt;, "data": &lt;JSON value&gt;}.

Functions

Function Signature Summary
_jsonable _jsonable(value: object) -> Any Recursively convert dataclasses, sets and mappings to JSON types.

Source

Permalink: https://gitlab.com/MSamy/orchestrata-ultimate-architecture/-/blob/a4cbc2bdf/src/orchestrata_ultimate/refactor/codemod/analysis/encode.py

"""AnalysisResultEncoder — analysis results as JSON-serialisable dicts."""

from __future__ import annotations

import dataclasses
from collections.abc import Mapping
from typing import Any

from orchestrata_ultimate.core.base.traces import traces

__all__ = ["AnalysisResultEncoder"]


def _jsonable(value: object) -> Any:
    """Recursively convert dataclasses, sets and mappings to JSON types."""
    if dataclasses.is_dataclass(value) and not isinstance(value, type):
        return {
            field.name: _jsonable(getattr(value, field.name))
            for field in dataclasses.fields(value)
        }
    if isinstance(value, Mapping):
        return {str(key): _jsonable(item) for key, item in value.items()}
    if isinstance(value, (set, frozenset)):
        return sorted(_jsonable(item) for item in value)
    if isinstance(value, (list, tuple)):
        return [_jsonable(item) for item in value]
    if value is None or isinstance(value, (str, int, float, bool)):
        return value
    raise TypeError(f"cannot encode {type(value).__name__} as JSON")


@traces(spec="110", story="US-110-006", reqs=["REQ-110-032"])
class AnalysisResultEncoder:
    """Encode any analysis result so ``json.dumps`` accepts it."""

    @traces(spec="110", story="US-110-006", reqs=["REQ-110-032"])
    @staticmethod
    def encode(result: object) -> dict[str, Any]:
        """``{"type": <result class>, "data": <JSON value>}``.

        A dataclass encodes field by field, a frozenset as a sorted list, a
        mapping with string keys; a list result (BreakingChange records)
        carries ``"type": "list"``.
        """
        if isinstance(result, (list, tuple)):
            return {"type": "list", "data": _jsonable(result)}
        return {"type": type(result).__name__, "data": _jsonable(result)}