Query API
New in v0.11.0
The Query API is the part of Syside Automator for asking questions about a model
from Python. Find the element with a given name, list what a part contains,
collect every usage of a definition, check the constraints a model asserts,
and turn the rules a model must follow into tests. It consists of three
packages: query for the questions,
evaluation for checking asserted
constraints, and testing for writing rules as
pytest tests. Every function takes a loaded model or an element from it and
returns ordinary Python values: lists, booleans, numbers, strings or None.
You need Syside Automator installed and a license activated; see Install Automator. If you have never loaded a model from Python, Models as Python objects shows what a loaded model looks like.
Try it
Save the script below as rover.py and run python rover.py. It carries its own
model as a string, so nothing else is needed:
import syside
from syside import query
MODEL = """
package Mission {
part def Wheel;
part def Motor;
part def Rover {
part wheels : Wheel [4];
part motor : Motor;
part spare : Wheel;
}
}
"""
def main() -> None:
(model, diagnostics) = syside.load_model(sysml_source=MODEL)
assert not diagnostics.contains_errors(warnings_as_errors=True)
# Find a definition by name, then everything typed by it.
wheel = query.find_by_name_and_type(model, "Wheel", syside.PartDefinition)
assert wheel is not None
for part in query.find_elements_of_type(model, syside.PartUsage):
if query.specializes(part, wheel):
print(f"{part.qualified_name} is a Wheel")
# List what a definition contains, and how many of each.
rover = query.find_by_name_and_type(model, "Rover", syside.PartDefinition)
assert rover is not None
for child in query.contents(rover):
if not isinstance(child, syside.PartUsage):
continue
bounds = query.multiplicity_bounds(child)
count = "1" if bounds is None else f"{bounds.lower}..{bounds.upper}"
print(f"{rover.name} has {count} {child.name}")
if __name__ == "__main__":
main()
It prints:
Mission::Rover::wheels is a Wheel
Mission::Rover::spare is a Wheel
Rover has 4..4 wheels
Rover has 1 motor
Rover has 1 spare
Four functions did the work.
find_by_name_and_type fetches one element by
name and kind.
find_elements_of_type lists every
element of a kind in the whole model. specializes asks
whether a part is typed by a definition, following the whole chain of specializations,
so a part typed by a subtype of
Wheel counts too. contents lists what an
element directly owns, and multiplicity_bounds
reads the [4] off a part. Replace the model string with paths=["your_model.sysml"]
to run the same script against a file.
Where it helps most
Large models. A question written as a
forloop over every element runs in time proportional to the model, and a script with twenty such loops over a model of millions of elements runs for hours. Build aNameIndex,TypeIndexorMetadataIndexonce, pass it asindex=, and each later lookup is a dictionary read. An index changes how long a query takes, never what it returns.Checks that run on every change.
check_modelevaluates everyassert constraintagainst the instances that bind values and returns a verdict per pair; thetestinghelpers report which elements break a rule, so the check can run in CI beside the code that depends on the model.Reading a model safely. No function in
syside.querychanges the model. To create, edit or delete elements, use the element classes described in Models as Python objects; the two work on the same objects.
Where to go next
Rewrite a script with syside.query – For readers with existing scripts: two examples shown against the element classes and again with
syside.queryCheck asserted constraints from Python – Evaluate every
assert constraintand read the verdictsTest a model with pytest – Write modeling rules as tests that name the elements breaking them
The complete list of functions, with the cost of each, is in the
API reference.