Skip to main content

opensysml

Python client for OpenSysML: parse, inspect and execute SysML v2 models over the sysml-grpc service.

pip install opensysml             # from PyPI, once the first release is published
pip install -e python/          # or from a checkout, at the repository root
import opensysml

model = opensysml.load("model.sysml", strict=True)   # raises on error diagnostics
print(model.eval("1 + 2 * 3"))                     # 7

print(model.eval("mass", subject="Demo::sedan"))   # 1200.0 — that object, not the default
                                                   # requires the service's evaluate_subject
                                                   # capability, rather than trusting a
                                                   # service that would ignore the subject

vehicle = model["Vehicle"]                         # by short name or FQN
vehicle.attributes()                               # own and inherited, with resolved facts
inst = model.instantiate("Demo::Vehicle")
inst.mass                                          # 1500.0 [kg] — a Quantity

model.verify_satisfaction()                        # every assert satisfy … by …
model.save("model.ttl")                            # RDF Turtle (experimental)

Declarations can be authored from notation strings while preserving the untouched source:

model.edit().add_part_def("", "Vehicle").apply()
model.edit().add_part("Vehicle", "engine", type="Engine").apply()

Use opensysml.loads(text, language="kerml") for inline KerML content.

Every call goes through the sysml-grpc service, which opensysml starts for you from ~/.opensysml/bin/sysml-grpc; the guide below says how to put it there.

Service ownership

opensysml never stops a service it did not start.

  • A connection that finds a healthy service already listening uses it and takes no ownership of it: nothing is recorded, and closing the connection leaves the service running. Whoever started it decides when it stops.
  • A service opensysml starts is recorded in ~/.opensysml/sysml-grpc-<port>.pid ($OPENSYSML_STATE_DIR overrides the directory) as the service's pid and process start time, plus the pid and start time of the process that started it. Only that process stops it, and only when the last connection holding it is closed or the interpreter exits.
  • The start times are what authenticate the record: a pid is re-checked against the start time written for it, so a pid the operating system has since reused is treated as a stale record — cleaned up, never signalled. A command line that merely looks like sysml-grpc is not identity and is never acted on.
  • A service that crashes leaves a record whose process is gone; the next connection detects that, removes it and starts a service of its own.

Pinned release digests

A download is verified against PINNED_SHA256 in opensysml/binary.py, which pins the SHA-256 of every asset of a release. The .sha256 served beside a binary comes from whoever served the binary, so it detects corruption but not a republished release; a pinned digest is independent of that origin. A download with no pin fails with a message naming the version, rather than falling back to the served checksum — $OPENSYSML_ALLOW_UNPINNED_DOWNLOAD=<owner/repo> (or =1 for any repository) accepts same-origin trust explicitly for what it names, with a warning.

At release time, after the service binaries are published and final:

export GITHUB_TOKEN=...            # the release API rate-limits unauthenticated calls
python scripts/pin_release_checksums.py --version v0.0.9 --write
git commit -am 'chore(python): pin release digests for v0.0.9'

The script downloads every sysml-grpc-* asset of that release, hashes what it downloaded, refuses the release if a .sha256 sidecar disagrees with the asset it describes, and rewrites the table in place. --check re-hashes the assets of every pinned release and fails on any disagreement, catching a release republished with another binary. A opensysml release therefore pins the service releases published before it; asking for a newer one needs a newer opensysml (or the explicit opt-in above), and leaves an already-downloaded binary serving rather than refusing to start — only a digest that contradicts a pin is treated as tampering and refuses to fall back.

Version

opensysml/_version.py is the only declaration: the packaging metadata reads it, opensysml.__version__ reports the installed distribution's version, and scripts/check_version.py fails a release whose tag names another version. The version tests therefore require the tree under test to be the installed distribution — pip install -e python/. A wheel of another version installed beside the source tree makes them fail with that remedy: the artifact is what is stale, not the declaration.

Generated typed classes

python -m opensysml.generate model.sysml -o model_types.py emits one class per SysML definition, and the generated hierarchy follows the model's generalization edges: specializes, subsets and redefines all become base classes, because Python has a single notion of inheritance. What tells the two apart is the members, not the bases:

  • a redefinition reuses the redefined feature's name, so its property overrides the base class's property of that name, and takes over the type and multiplicity it does not restate (attribute :>> mass = 2.0; stays float, and a redefined 0..* feature stays a list[...]);
  • a subset under a new name adds a property beside the base class's one, and likewise inherits the type and multiplicity it leaves out.

