Skip to content

symfonic.diagnostics.inspection.context

context

Everything the offline inspection is allowed to look at.

symfonic doctor answers questions by constructing an agent and asking the process about itself. That works for the traps it was built for and is the wrong instrument for a migration, for three reasons:

  1. it needs the adopter's dependencies installed and their agent importable -- exactly what is in doubt while they are migrating;
  2. it sees the configuration that survived construction, and the legacy spellings, mirrors, and never-set defaults a migration cares about are collapsed before the audit gets a turn;
  3. it cannot see the project's source at all, so no import question is answerable.

This context is the alternative: raw configuration, a directory of text, and a mapping of environment variables. Nothing here is executed, imported, or connected to.

InspectionContext dataclass

InspectionContext(config: Mapping[str, Any] = dict(), package_version: str | None = None, project_root: Path | None = None, environ: Mapping[str, str] = dict(), available_roots: frozenset[str] | None = None, framework_root: Path | None = None, max_source_files: int = 5000)

The inputs one offline inspection may read.

Every field is injectable so a caller -- a test, a CI job, a reviewer inspecting somebody else's checkout -- gets the same answer the adopter gets, without depending on the inspecting process's own environment.

for_target classmethod

for_target(*, project_root: Path | None = None, config: Mapping[str, Any] | None = None, environ: Mapping[str, str] | None = None, package_version: str | None = None) -> InspectionContext

Build a context describing the inspected deployment.

environ defaults to empty rather than to os.environ. The replaced from_process did the latter, which quietly turned every platform-security answer into a statement about the machine running the inspection -- and the caller has no way to notice, because a laptop with no production marker set produces exactly the silence a clean production deployment would.

Source code in src/symfonic/diagnostics/inspection/context.py
@classmethod
def for_target(
    cls,
    *,
    project_root: Path | None = None,
    config: Mapping[str, Any] | None = None,
    environ: Mapping[str, str] | None = None,
    package_version: str | None = None,
) -> InspectionContext:
    """Build a context describing the *inspected* deployment.

    ``environ`` defaults to empty rather than to ``os.environ``. The
    replaced ``from_process`` did the latter, which quietly turned every
    platform-security answer into a statement about the machine running the
    inspection -- and the caller has no way to notice, because a laptop with
    no production marker set produces exactly the silence a clean production
    deployment would.
    """
    return cls(
        config=dict(config or {}),
        project_root=project_root,
        environ=dict(environ or {}),
        package_version=package_version,
    )

resolved_framework_root

resolved_framework_root() -> Path

The directory containing the symfonic package.

src/ in a checkout, site-packages/ in an installed environment; only the symfonic subtree under it is ever scanned.

Source code in src/symfonic/diagnostics/inspection/context.py
def resolved_framework_root(self) -> Path:
    """The directory *containing* the ``symfonic`` package.

    ``src/`` in a checkout, ``site-packages/`` in an installed environment;
    only the ``symfonic`` subtree under it is ever scanned.
    """
    if self.framework_root is not None:
        return self.framework_root
    import symfonic

    return Path(symfonic.__file__).resolve().parent.parent

resolved_roots

resolved_roots() -> frozenset[str]

Installed optional import roots, probed once if not supplied.

Source code in src/symfonic/diagnostics/inspection/context.py
def resolved_roots(self) -> frozenset[str]:
    """Installed optional import roots, probed once if not supplied."""
    if self.available_roots is not None:
        return self.available_roots
    return detect_available_roots()

sources

sources() -> tuple[tuple[str, str], ...]

Every readable *.py under the project root, as (path, text).

Paths are project-relative POSIX strings because they end up in a report somebody reads; absolute paths from the inspecting machine would be noise in a document that outlives the run.

Source code in src/symfonic/diagnostics/inspection/context.py
def sources(self) -> tuple[tuple[str, str], ...]:
    """Every readable ``*.py`` under the project root, as (path, text).

    Paths are project-relative POSIX strings because they end up in a
    report somebody reads; absolute paths from the inspecting machine
    would be noise in a document that outlives the run.
    """
    root = self.project_root
    if root is None:
        return ()
    collected: list[tuple[str, str]] = []
    for path in self._candidate_paths():
        try:
            text = path.read_text(encoding="utf-8")
        except (OSError, UnicodeDecodeError):
            continue
        collected.append((path.relative_to(root).as_posix(), text))
        if len(collected) >= self.max_source_files:
            break
    return tuple(collected)