Skip to content

Catalogue

The catalogue of configurable types (rules, weighting schemes, constraints) and their parameters, which a client uses to build forms.

catalogue

A catalogue of the configurable types a client can offer, built from the classes themselves.

Selection rules, weighting schemes and optimiser constraints each register here with the :func:register decorator where they are defined. A client reads the catalogue to render a proper form for each type (field names, types, defaults, labels, choices) and to validate a submitted RuleSpec before it is built, instead of asking the user to type a class name and a set of key/value pairs.

What is introspected and what is declared

Introspection reads the constructor and gets the facts: parameter names, types, which are required, and their defaults. Those cannot go stale, because they are the signature.

Three things introspection cannot know, so they are declared with :class:Display:

  • a label: min_avg_daily_volume is a field name, "Minimum ADV" is a label
  • an order: a form has a designed reading order; a signature's order is whatever was convenient to write
  • choices: a parameter annotated str may accept exactly three values, and the annotation cannot say so

Anything not declared falls back to a label derived from the parameter name and to signature order.

Display dataclass

Display(
    label: str,
    order: int | None = None,
    choices: tuple[str, ...] | None = None,
    help: str | None = None,
)

What introspection cannot know about one parameter.

Attributes:

Name Type Description
label str

Human-readable field name.

order int | None

Position in the form. Defaults to signature order.

choices tuple[str, ...] | None

The values this parameter accepts, when it is a closed set.

help str | None

One line of guidance shown with the field.

Parameter dataclass

Parameter(
    name: str,
    type: str,
    required: bool,
    default: Any = None,
    label: str = "",
    order: int = 0,
    choices: tuple[str, ...] | None = None,
    help: str | None = None,
)

One parameter of a configurable type, ready to render.

Entry dataclass

Entry(
    name: str,
    kind: str,
    label: str,
    summary: str,
    parameters: tuple[Parameter, ...] = tuple(),
)

One configurable type: what it is called, and what it takes.

display_type

display_type(annotation: Any) -> tuple[str, bool]

Map a constructor annotation to a display type.

Parameters:

Name Type Description Default
annotation Any

The annotation as inspect.signature resolved it.

required

Returns:

Name Type Description
tuple str

The display type, and whether the annotation admits None. The

bool

second is what distinguishes "leave this blank" from "this is required"

tuple[str, bool]

for a parameter that also carries a default.

parameters_of

parameters_of(
    cls: type, displays: dict[str, Display] | None = None
) -> tuple[Parameter, ...]

Read a class's constructor into renderable parameters.

Parameters:

Name Type Description Default
cls type

The class to introspect.

required
displays dict[str, Display] | None

Per-parameter presentation, keyed by parameter name.

None

Returns:

Name Type Description
tuple tuple[Parameter, ...]

Parameters in declared order, then signature order.

register

register(
    kind: str,
    label: str,
    fields: dict[str, Display] | None = None,
) -> Any

Register a class in the catalogue, as a decorator.

Parameters:

Name Type Description Default
kind str

SELECTION, WEIGHTING or CONSTRAINT.

required
label str

Human-readable name for the type itself.

required
fields dict[str, Display] | None

Per-parameter presentation. Anything omitted falls back to the signature: a derived label and signature order.

None

Returns:

Type Description
Any

The class, unchanged. Registration is a side effect, so decorating

Any

never alters behaviour: a class works identically registered or not,

Any

but an unregistered one is never offered to a client.

entries

entries(kind: str) -> list[Entry]

Every registered type of one kind, by name.

entry_for

entry_for(kind: str, name: str) -> Entry | None

One entry, or None if nothing of that name is registered.

classes

classes(kind: str) -> dict[str, type]

Name -> class for one kind.

The class that gets constructed and the entry that describes it come from one registration, so they cannot name different things.

parameter_names

parameter_names(kind: str, name: str) -> set[str]

The parameters one type accepts, for validating a submitted spec.

registered_names

registered_names(kind: str) -> set[str]

Every registered name of one kind.