With multiple supertypes, bases are emitted in declaration order, a target named twice appearing once, and Python resolves members left to right by its usual MRO. A base another declared base already specializes is left implicit — Hybrid :> Vehicle, Electric where Electric :> Vehicle emits class Hybrid(Electric), which Python can linearize and which keeps both relationships and Electric's properties. Where no order linearizes at all (two bases specializing a shared pair in opposite orders), rather than emit a module that fails to import, the generator keeps the bases it can and records what it left out as a comment on the class, naming the edge:

class Both(One):
    # specializes Demo::Two, left out: Python cannot linearize it with the bases above

A base outside the generated model is reported the same way. Both are the model's hierarchy being wider than Python's, not facts being discarded — the service reports every edge, and Symbol.specializations still carries them all.

Limitation, unchanged: only structural usages (attribute, part, item, occurrence, individual, port, enum) become properties. Behavioral and connector usages — action, state, calc, constraint, requirement, connection, flow, interface, allocation, case — are not instance feature values, so a generated class has no member for them; reach them through model["Demo::Vehicle"], verify_constraint and verify_satisfaction.

Names that shadow builtins

Neither builtin name is a live part of the API any more: the module-level evaluation function is opensysml.evaluate, and the execution error is opensysml.ExecutionError. opensysml.eval and opensysml.errors.RuntimeError remain as deprecated aliases that warn on use, out of their modules' __all__, so a star-import binds neither.

import opensysml

opensysml.evaluate("1 + 2", file_path="model.sysml")   # opensysml.eval warns
model.eval("mass", subject="Demo::sedan")            # a method shadows nothing
from opensysml import eval          # shadows the builtin in this module — don't

Guidance for this package and for code around it:

  • Call opensysml.evaluate. opensysml.eval still works and returns the same result, warning DeprecationWarning; it goes away in 1.0.0.
  • Import the package, not its names, for anything named like a builtin.
  • Catch opensysml.ExecutionError (or its base opensysml.OpenSysMLError), never opensysml.errors.RuntimeError, which warns and is due for removal.
  • Do not name a new public function or exception after a builtin.

0.2.0 therefore publishes evaluate as the name to write, with both builtin names deprecated rather than removed, so code written against 0.1.x keeps running until 1.0.0.

Running the tests

make build                                    # builds bin/sysml-grpc
pip install -e python/ && pip install pytest pytest-mock
python -m pytest python/tests/ -q             # service-backed tests skip

Tests that need a service skip when none answers on localhost:50051 and no binary is available to spawn one. Where a service is provided — as in CI — export OPENSYSML_REQUIRE_SERVICE=1, and its absence fails instead of skipping.

Documentation

Release files for opensysml 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for opensysml 0.3.1
File Size Uploaded
opensysml-0.3.1.tar.gz 198.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for opensysml 0.3.1
File Interpreter ABI Platform
opensysml-0.3.1-py3-none-any.whl Python 3 none any Details

Total release size: 311.2 kB

Release files / opensysml-0.3.1.tar.gz

Download URL opensysml-0.3.1.tar.gz
Size 198.6 kB
Tags Source
SHA-256 checksum
How to use checksums
24fb488a18a4a0f48a219853c46277e039f64e50d916f2176aa84574402230ce
BLAKE2b-256 checksum
How to use checksums
700b5a8a9e5b3f3b6b2d32936f1954bae3947b10115621afa87904faab03ed4a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.16

Release files / opensysml-0.3.1-py3-none-any.whl

Download URL opensysml-0.3.1-py3-none-any.whl
Size 112.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3b985522843be37767f0ddf697064f6eaa39d87b30d80af4481dfb62922ff1e8
BLAKE2b-256 checksum
How to use checksums
392ca3c81c439e3952955a0c07686efe6cd8415c058075d98f6984ed3afb7619
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.16

Release history Release notifications | RSS feed

0.9.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.2

2 release files

This release

0.3.1 This release

2 release files

0.3.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page