query

Description

syside.query: read-only queries over a loaded model.

Use this package to ask questions about a model that syside.load_model() has produced: find elements by name or metadata, walk generalization and containment hierarchies, and inspect features, connections, and variation points. No function here modifies the model.

Three kinds of names are exported:

Every function returns a plain, fully materialized value (a list, set, bool, scalar, or None), never a lazy view of the model.

Index

Classes

MetadataIndex

A metadata lookup over a model, built once by build_metadata_index().

MultiplicityBounds

The declared multiplicity bounds of a feature.

NameIndex

A name -> elements lookup over a model, built once by build_name_index().

TypeIndex

A concrete-type -> elements lookup, built once by build_type_index().

Unbounded

The unbounded (*) upper multiplicity bound: a top element ordered above every int.

Attributes

UNBOUNDED

R

The singleton Unbounded upper bound (*).

Functions

actors

Return the actor parameters in effect for element, in parameter order.

declared_actors

Return the actor parameters element declares itself, in declaration order.

containers

Yield the owner chain above element, nearest first.

annotated_elements

Return the elements metadata_usage annotates, linear in the number of annotated elements.

build_metadata_index

Build a MetadataIndex for model in one pass, linear in model size.

build_name_index

Build a NameIndex for model in one pass, linear in model size.

build_type_index

Build a TypeIndex for model in one pass, linear in model size.

contains

Return whether container contains element (at any depth).

contents

Return the direct contents (owned elements) of element, linear in the number of owned elements.

connection_ends

Return the end features of connection (its connector_ends) as a list.

find_bindings_of

Find the binding connectors touching element and the bound counterpart.

cross_subsets

Return the features feature cross-subsets (via cross subsetting), linear in the number of cross-subsetted features.

all_contents

Yield every element below element in ownership, breadth-first.

description

Return the concatenated body text of element’s documentation, or None.

declared_types_of

Return the types feature declares, linear in the number of declared typings.

all_types_of

Return every type reachable from feature’s typings, including what those types inherit, linear in its size.

documentation

Return the documentation elements owned by element, linear in the number of documentation elements.

enumeration_values

Return the enumerated values of enum_def, linear in the number of values.

exposed_elements

Return the elements exposed by view (its exposed_elements), linear in the number of exposed elements.

feature_chain

Return the chaining features of feature (the feature-chain links), linear in the chain length.

featured_in

Return the types that feature is featured by (its featuring contexts).

features_of

Return the visible features of type_, linear in the feature count.

find_by_name

Find an element named name within scope.

find_by_name_and_type

Find the first model element of type element_type named name.

find_by_qualified_name

Return the element whose rendered qualified name is exactly qualified_name.

get_by_path

Resolve path (a list of simple names) starting from root.

find_elements_of_type

Return every element of type element_type that library_elements covers.

find_metadata_usages

Find the metadata usages within scope, optionally filtered by what defines them.

find_relationships

Return every relationship of type relationship_type that library_elements covers.

has_metadata_prefix

Return whether element carries a metadata prefix named prefix_name.

find_connectors_of

Find every connector one of whose ends attaches to element.

find_connectors_to

Find the connectors with element on their target side.

find_connectors_from

Find the connectors with element on their source side.

inherited_features

Return the core Type.inherited_features view, linear in its size.

is_contained_in

Return whether element is nested anywhere inside container.

metadata_prefixes

