This notebook introduces :> specialization; after running it you can declare that the whole is a kind of the concept that names it.
The previous notebook declared HeatingSystem and ControlSystem as standalone types with no supertype. This notebook declares Toaster, the actual whole, as a specialization of ToastingSystem: the system that carries out the purpose is a kind of the concept that names it, not the other way around. Toaster’s body (its cycle-time slot and its subsystem parts) is completed in the next notebook.
from pathlib import Path
import opensysml
from toaster.report import format_diagnostics
conn = opensysml.connect(version="v0.9.0")
# editor.add_part_def(owner='ToasterDemo', name='Toaster', specializes=['ToastingSystem']) when API ships
TOASTER_SPEC_DEF = "part def Toaster :> ToastingSystem;"
print(TOASTER_SPEC_DEF)part def Toaster :> ToastingSystem;
:> declares that every Toaster is a kind of ToastingSystem, inheriting the purpose action ToastBread it performs. The supertype must be in scope: ToastingSystem was declared in notebook 01 and is present in the cumulative model.
TOASTER_INCREMENT = TOASTER_SPEC_DEF
source = Path("../../models/ch01-cumulative.sysml").read_text()
model = conn.load_from_content(source, strict=False)
assert model.ok, f"Model failed: {format_diagnostics(model.diagnostics)}"Toaster :> ToastingSystem loads with no diagnostics. The next cell checks what happens when the named supertype does not exist.
# Negative control: the supertype must exist in the same package or be imported.
bad_source = """
package Bad {
part def Toaster :> UndefinedBase;
}
"""
bad = conn.load_from_content(bad_source, strict=False)
assert not bad.ok
print("Expected error:", bad.diagnostics[0].message)Expected error: unresolved reference: UndefinedBase
Specializing an undefined type raises an unresolved-reference error, reported in bad.diagnostics[0].
toaster = model.find("ToasterDemo::Toaster")
assert toaster is not None
specs = toaster.specializations
print(f"Toaster specializations ({len(specs)}):")
for s in specs:
print(f" {s.kind}: {s.declared} -> {s.target_id}")
conn.close()Toaster specializations (1):
specializes: ToastingSystem -> ToasterDemo::ToastingSystem
toaster.specializations reports the target above, confirming Toaster :> ToastingSystem is recorded, not just parsed.
part def Toaster :> ToastingSystem; printed above loaded without error, and toaster.specializations shows the target reported below, confirming the relationship is now part of the model.
Try the chapter exercise in exercises/ch01/exercise.ipynb: declare that CoffeeMaker specializes BrewingSystem and confirm the specialization records correctly.