Skip to content

visualdynamics.report.features

features

Report features: optional additions to a report, each turned on or off.

A report carries the keys of the features enabled on it (Report.features). With none enabled, the report renders exactly as it would without this module: the same page, byte for byte. Each feature changes only what its description says, and only when it is enabled, so a lab turns on what its reports need and nothing else moves.

The registry (FEATURES) lists every feature, including those not yet built, so the selection tool shows what is planned. A feature that is not built is marked unavailable and cannot be enabled. A feature can need another feature (requires names its key), or a capability the package does not have yet (CAPABILITIES); while a requirement is not met, the feature cannot be enabled and the tool says what is missing.

The three presets (PRESETS) are starting points: the default (no features, today's report), a test report, and a flash report. A preset may name features that are not available yet; applying it enables the ones that are and says which it skipped and why.

configure is the one place a report's features change, and refuses an unknown key, an unavailable feature or an unmet requirement before changing anything. Project.report_features and the report editor's Report Features panel both call it.

Classes:

Name Description
ReportFeature

One optional addition to a report.

Capability

Something a feature needs that the package itself must provide.

Preset

A named set of features to start from.

FeatureSelection

What configure left enabled, and what it has to say.

Functions:

Name Description
unmet

What stands between feature key and being enabled, in words.

reason

unmet as one line for the selection tool: '' when the feature

enabled

The features that act on report when it is rendered.

defaults

The features a new report starts with: those whose default is

configure

Change which features are enabled on report.

preset_of

The preset whose available features are exactly keys, or None

Classes

ReportFeature dataclass

ReportFeature(key: str, title: str, group: str, description: str, requires: tuple[str, ...] = (), default: bool = False, available: bool = True)

One optional addition to a report.

key is what a report stores and a script names. title and description are what the selection tool shows: the title on the check box, the description beneath it and in its tooltip. group is one of GROUPS. requires names what must be in place first: the key of another feature, which must be enabled, or the key of a capability in CAPABILITIES, which the package must have. default is whether a new report starts with the feature on; every feature is off by default. available is False for a feature that is registered so the tool can show it but is not built yet.

Capability dataclass

Capability(key: str, label: str, probe: Callable[[], bool])

Something a feature needs that the package itself must provide.

label names it in a reason ("needs the multi-point composite"). probe answers whether this installation has it.

Preset dataclass

Preset(key: str, title: str, description: str, features: tuple[str, ...])

A named set of features to start from.

FeatureSelection dataclass

FeatureSelection(enabled: tuple[str, ...], notes: tuple[str, ...] = ())

What configure left enabled, and what it has to say.

enabled is the report's features afterwards, in registry order. notes says what was skipped or turned off along the way: a preset's unavailable features, or a feature turned off because a feature it needs was.

Functions:

unmet

unmet(key: str, enabled: Iterable[str] = ()) -> list[str]

What stands between feature key and being enabled, in words.

Empty when nothing does. An unbuilt feature says 'not yet available' first and then what it will wait on; a built one names each feature that must be enabled (from enabled) and each capability this installation does not have. Raises ValueError for a key that is not a registered feature.

Source code in src/visualdynamics/report/features.py
def unmet(key: str, enabled: Iterable[str] = ()) -> list[str]:
    """What stands between feature `key` and being enabled, in words.

    Empty when nothing does. An unbuilt feature says 'not yet
    available' first and then what it will wait on; a built one names
    each feature that must be enabled (from `enabled`) and each
    capability this installation does not have. Raises `ValueError` for
    a key that is not a registered feature.
    """
    feature = _feature(key)
    on = set(enabled)
    missing = []
    for need in feature.requires:
        if need in FEATURES:
            if need not in on:
                missing.append(f'needs {FEATURES[need].title}')
        elif need in CAPABILITIES:
            if not CAPABILITIES[need].probe():
                missing.append(f'needs {CAPABILITIES[need].label}')
        else:
            missing.append(f'needs {need!r}, which is not known')
    if not feature.available:
        waits = [reason.removeprefix('needs ') for reason in missing]
        return [NOT_YET + (f'; waits on {", ".join(waits)}' if waits
                           else '')]
    return missing

reason

reason(key: str, enabled: Iterable[str] = ()) -> str

unmet as one line for the selection tool: '' when the feature can be enabled.

Source code in src/visualdynamics/report/features.py
def reason(key: str, enabled: Iterable[str] = ()) -> str:
    """`unmet` as one line for the selection tool: '' when the feature
    can be enabled."""
    return '; '.join(unmet(key, enabled))

enabled

enabled(report: Any) -> tuple[str, ...]

The features that act on report when it is rendered.

Read from report.features, kept to the registered, available features whose requirements are met, in registry order. A key the registry does not know (written by a later version, say) is kept on the report but does nothing here.

Source code in src/visualdynamics/report/features.py
def enabled(report: Any) -> tuple[str, ...]:
    """The features that act on `report` when it is rendered.

    Read from `report.features`, kept to the registered, available
    features whose requirements are met, in registry order. A key the
    registry does not know (written by a later version, say) is kept on
    the report but does nothing here.
    """
    stored = set(getattr(report, 'features', ()) or ())
    out: list[str] = []
    for key, feature in FEATURES.items():
        if key in stored and feature.available and not unmet(key, out):
            out.append(key)
    return tuple(out)

defaults

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

The features a new report starts with: those whose default is True. None are, so a new report starts as Visual Dynamics writes it.

Source code in src/visualdynamics/report/features.py
def defaults() -> tuple[str, ...]:
    """The features a new report starts with: those whose `default` is
    True. None are, so a new report starts as Visual Dynamics writes it."""
    return tuple(key for key, feature in FEATURES.items()
                 if feature.default and feature.available)

configure

configure(report: Any, *, enable: str | Iterable[str] = (), disable: str | Iterable[str] = (), preset: str | None = None) -> FeatureSelection

Change which features are enabled on report.

With preset, the report's features are replaced by the preset's available ones first, and every feature the preset names but cannot have is skipped with a note. Then disable turns features off, and a feature that needs one turned off goes with it, with a note. Then enable turns features on; a feature needing another may be enabled in the same call as the one it needs.

Every request is checked before anything changes, so a refused call leaves the report as it was. Refused: an unknown feature or preset, a feature that is not yet available, a feature whose requirement is not met, and a key both enabled and disabled. Keys the registry does not know that are already on the report (from a later version) are kept, except when a preset replaces the set.

Returns:

Type Description
FeatureSelection

The features enabled afterwards and the notes.

Raises:

Type Description
ValueError

For a refusal; the message names the feature and the reason.

TypeError

When a key is not a string.

Source code in src/visualdynamics/report/features.py
def configure(report: Any, *, enable: str | Iterable[str] = (),
              disable: str | Iterable[str] = (),
              preset: str | None = None) -> FeatureSelection:
    """Change which features are enabled on `report`.

    With `preset`, the report's features are replaced by the preset's
    available ones first, and every feature the preset names but cannot
    have is skipped with a note. Then `disable` turns features off, and
    a feature that needs one turned off goes with it, with a note. Then
    `enable` turns features on; a feature needing another may be
    enabled in the same call as the one it needs.

    Every request is checked before anything changes, so a refused call
    leaves the report as it was. Refused: an unknown feature or preset,
    a feature that is not yet available, a feature whose requirement is
    not met, and a key both enabled and disabled. Keys the registry
    does not know that are already on the report (from a later version)
    are kept, except when a preset replaces the set.

    Returns
    -------
    FeatureSelection
        The features enabled afterwards and the notes.

    Raises
    ------
    ValueError
        For a refusal; the message names the feature and the reason.
    TypeError
        When a key is not a string.
    """
    turn_on = _keys(enable, 'enable')
    turn_off = _keys(disable, 'disable')
    both = sorted(set(turn_on) & set(turn_off))
    if both:
        raise ValueError(f'{", ".join(map(repr, both))} cannot be enabled '
                         'and disabled in one call')
    notes: list[str] = []
    stored = list(getattr(report, 'features', ()) or ())
    if preset is not None:
        chosen = PRESETS.get(preset)
        if chosen is None:
            raise ValueError(f'unknown preset {preset!r}; the presets '
                             f'are: {", ".join(PRESETS)}')
        current: list[str] = []
        for key in chosen.features:
            if key not in FEATURES:
                notes.append(f'{chosen.title}: {key!r} skipped, not a '
                             'known feature')
                continue
            missing = unmet(key, current)
            if missing:
                notes.append(f'{chosen.title}: {FEATURES[key].title} '
                             f'skipped, {"; ".join(missing)}')
                continue
            current.append(key)
        unknown: list[str] = []
    else:
        current = [key for key in stored if key in FEATURES]
        unknown = [key for key in stored if key not in FEATURES]
    # off, and whatever stood on what went off
    off = set(turn_off)
    changed = True
    while changed:
        changed = False
        for key in current:
            if key in off:
                continue
            gone = [need for need in FEATURES[key].requires if need in off]
            if gone:
                off.add(key)
                changed = True
                # only features are ever turned off, so what went is
                # a feature
                notes.append(f'{FEATURES[key].title} turned off with '
                             f'{FEATURES[gone[0]].title}, which it needs')
    current = [key for key in current if key not in off]
    # on, in the registry's order, so a feature asked for with the one
    # it needs finds that one already on
    for key in _ordered(turn_on):
        if key in current:
            continue
        missing = unmet(key, current)
        if missing:
            raise ValueError(f'{FEATURES[key].title} ({key!r}) cannot be '
                             f'enabled: {"; ".join(missing)}')
        current.append(key)
    result = _ordered(current)
    report.features = result + unknown
    return FeatureSelection(tuple(result), tuple(notes))

preset_of

preset_of(keys: Sequence[str]) -> str | None

The preset whose available features are exactly keys, or None when the set matches none of them (a custom choice).

Source code in src/visualdynamics/report/features.py
def preset_of(keys: Sequence[str]) -> str | None:
    """The preset whose available features are exactly `keys`, or None
    when the set matches none of them (a custom choice)."""
    wanted = set(_ordered(keys))
    for preset in PRESETS.values():
        if wanted == set(configure(_Scratch(), preset=preset.key).enabled):
            return preset.key
    return None