Source code for longeron.stdlib

"""The vendored SysML v2 standard library (Stage D).

A curated subset of the official
`SysML-v2-Release <https://github.com/Systems-Modeling/SysML-v2-Release>`_
model library ships with this package (see ``longeron/_stdlib/README.md``):
the complete Systems Library, the core Quantities-and-Units files, the
Analysis domain-library files (``AnalysisTooling``, ``TradeStudies``), and
a small shim for the KerML kernel names (``ScalarValues::Real``, ...).
Longeron-authored extension libraries (``LongeronSurfaces``) ship beside
the vendored content in ``_stdlib/extensions/`` -- self-declaring, and
never labeled standard.

Loading the library cold takes minutes with the ANTLR Python runtime, so a
prebuilt serialization ships alongside the sources and the content-addressed
workspace cache backs it up: after the first build, loads take milliseconds.
The prebuilt is plain JSON in the same lossless schema as
:func:`longeron.to_json` -- inspectable text, no pickles.

    model = longeron.loads("...user model...")
    longeron.add_standard_library(model)
    interp = longeron.Interpreter(model)
    interp.resolve("Parts::Part")
    interp.evaluate("ISQ::mass")   # resolvable now
"""

from __future__ import annotations

import hashlib
import json
from pathlib import Path

from . import model as M
from .workspace import load_dir

_STDLIB_DIR = Path(__file__).parent / "_stdlib"
_PREBUILT = _STDLIB_DIR / "prebuilt.json"

_prebuilt_data: dict | None = None
_fingerprint_cache: str | None = None


def _stdlib_fingerprint() -> str:
    """Hash of everything that affects the prebuilt JSON's validity:
    the model/AST class definitions and the vendored library sources.
    (Builder changes require ``make stdlib`` to refresh the prebuilt.)
    """

    global _fingerprint_cache
    if _fingerprint_cache is None:
        from . import ast as ast_module
        from . import model as model_module

        digest = hashlib.sha256()
        for module in (model_module, ast_module):
            module_file = getattr(module, "__file__", None)
            if module_file:
                digest.update(Path(module_file).read_bytes())
        for source in sorted(_STDLIB_DIR.rglob("*.sysml")):
            digest.update(source.read_bytes())
        _fingerprint_cache = digest.hexdigest()[:16]
    return _fingerprint_cache


def standard_library_dir() -> Path:
    return _STDLIB_DIR


[docs] def standard_library_model(*, cache: bool = True) -> M.Model: """A fresh :class:`~longeron.model.Model` of the vendored library. Each call returns independent objects (safe to merge into user models). """ from .importer import from_dict global _prebuilt_data if _prebuilt_data is None: _prebuilt_data = _load_prebuilt_data() if _prebuilt_data: model = from_dict(_prebuilt_data) if isinstance(model, M.Model): return model model = load_dir(_STDLIB_DIR, cache=cache) # Stable symbolic name (house convention for non-disk sources): the # loading machine's absolute _stdlib path must not ship in prebuilt.json. model.source_name = "<stdlib>" _store_prebuilt(model) return model
[docs] def add_standard_library(model: M.Model, *, cache: bool = True) -> M.Model: """Add the standard-library packages to ``model`` (idempotent).""" existing = {m.name for m in model.members if m.name} library = standard_library_model(cache=cache) for package in list(library.members): if package.name not in existing: model.add(package) return model
def _load_prebuilt_data() -> dict: """The prebuilt's model dict, when the bundled JSON is usable.""" try: with _PREBUILT.open(encoding="utf-8") as handle: payload = json.load(handle) if payload.get("fingerprint") == _stdlib_fingerprint(): model_data = payload["model"] if isinstance(model_data, dict): return model_data except Exception: # missing, corrupt, or stale pass return {} def _store_prebuilt(model: M.Model) -> None: """Best-effort refresh of the bundled prebuilt JSON.""" from .export import to_dict global _prebuilt_data data = to_dict(model) _prebuilt_data = data try: with _PREBUILT.open("w", encoding="utf-8") as handle: json.dump({"fingerprint": _stdlib_fingerprint(), "model": data}, handle, indent=1) except OSError: # read-only installation: the workspace cache still helps pass