Return element’s prefix metadata usages (the #Name prefixes), linear in the number of prefixes.

metadata_usages

Return all metadata usages owned by element (prefix and body), linear in the number of metadata usages.

multiplicity_bounds

Return the multiplicity bounds feature itself declares, or None.

generalization_names

Return the rendered qualified names of the direct heritage targets.

redefined_by

Return the features that redefine feature (inverse of redefinition).

redefines

Return the features feature redefines (via redefinition), linear in the number of redefined features.

referenced_feature

Return the feature referenced by expression.

siblings

Return the other direct contents of element’s owner.

specializes

Return whether type_ specializes candidate_supertype, directly or indirectly.

superclassifiers

Return the classifiers type_ directly subclassifies (KerML superclassifiers), linear in their number.

subject_of

Return the subject parameter in effect for element, constant time.

declared_subject_of

Return the subject element declares itself, or None, constant time.

subsets

Return the features feature subsets, linear in the number of subsetted features.

subclassifiers

Return the classifiers that directly subclassify type_ (KerML subclassifiers).

supertypes

Return every type type_ inherits from, directly or indirectly, nearest first.

textual_representation

Return the body of element’s textual representation in language.

variants

Return the variant usages of a variation variation, linear in the number of variants.

specializations

Return the elements declaring a heritage relationship to target.

generalizations

Return the heritage targets declared by element.

walk_containment

Walk the ownership tree from element, yielding the elements reached.

Enumerations

LibraryElements

Which documents, and which library elements within them, the find_* lookups that take it and the index builders cover (the lookups are listed in the syside.query._functions module docstring).


Attributes

UNBOUNDED: Final = 'Unbounded(...)'

The singleton Unbounded upper bound (*).

Functions

actors(element: syside.query._CaseLike) → list[syside.PartUsage]

Return the actor parameters in effect for element, in parameter order.

Cost is O(a), where a is the number of actors returned. Reads the core actor_parameters accessor, present on requirement and case usages/definitions (and their subtypes: use-case, verification-case, analysis-case, …). The result is the effective list, not a union: actors are parameters, and an actor element declares redefines the inherited actor at the same position and replaces it, while inherited actors beyond the declared ones are kept. For use case def Base { subject s; actor driver; actor passenger; } and use case def Sub :> Base { subject :>> s; actor mech; }, actors(Sub) is [Sub::mech, Base::passenger] and driver is not returned. For only the actors element declares itself, use declared_actors().

declared_actors(element: syside.query._CaseLike) → list[syside.PartUsage]

Return the actor parameters element declares itself, in declaration order.

Cost is O(a), where a is the number of actors in effect for element (the inherited ones are visited and dropped).

Only the actor declarations written in element’s own body; inherited actors are left out. For every actor in effect, inherited ones included, use actors().

containers(element: syside.Element) → Iterator[syside.Element]

Yield the owner chain above element, nearest first.

annotated_elements(metadata_usage: syside.MetadataUsage) → list[syside.Element]

Return the elements metadata_usage annotates, linear in the number of annotated elements.

build_metadata_index(model: syside.Model, *, library_elements: syside.query.LibraryElements = LibraryElements.WORKSPACE) → syside.query.MetadataIndex

Build a MetadataIndex for model in one pass, linear in model size.

Scans every syside.MetadataUsage of the documents library_elements selects once, recording it and, when its metadata_definition resolves, indexing it under that definition (by identity) and, when the definition is named, under its simple name.

library_elements is tested against the usage, not against its metadata_definition: a @Tag on an ordinary part is indexed under NONE even when Tag is declared inside a library package. Only usages that themselves lie in a library package are dropped. Build the index with the same value the lookups it backs will use.

build_name_index(model: syside.Model, *, library_elements: syside.query.LibraryElements = LibraryElements.WORKSPACE) → syside.query.NameIndex

Build a NameIndex for model in one pass, linear in model size.

Scans every element of the documents library_elements selects once, recording each non-membership element under its rendered qualified_name (when it has one; the first element to claim a qualified name wins, matching core semantics) and appending every element, memberships included, to the per-simple-name bucket (when it has a name). The buckets keep memberships because their consumers filter by type and so ask for one deliberately; by_qualified_name does not, for the reason given on NameIndex.

Elements library_elements excludes are not indexed at all. Skipping them during the scan (rather than post-filtering) matters: a post-hoc filter could not undo an excluded element having claimed a qualified name ahead of an included namesake. Build the index with the same value the lookups it backs will use.

build_type_index(model: syside.Model, *, library_elements: syside.query.LibraryElements = LibraryElements.WORKSPACE) → syside.query.TypeIndex

Build a TypeIndex for model in one pass, linear in model size.

Scans every element of the documents library_elements selects once and buckets it under its exact runtime class. Build the index with the same value the lookups it backs will use.

contains(container: syside.Element, element: syside.Element) → bool

Return whether container contains element (at any depth).

The inverse reading of is_contained_in(): contains(a, b) == is_contained_in(b, a).

contents(element: syside.Element) → list[syside.Element]

Return the direct contents (owned elements) of element, linear in the number of owned elements.

connection_ends(connection: syside.ConnectionUsage) → list[syside.Feature]

Return the end features of connection (its connector_ends) as a list.

connector_ends is a syside.LazyIterator. .collect() drains it into a Python list in a single O(n) pass over the n ends, which is what you want when you need every end materialized: to index it, take its length, hold it, or walk it more than once. (Most connections have only two ends, so the cost is negligible here; the guidance matters for the larger lazy collections these query helpers also wrap.)

.for_each(callback) on the underlying connection.connector_ends is the alternative, but it is worth it only when the callback can stop early: it allocates no list and short-circuits when the callback returns a stop signal, yet it pays a Python call per element where .collect() builds the list in native code. For a full drain it is therefore the slower of the two, measured at 561 us against 392 us for .collect() at n=4000. Prefer .collect() unless you genuinely short-circuit, and expect the crossover only on large collections.

Do not read the ends back with repeated .at(i) / [i] indexing in a loop. A LazyIterator re-seeks from the head on each index, so an index-per-element loop is O(n²), measured ~67x slower than .collect() at n=4000 (74 ms vs 1.1 ms). A LazyIterator is not a Python iterable, so .collect() or .for_each() is the only correct way to walk all ends.

find_bindings_of(model: syside.Model, element: syside.Feature) → list[tuple[syside.ConnectorAsUsage, syside.Feature]]

Find the binding connectors touching element and the bound counterpart.

Every document of the user model is scanned (the same scope as find_connectors_of()), at a cost linear in the model’s size. Returns one (connector, other) pair per syside.BindingConnectorAsUsage in the model one of whose ends attaches to element, where other is the feature the opposite end references. Ends attach exactly as for find_connectors_of(), so a chained end (bind a.p = b.p) attaches to every link of its chain, and the same connectors are found by find_connectors_of(model, element, kind=syside.BindingConnectorAsUsage). For a chained opposite end other is the chain feature (b.p), whose links are available through feature_chain(). A binding both of whose ends attach to element contributes a pair with other being the second end’s feature (element itself for bind x = x).

cross_subsets(feature: syside.Feature) → list[syside.Feature]

Return the features feature cross-subsets (via cross subsetting), linear in the number of cross-subsetted features.

Cross subsetting is the connection/association-end crosses (=>) relationship; its target is the cross feature, typically a feature chain (the other end followed by that end’s cross feature). An ordinary feature yields [].

all_contents(element: syside.Element) → Iterator[syside.Element]

Yield every element below element in ownership, breadth-first.

Backed by walk_containment(); the walk drains each owned_elements iterator once and de-duplicates by identity.

description(element: syside.Element) → str | None

Return the concatenated body text of element’s documentation, or None.

The bodies of every syside.Documentation owned by element are joined, in owned order, with a blank line between them, matching the way a VS Code hover concatenates multiple doc comments rather than showing only the first. None if element has no documentation.

declared_types_of(feature: syside.Feature) → list[syside.Type]

Return the types feature declares, linear in the number of declared typings.

Only the targets of feature’s own feature typings: what is written after : in the declaration (KerML FeatureTyping). A feature that declares no typing yields [], even when it conforms to types through subsetting or redefinition. For every type the feature conforms to, use all_types_of().

all_types_of(feature: syside.Feature) → list[syside.Type]

Return every type reachable from feature’s typings, including what those types inherit, linear in its size.

Declared typings, plus the types of the features it subsets or redefines, plus everything those types inherit from. A plain part p : Mid therefore yields Mid alongside Parts::Part up to Base::Anything, and a feature with no declared typing still yields the types it inherits. This is broader than KerML’s derived Feature::type (which covers feature typings only, not typing reached through subsetting), yet the subsetted feature itself is not included, even though a KerML feature is a type. For the subsetted/redefined features use subsets() / redefines(), and for every type inherited from through any heritage kind, which does include them, use supertypes(). For only the declared typings, use declared_types_of().

documentation(element: syside.Element) → list[syside.Documentation]

Return the documentation elements owned by element, linear in the number of documentation elements.

enumeration_values(enum_def: syside.EnumerationDefinition) → list[syside.EnumerationUsage]

Return the enumerated values of enum_def, linear in the number of values.

exposed_elements(view: syside.ViewUsage) → list[syside.Element]

Return the elements exposed by view (its exposed_elements), linear in the number of exposed elements.

feature_chain(feature: syside.Feature) → list[syside.Feature]

Return the chaining features of feature (the feature-chain links), linear in the chain length.

A feature that is not a chain yields [].

featured_in(feature: syside.Feature) → list[syside.Type]

Return the types that feature is featured by (its featuring contexts).

Backed by the direct Feature.featuring_types accessor (linear in the number of featuring types). Unlike the other inverse queries this needs no model: type featuring is a forward accessor on the feature, not an inverse model scan.

features_of(type_: syside.Type) → list[syside.Feature]

Return the visible features of type_, linear in the feature count.

The core Type.features view: type_’s own features plus the inherited features that remain visible members. A feature that one of type_’s own features redefines is not included (KerML excludes redefined memberships from a type’s inherited memberships).

find_by_name(scope: syside.Element, name: str, *, recursive: bool = False) → syside.Element | None

Find an element named name within scope.

With recursive=False (the default) only the declared members of scope are searched (the members of scope’s own memberships), via the constant-time Namespace.get_member accessor. An alias declared in scope resolves to its target (which scope does not own, so do not assume result.owner is scope); a member that scope only imports does not resolve, and None for an imported name does not mean the import is broken. A non-syside.Namespace scope has no members and yields None.

With recursive=True the whole subtree below scope is searched and the first element (breadth-first) whose simple name equals name is returned, or None if none matches.

This lookup takes no library_elements: it descends into a library package under scope like any other namespace, and its result may have is_library_element set.

find_by_name_and_type(model: syside.Model, name: str, element_type: type[syside.query.find_by_name_and_type.ElementT], *, library_elements: syside.query.LibraryElements = LibraryElements.WORKSPACE, index: syside.query.NameIndex | None = None) → syside.query.find_by_name_and_type.ElementT | None

Find the first model element of type element_type named name.

Returns the first (scan-order) element whose simple name equals name and which is an instance of element_type (subtypes included), or None. library_elements selects which documents are scanned and which library elements within them are matched (see LibraryElements). With an index this is a single name-bucket read; without one it is a model.nodes scan over element_type, linear in the scanned documents’ size.

find_by_qualified_name(model: syside.Model, qualified_name: str, *, library_elements: syside.query.LibraryElements = LibraryElements.WORKSPACE, index: syside.query.NameIndex | None = None) → syside.Element | None

Return the element whose rendered qualified name is exactly qualified_name.

library_elements selects which documents are searched and which library elements within them can resolve (see LibraryElements); a name in an environment document, such as "Base::Anything", resolves only under ALL, while a library package declared inside a workspace document is found under WORKSPACE and ALL.

The name asked for must be the element’s own: this is the inverse of Element.qualified_name, so it answers only to the name an element renders as. get_member follows an alias to its target, so the walk below can arrive at an element by a name that is not its own, e.g. AP::A reaching P::A through alias AP for P. Such a hit is rejected, so that both paths answer the same question; get_by_path() is the lookup that does follow aliases.

With an index this is a single dict read. Without one the qualified name is walked segment by segment from each selected document’s root, using the constant-time Namespace.get_member accessor (never a per-element path scan), returning the first match in document order or None. Several matches exist only when documents each declare a same-named root package; for the bare name Dup only the first such package is returned (see NameIndex). Their members stay reachable by their own qualified names, since every document root is tried: Dup::BB declared in the second document is found. To see all the same-named packages read build_name_index(model).by_simple_name[name], which lists every element of a simple name.

Named find_, not get_: the un-indexed lookup tries every document, so its cost is linear in the number of documents, and one document per package is common practice (see the module docstring’s naming note). It reads the tree directly, so it always reflects the current model, including elements created, renamed, or removed since the model was built. The unindexed walk never scans elements, so ALL costs nothing extra here: fetching one known stdlib element this way is independent of both the user model’s size and the stdlib’s (~150k elements). Only the scanning lookups (find_elements_of_type(), find_relationships(), an unindexed find_by_name_and_type()) and the index builders pay for the widened scan under ALL.

get_by_path(root: syside.Element, path: list[str]) → syside.Element | None

Resolve path (a list of simple names) starting from root.

Each segment is looked up among the declared members of the current element (the members of its own memberships: aliases resolve, to a target the element does not own; imported members do not) with the constant-time Namespace.get_member accessor. Returns the element reached by the final segment, or None if any segment does not resolve or a non-namespace is reached mid-path. An empty path returns root.

This lookup takes no library_elements: a segment naming a library package resolves like any other namespace, and the result may have is_library_element set.

find_elements_of_type(model: syside.Model, element_type: type[syside.query.find_elements_of_type.ElementT], *, include_subtypes: bool = True, library_elements: syside.query.LibraryElements = LibraryElements.WORKSPACE, index: syside.query.TypeIndex | None = None) → list[syside.query.find_elements_of_type.ElementT]

Return every element of type element_type that library_elements covers.

With include_subtypes=True (the default) instances of subclasses are included. library_elements selects which documents are scanned and which library elements within them are returned (see LibraryElements).

With a TypeIndex this unions the relevant exact-type buckets; without one it is a single model.nodes scan, linear in the scanned documents’ size.

find_metadata_usages(scope: syside.Element, *, name: str | None = None, definition: syside.MetadataDefinition | None = None, specializes: syside.Type | None = None, library_elements: syside.query.LibraryElements = LibraryElements.WORKSPACE, index: syside.query.MetadataIndex | None = None) → list[syside.MetadataUsage]

Find the metadata usages within scope, optionally filtered by what defines them.

At most one of name, definition, specializes may be given; with none, every metadata usage in scope is returned. The three filters ask about the metadata definition a usage is defined by (SysML @Tag or metadata t defined by Tag; core metadata_definition):

  • definition=tag keeps the usages whose metadata_definition is that element (identity, not name). This is direct typing only: a @Sub usage where metadata def Sub :> Tag is defined by Sub, not Tag, exactly as the spec derives a feature’s types with redundant supertypes removed.

  • specializes=tag keeps the usages that directly or indirectly specialize that type, in the spec’s Type::specializes sense (see specializes()): a @Sub usage is kept when asked about Tag.

  • name="Tag" keeps the usages whose metadata_definition has that simple name. It cannot tell two same-named definitions apart and misses specializations; prefer definition or specializes when you hold the element.

For a single usage the same questions are usage.metadata_definition is tag (direct) and specializes() (transitive); this function is the bulk form. declared_types_of() on a metadata usage returns [metadata_definition], the same element; all_types_of() is not the per-usage form of definition: it returns the whole implicit type set (MetadataItem, Metaobject, … down to Base::Anything), a strict superset of {metadata_definition}.

library_elements selects which library usages are returned (see LibraryElements; the document half of its meaning is qualified below). It is tested against each usage, not against its metadata_definition: a @Tag usage on an ordinary part is returned under NONE even when the Tag definition lives in a library package; only usages that themselves lie in a library package are dropped.

Unlike the model-scanning lookups, this one walks containment from scope and has no model to consult, so NONE and WORKSPACE exclude environment usages only in documents of tier DocumentTier.StandardLibrary; usages in a third-party environment document (tier Project) are kept under every value. Since containment never crosses documents, this matters only when scope itself lies in an environment document – and then an index built under NONE or WORKSPACE never held those usages, so pass ALL on both sides.

The search is over the subtree below scope (scope included), in scan order. An index (built with build_metadata_index()) is a model-wide pool of usages; it is restricted to scope before filtering, so the result is identical to the unindexed subtree walk for any scope, not only the model root. It pays off when scope is large or the lookup is repeated.

find_relationships(model: syside.Model, relationship_type: type[syside.Relationship], *, exclude_subtypes_of: type[syside.Relationship] | None = None, library_elements: syside.query.LibraryElements = LibraryElements.WORKSPACE, index: syside.query.TypeIndex | None = None) → list[syside.Relationship]

Return every relationship of type relationship_type that library_elements covers.

Subtypes of relationship_type are included. exclude_subtypes_of, if given, drops any relationship that is also an instance of that type (and its subtypes), e.g. relationship_type=Specialization, exclude_subtypes_of=Subsetting keeps specializations that are not subsettings. Both conditions must hold: a relationship is returned only when it is an instance of relationship_type and not an instance of exclude_subtypes_of (or exclude_subtypes_of is None).

library_elements selects which documents are scanned and which library relationships within them are returned (see LibraryElements); it is tested against the relationship itself, not against its related elements.

With a TypeIndex this unions the matching exact-type buckets; without one it is a single model.nodes scan, linear in the scanned documents’ size.

has_metadata_prefix(element: syside.Namespace, prefix_name: str) → bool

Return whether element carries a metadata prefix named prefix_name.

Matches prefix_name against the simple name of each prefix usage’s resolved metadata_definition (its metaclass).

find_connectors_of(model: syside.Model, element: syside.Feature) → list[syside.ConnectorAsUsage]
find_connectors_of(model: syside.Model, element: syside.Feature, *, kind: type[syside.query._ConnectorT]) → list[syside.query._ConnectorT]

Find every connector one of whose ends attaches to element.

Every document of the user model is scanned, so a connector in another document that attaches to element (package B { connect A::x to A::y; } in a second file) is returned. The cost is linear in the model’s size; the result is in model.nodes order. Standard-library connectors are outside the user model and are never returned. Without kind this covers the whole connector family (connect connections, bind bindings, flow transfers, and successions); pass kind to keep one branch of it, e.g. kind=syside.BindingConnectorAsUsage, and the result is typed accordingly. Subtypes of kind are included, so kind=syside.FlowUsage also matches SuccessionFlowUsage; drop them afterwards with an exact type(c) is test if only the base kind is wanted. An end attaches to the feature it references and, when that feature is a chain, to every link of the chain: connect a.pa to b.pb is returned for the parts a/b and for the ports pa/pb alike. This is broader than KerML’s relatedFeature, which names the end features themselves (here the two chains), not their links. Use this for “everything attached to element”; use find_connectors_from() / find_connectors_to() when the source/target split matters: for a flow or succession that split is the model’s direction, and merging the two sides here loses it.

find_connectors_to(model: syside.Model, element: syside.Feature) → list[syside.ConnectorAsUsage]
find_connectors_to(model: syside.Model, element: syside.Feature, *, kind: type[syside.query._ConnectorT]) → list[syside.query._ConnectorT]

Find the connectors with element on their target side.

Every document of the user model is scanned (the same scope as find_connectors_of()), at a cost linear in the model’s size; the result is in model.nodes order. kind restricts the result to one connector kind, subtypes included, as for find_connectors_of(). What the target side means depends on the connector kind: for a flow it is the destination of the payload and for a succession the successor, which is real model semantics. connect connections and bind bindings are not directed, so there to reflects only the order the ends were declared (connect a to b and connect b to a mean the same connection, and bind x = y asserts a symmetric equality). A chained end attaches to every link of its chain (see find_connectors_of()). For every connector touching element regardless of side, use find_connectors_of().

find_connectors_from(model: syside.Model, element: syside.Feature) → list[syside.ConnectorAsUsage]
find_connectors_from(model: syside.Model, element: syside.Feature, *, kind: type[syside.query._ConnectorT]) → list[syside.query._ConnectorT]

Find the connectors with element on their source side.

Every document of the user model is scanned (the same scope as find_connectors_of()), at a cost linear in the model’s size; the result is in model.nodes order. kind restricts the result to one connector kind, subtypes included, as for find_connectors_of(). What the source side means depends on the connector kind: the origin of a flow’s payload, a succession’s predecessor, or (for connect connections and bind bindings, which are not directed) merely the first-declared end; see find_connectors_to() for the same caveat and find_connectors_of() for the side-independent set. A chained end attaches to every link of its chain (see find_connectors_of()).

inherited_features(type_: syside.Type) → list[syside.Feature]

Return the core Type.inherited_features view, linear in its size.

Not a subset of features_of(): this view lists what the supertypes contribute regardless of visibility in type_, so it still contains every feature that one of type_’s own features redefines – which features_of(), listing visible members, correctly omits. To enumerate the visible non-owned features, subtract type_’s own features from features_of() instead of calling this.

is_contained_in(element: syside.Element, container: syside.Element) → bool

Return whether element is nested anywhere inside container.

True when container is element’s owner, its owner’s owner, and so on, i.e. element appears (at any depth) within container’s braces.

metadata_prefixes(element: syside.Namespace) → list[syside.MetadataUsage]

Return element’s prefix metadata usages (the #Name prefixes), linear in the number of prefixes.

The core prefixes view yields (membership, feature) pairs; only syside.MetadataUsage features are returned.

metadata_usages(element: syside.Element) → list[syside.MetadataUsage]

Return all metadata usages owned by element (prefix and body), linear in the number of metadata usages.

Reads the core metadata view and keeps the syside.MetadataUsage entries.

multiplicity_bounds(feature: syside.Feature) → syside.query.MultiplicityBounds | None

Return the multiplicity bounds feature itself declares, or None.

Declared only, no resolution: None means feature declares no multiplicity (or declares one whose bounds the core has not computed), not that the effective multiplicity is absent. In SysML a feature with no declared multiplicity defaults to 1..1, and a redefining feature takes the redefined feature’s multiplicity; neither default is applied here, so always branch on None before reading .lower/.upper. Both returned bounds are inclusive (an unbounded * upper bound is UNBOUNDED).

generalization_names(type_: syside.Type) → list[str]

Return the rendered qualified names of the direct heritage targets.

A target with no qualified name is skipped.

redefined_by(model: syside.Model, feature: syside.Feature) → list[syside.Feature]

Return the features that redefine feature (inverse of redefinition).

Linear in model size: scans the model’s redefinition relationships via specializations().

redefines(feature: syside.Feature) → list[syside.Feature]

Return the features feature redefines (via redefinition), linear in the number of redefined features.

referenced_feature(expression: syside.FeatureReferenceExpression) → syside.Feature

Return the feature referenced by expression.

Raises ValueError if the reference is unresolved (the core referent is None), so the return type is a non-optional syside.Feature.

siblings(element: syside.Element) → list[syside.Element]

Return the other direct contents of element’s owner.

element itself is excluded. An element with no owner has no siblings.

specializes(type_: syside.Type, candidate_supertype: syside.Type) → bool

Return whether type_ specializes candidate_supertype, directly or indirectly.

True exactly when candidate_supertype is one of type_’s heritage targets, or one of their heritage targets, and so on: the same walk as supertypes(), so it also crosses conjugation. specializes(cp, PD) is True for port cp : ~PD although KerML has no specialization from ~PD to PD, and cp’s PD features run in the opposite direction. To test KerML specialization alone, check membership in generalizations(type_, transitive=True, exclude=syside.Conjugation).

superclassifiers(type_: syside.Type) → list[syside.Classifier]

Return the classifiers type_ directly subclassifies (KerML superclassifiers), linear in their number.

Subclassification only: the :> between classifiers. A feature (any part/attribute/port/… usage) specializes through subsetting, redefinition, and typing instead, and KerML allows only a classifier as a subclassifier, so for a feature this is always []; see subsets(), redefines(), and declared_types_of() for those, or supertypes() for everything inherited from, directly or indirectly, through every heritage kind.

subject_of(element: syside.query._CaseLike) → syside.Usage | None

Return the subject parameter in effect for element, constant time.

Reads the core subject_parameter accessor, present on requirement and case usages/definitions (and their subtypes). SysML gives these types exactly one subject, inherited when not declared (the SysML derive...SubjectParameter constraints of requirement and case definitions and usages all read the KerML featureMembership, which includes inherited memberships): an element with no subject of its own yields the inherited standard-library parameter (e.g. Requirements::RequirementCheck::subj), not None. To ask whether element declares a subject itself, use declared_subject_of(). None is only the core accessor’s miss case and does not occur for a well-formed model.

declared_subject_of(element: syside.query._CaseLike) → syside.Usage | None

Return the subject element declares itself, or None, constant time.

None means element writes no subject in its own body, so its effective subject is inherited (see subject_of(), which returns that inherited parameter). A declared subject is also the effective one, since SysML has it redefine every inherited subject parameter.

subsets(feature: syside.Feature) → list[syside.Feature]

Return the features feature subsets, linear in the number of subsetted features.

Follows subsetting heritage including its subtypes: KerML defines redefinition as a kind of subsetting, so a redefined feature appears here as well as in redefines(); unioning the two counts the redefined feature twice.

subclassifiers(model: syside.Model, type_: syside.Type) → list[syside.Classifier]

Return the classifiers that directly subclassify type_ (KerML subclassifiers).

The inverse of subclassification only (the inverse counterpart of superclassifiers(), not of supertypes()): a feature that subsets, redefines, or is typed by type_ is not returned.

Linear in model size: the core exposes no inverse index, so this scans the model’s subclassification relationships via specializations().

supertypes(type_: syside.Type) → list[syside.Type]

Return every type type_ inherits from, directly or indirectly, nearest first.

These are type_’s direct and indirect KerML supertypes (every specialization kind, not only :> between classifiers) plus the types reached by crossing a conjugation. KerML does not make a conjugated type’s original a supertype of it, but this walk includes it: for port cp : ~PD, the result of supertypes(cp) contains PD even though the only edge from ~PD to PD is a Conjugation, and the features inherited through it have their directions reversed. Pass exclude=syside.Conjugation to generalizations() to stop at conjugations.

Each type appears once, in the order the walk first reaches it: the direct heritage targets, then theirs, and so on. Base::Anything is usually last, but a type that names it as a direct heritage target (part def C :> Base::Anything, Deep;) reaches it first. That is type_’s heritage targets, and their heritage targets, and so on until no new type is reached (the transitive closure of the heritage relationship). Heritage covers every specialization kind (subclassification, feature typing, subsetting, and redefinition) and also conjugation, so for a feature the result includes the features it subsets or redefines (a KerML Feature is a Type, e.g. Base::things), not only classifiers.

For example, given the SysML:

part def Wheel;
part wheel : Wheel;

the result includes Wheel and everything it inherits, plus the library features wheel subsets:

syside.query.supertypes(wheel)
# [Wheel, Parts::parts, Parts::Part, Items::items, Items::Item, ...,
#  Base::things, Base::Anything]

Because it follows every kind, this is a larger set than repeating superclassifiers() until nothing new is reached, since that function follows subclassification (:> between classifiers) alone; use syside.query.generalizations() with via= to follow one specific kind.

textual_representation(element: syside.Namespace, language: str) → str | None

Return the body of element’s textual representation in language.

Matches language case-insensitively (KerML defines language names as case-insensitive). Returns the body of the first matching representation, or None if none matches. Linear in the number of owned textual representations.

variants(variation: syside.Definition | syside.Usage) → list[syside.Element]

Return the variant usages of a variation variation, linear in the number of variants.

Both syside.Usage and syside.Definition expose a variants accessor.

specializations(model: syside.Model, target: syside.Element, *, via: type[syside.Specialization] | type[syside.Conjugation] | None = None) → list[syside.Element]

Return the elements declaring a heritage relationship to target.

This is the inverse of generalizations() (one direct step): the elements e such that target appears among generalizations(e). For example, with via=syside.Subclassification it returns the direct subtypes of target; with via=syside.Redefinition the features that redefine target.

via filters by relationship type exactly as in generalizations() (subtypes included; None accepts every kind).

Cost and scope: the core exposes no inverse-heritage index, so this scans the model’s relationship nodes via model.nodes(via or syside.Relationship, include_subtypes=True) and keeps those whose resolved target is target. The scan spans every document of the user model (cross-document subtypes are found) and is keyed on object identity, so the match is exact (no name collisions). Cost is linear in the model’s relationship count. Standard-library documents fall outside the MODEL document scope and are not searched.

Results are de-duplicated by object identity and returned in scan order. target itself is never included (a self-referential heritage relationship is ignored).

generalizations(element: syside.Type, *, via: type[syside.Specialization] | type[syside.Conjugation] | None = None, exclude: type[syside.Specialization] | type[syside.Conjugation] | None = None, transitive: bool = False) → list[syside.Element]

Return the heritage targets declared by element.

The heritage of a syside.Type is the set of specialization and conjugation relationships it declares; each one points at a target type (the general of a specialization, the original_type of a conjugation). This returns those targets.

via filters to relationships that are instances of the given relationship type, e.g. syside.FeatureTyping, syside.Subsetting, syside.Redefinition, syside.Subclassification or syside.CrossSubsetting. Subtypes of the given type are included (e.g. via=syside.Subsetting also matches Redefinition and CrossSubsetting). via=None accepts every heritage kind. Only heritage relationships, meaning subtypes of syside.Specialization or syside.Conjugation, are valid; a non-heritage kind such as syside.Disjoining is a static type error (it never appears in heritage, so it could only ever yield []).

exclude is the complement of via: relationships that are instances of the given type (and its subtypes) are skipped. A transitive walk does not cross an excluded relationship, so the result is the closure reachable without that heritage kind. For example, exclude=syside.ReferenceSubsetting follows every other specialization (including plain syside.Subsetting, of which ReferenceSubsetting is a subtype) but never the reference-subsetting edge a connector introduces. That is the basis for asking what types a feature has independently of what a connection imposes on it.

``via`` and ``exclude`` are conjunctive (ANDed). A relationship is followed if and only if it satisfies both restrictions: it is an instance of via (or via is None) and it is not an instance of exclude (or exclude is None). Passing the same type to both, or a via that is a subtype of exclude, therefore matches nothing and yields [].

With transitive=False (the default) only the direct targets are returned. With transitive=True the chain is followed to its closure: each target’s own heritage targets are added, transitively. The via and exclude filters apply at every step of a transitive walk.

Results are de-duplicated by object identity and returned in first-seen order; element itself is never included. Cyclic heritage (which a malformed model can contain) terminates rather than looping forever.

element is a syside.Type: only types declare heritage. This is linear in the number of heritage relationships visited (the direct count, or the reachable closure when transitive is set), each step constant time.

walk_containment(element: syside.Element, *, direction: Literal[up, down], max_depth: int | None = None) → Iterator[syside.Element]

Walk the ownership tree from element, yielding the elements reached.

With direction="up" the walk follows the owner chain: it yields element’s owner, then that owner’s owner, and so on up to the root namespace. With direction="down" it traverses owned_elements breadth-first: first the direct contents, then their contents, and so on. In neither direction is element itself yielded.

max_depth bounds the walk. max_depth=1 yields only the elements one step away (the owner, or the direct contents); max_depth=None (the default) is unbounded. max_depth=0 yields nothing. A negative max_depth is treated as 0.

The downward walk de-duplicates by object identity and is cycle-safe: a well-formed ownership tree is acyclic, but a malformed model is still guaranteed to terminate and to yield each element at most once. The upward walk likewise stops if the owner chain ever revisits an element.

Downward traversal drains each owned_elements LazyIterator with a single .collect() (linear per node), never per-element Python iteration over the live iterator.

Enumerations

class LibraryElements

Which documents, and which library elements within them, the find_* lookups that take it and the index builders cover (the lookups are listed in the syside.query._functions module docstring).

  • NONE – the documents WORKSPACE scans (environment documents are not scanned, exactly as under WORKSPACE, with the same find_metadata_usages() exception), minus every element with is_library_element set, including the workspace’s own library package namespaces and their contents – a library package is itself a library element.

  • WORKSPACE (the default everywhere) – every document the model itself loaded (model.documents), including its own library package namespaces. Documents supplied through the model’s environment are not scanned: the SysML/KerML standard libraries, and any third-party documents added to the environment (e.g. via Model.to_environment) alike. Environment membership is the discriminator, not DocumentTier – with one exception: find_metadata_usages() walks containment from a scope element and has no model to consult, so it can only recognise the standard library by DocumentTier.StandardLibrary; see its docstring.

  • ALL – workspace and every environment document both.

The value is tested against each element a lookup finds or a builder indexes, never against the elements it reaches through references (see the syside.query._functions module docstring). A lookup and the index backing it must use the same value; a mismatch is a caller error (see the module docstring’s contract).

Which documents, and which library elements within them, the find_* lookups that take it and the index builders cover (the lookups are listed in the syside.query._functions module docstring).

  • NONE – the documents WORKSPACE scans (environment documents are not scanned, exactly as under WORKSPACE, with the same find_metadata_usages() exception), minus every element with is_library_element set, including the workspace’s own library package namespaces and their contents – a library package is itself a library element.

  • WORKSPACE (the default everywhere) – every document the model itself loaded (model.documents), including its own library package namespaces. Documents supplied through the model’s environment are not scanned: the SysML/KerML standard libraries, and any third-party documents added to the environment (e.g. via Model.to_environment) alike. Environment membership is the discriminator, not DocumentTier – with one exception: find_metadata_usages() walks containment from a scope element and has no model to consult, so it can only recognise the standard library by DocumentTier.StandardLibrary; see its docstring.

  • ALL – workspace and every environment document both.

The value is tested against each element a lookup finds or a builder indexes, never against the elements it reaches through references (see the syside.query._functions module docstring). A lookup and the index backing it must use the same value; a mismatch is a caller error (see the module docstring’s contract).

Which documents, and which library elements within them, the find_* lookups that take it and the index builders cover (the lookups are listed in the syside.query._functions module docstring).

  • NONE – the documents WORKSPACE scans (environment documents are not scanned, exactly as under WORKSPACE, with the same find_metadata_usages() exception), minus every element with is_library_element set, including the workspace’s own library package namespaces and their contents – a library package is itself a library element.

  • WORKSPACE (the default everywhere) – every document the model itself loaded (model.documents), including its own library package namespaces. Documents supplied through the model’s environment are not scanned: the SysML/KerML standard libraries, and any third-party documents added to the environment (e.g. via Model.to_environment) alike. Environment membership is the discriminator, not DocumentTier – with one exception: find_metadata_usages() walks containment from a scope element and has no model to consult, so it can only recognise the standard library by DocumentTier.StandardLibrary; see its docstring.

  • ALL – workspace and every environment document both.

The value is tested against each element a lookup finds or a builder indexes, never against the elements it reaches through references (see the syside.query._functions module docstring). A lookup and the index backing it must use the same value; a mismatch is a caller error (see the module docstring’s contract).

Which documents, and which library elements within them, the find_* lookups that take it and the index builders cover (the lookups are listed in the syside.query._functions module docstring).

  • NONE – the documents WORKSPACE scans (environment documents are not scanned, exactly as under WORKSPACE, with the same find_metadata_usages() exception), minus every element with is_library_element set, including the workspace’s own library package namespaces and their contents – a library package is itself a library element.

  • WORKSPACE (the default everywhere) – every document the model itself loaded (model.documents), including its own library package namespaces. Documents supplied through the model’s environment are not scanned: the SysML/KerML standard libraries, and any third-party documents added to the environment (e.g. via Model.to_environment) alike. Environment membership is the discriminator, not DocumentTier – with one exception: find_metadata_usages() walks containment from a scope element and has no model to consult, so it can only recognise the standard library by DocumentTier.StandardLibrary; see its docstring.

  • ALL – workspace and every environment document both.

The value is tested against each element a lookup finds or a builder indexes, never against the elements it reaches through references (see the syside.query._functions module docstring). A lookup and the index backing it must use the same value; a mismatch is a caller error (see the module docstring’s contract).

Used by: