On this page
Target introspection -- generates raw table data for all release targets showing ecosystem, detection files, capabilities, and tag formats.
#rlsbl.targets.introspect
#rlsbl.targets.introspect
Target introspection -- the axis inventory, the completeness assertions, and the committed support-matrix artifact every docs table is rendered from.
The release-target protocol is the single authority for what each target supports. This module is where that authority is enumerated: one TargetAxis per support axis, each naming how the target answers it, and a generator that asks every registered target every axis and serializes the answers to a committed JSON file.
Two things rest on the enumeration being complete:
- The artifact.
rlsbl/data/support-matrix.jsonis generated from here
and committed. The docs directives read that file instead of importing rlsbl, so rendering the documentation no longer needs the package installed in the docs environment -- an import path that once broke a release when the selfdoc environment lost its rlsbl overlay.
- The completeness assertions, which run at import time. A registered
target that cannot answer an axis is an error, and a support surface added to BaseTarget without a matching axis is an error too. Neither can be discovered later by a reader noticing a blank cell.
#TargetAxis
One support axis, and how a target answers it.
Attributes:
name: the axis identifier, used as the key in the artifact.doc: one line saying what the axis means and how it is answered.answer: called with a target, returns a JSON-serializable answer.attr: theBaseTargetattribute this axis reads, when it is not
spelled the same as the axis. The completeness assertion walks the protocol's attributes and looks each one up here, so an axis whose name differs from its source (build_timeout_default reads BUILD_TIMEOUT_DEFAULT) must say so or its source reads as unclassified.
#source_attr
def source_attr(self) -> strThe protocol attribute this axis answers from.
#_prop
def _prop(name: str)Answer an axis by reading the target property of the same name.
#public_attributes
def public_attributes(cls=BaseTarget) -> frozenset[str]Every public attribute of cls -- the set that must be classified.
#axis_source_attributes
def axis_source_attributes(axes=TARGET_AXES) -> frozenset[str]The protocol attributes the axes read.
#unclassified_attributes
def unclassified_attributes(cls=BaseTarget, axes=TARGET_AXES, excluded=None) -> frozenset[str]Public attributes of cls that are neither an axis source nor excluded.
#assert_axis_inventory_is_complete
def assert_axis_inventory_is_complete(cls=BaseTarget, axes=TARGET_AXES, excluded=None) -> NoneEvery public attribute of the protocol is an axis or an excluded operation.
This is the "a new fact must reach the matrix" direction, and it is stated by exclusion so that no naming convention can hide one: adding anything public to the base class without either giving it an axis or excluding it with a reason is an error at import time, not a column that quietly never appears. An exclusion naming an attribute that no longer exists is an error too -- a stale exclusion is an unpoliced surface waiting to be re-added.
#target_axis_answers
def target_axis_answers(registry=None, axes=TARGET_AXES) -> dictAsk every registered target every axis.
Returns {target_name: {axis_name: answer}}. Raises naming the target and the axis when a target cannot answer one -- a registered target that does not implement the whole protocol is a hard error, never a blank cell.
#assert_every_target_answers_every_axis
def assert_every_target_answers_every_axis(registry=None, axes=TARGET_AXES) -> NoneCompleteness in the target direction: no registered target may be short.
#_format_dev_install
def _format_dev_install(specs) -> strFormat the dev_install_command answer into a compact string.
#generate_target_table_data
def generate_target_table_data(answers=None) -> tuple[list[str], list[list[str]]]Generate raw data for a markdown table of all release targets.
Returns (headers, rows) where each row has one cell per header, sorted alphabetically by target name. Every cell comes from the axis answers, so the table cannot describe a target differently from the matrix.
#_check_scopes
def _check_scopes() -> dictThe check-vs-target scope map, in the artifact's shape.
#build_matrix
def build_matrix() -> dictBuild the whole support matrix as a plain JSON-serializable document.
Three registries feed it: the release targets (every axis above), the check-vs-target scope map, and the publish pipelines. Each also contributes the exact headers/rows its docs table renders, so the directives are pure renderers with no derivation of their own.
#render_matrix
def render_matrix() -> strSerialize the matrix deterministically.
Sorted keys, a fixed indent and a trailing newline: two runs on the same registry produce byte-identical text, which is what makes the freshness check a regenerate-and-compare rather than a structural diff.
#matrix_path
def matrix_path(project_root: str) -> strAbsolute path of the committed artifact inside project_root.
#write_matrix
def write_matrix(project_root: str) -> boolWrite the artifact into project_root. Returns True when it changed.