sysml2py
A pure-Python library for building SysML v2 models — generated, not handcrafted.
sysml2py is a pure-Python API for constructing SysML v2 textual models.
You write Python; it emits spec-correct .sysml notation. Its AST node
classes, builder API, and printer are deterministically generated by a
companion project (sysml2py-lab) from the SysML v2 grammar + relationship
model + corpus — so the library stays in lockstep with the language instead
of drifting behind a hand-written parser.
from sysml2py import PackageBuilder, Part, Attribute
pkg = PackageBuilder('MyModel')
stage = Part('Stage_1')
stage.add(Attribute('mass'))
stage.add(Attribute('thrust'))
pkg.add(stage)
print(pkg.dump())
package MyModel
{
part Stage_1
{
attribute mass;
attribute thrust;
}
}
Why a full overhaul? What happened before 1.0.0
The 0.5.x line was a hand-maintained parser built on textX, astropy, and
PyYAML (grammar/SysML_compiled.tx + hand-written classes) that loaded slow,
dragged in heavy runtime dependencies, and covered only a sliver of SysML v2.
Maintaining it meant hand-editing parser code every time the language moved.
1.0.0 is a ground-up rewrite with a different philosophy. We no longer
handcraft the implementation. Instead, sysml2py-lab — the generation
factory — consumes:
- the SysML v2 grammar (the authoritative syntax + relationships),
- a relationship model of which modifiers and children each element allows, and
- a growing corpus of real models as usage evidence,
…and deterministically generates the entire sysml2py library from
those inputs. No growing pile of hand-written classes to maintain — when the
language or the corpus changes, we regenerate.
What that buys you
- Pure standard library at runtime.
pip install sysml2pynow pulls nothing beyond the stdlib — no textX, no astropy, no yaml. (The old 0.5.x line shipped a textX-backed parser with astropy as a runtime dependency.) - Broad AST coverage with an honest fallback. Hundreds of generated node
classes mirror the grammar rules. Constructs not yet structurally
represented are preserved as
Unsupportednodes carrying their raw text — zeroNotImplementedError; nothing is silently dropped. - Reproducible provenance. Every install records the exact grammar, spec, relationship-model, and corpus SHAs plus the generator version that produced the wheel — you can always prove which inputs your library came from.
- A cast that stays true (during generation). Output the generator produces is re-parsed and compared element-for-element against the model that produced it, and validated against an independent SysML v2 oracle — if it doesn't round-trip, it doesn't ship.
Why generate instead of handcrafting it ourselves?
Two words: drift and scale.
Hand-written parsers drift. They accumulate special cases, silently diverge from the spec, and every grammar adjustment becomes a manual archaeology project. Generation inverts that: the grammar is the source of truth, and the implementation is derived from it mechanically. When SysML v2 evolves or the corpus reveals a new construct, we update the inputs and regenerate — the whole library stays consistent by construction, and a regression that breaks an earlier model is caught the same way it was introduced.
This is the same reason we validate the output against an external oracle rather than our own assumptions: the generator never gets to grade its own homework.
Building a model (friendly builder API)
Friendly names drop the Builder/Usage/Definition suffixes — Part,
Attribute, Item, Port, Requirement, Reference, ... are builders.
Usage vs. definition is chosen at creation via kind=; add() appends
children so a full package dumps from nested composition.
from sysml2py import PackageBuilder, Part
pkg = PackageBuilder('Rocket')
pkg.add(Part('Stage_1'))
pkg.add(Part('Stage_2'))
print(pkg.dump())
package Rocket
{
part Stage_1;
part Stage_2;
}
A definition is the same class with kind= at creation:
>>> Part('Stage_1', kind='definition').dump()
'part def Stage_1;'
Kinds without a definition form (Package, Comment, Actor,
Reference, Transition, Import, Message, Succession,
AliasMember, Subject, ObjectiveRequirement) reject
kind='definition' with a ValueError — the generator never emits a
definition it cannot cast back, and a class-name kind like
'PartDefinition' raises too, since it would print invalid SysML.
Container names that collide with AST node classes (Package, Comment,
Import, Message, Succession, AliasMember) keep the *Builder
suffix — use PackageBuilder, ImportBuilder, etc. The classic *Builder
names (PartUsageBuilder, ...) remain available if you prefer them.
Units
Value-bearing attributes are first-class. Pass a value and a unit and the
library renders the SysML [unit] syntax; inside a container it also imports
the unit automatically as a public import (KERML-74 requires an explicit
visibility modifier):
from sysml2py import PackageBuilder, Attribute
pkg = PackageBuilder('Rocket')
pkg.add(Attribute('mass', value=1000, unit='kg'))
print(pkg.dump())
package Rocket
{
public import SI::kg;
attribute mass = 1000[kg];
}
Note: a bare, childless attribute dumps without the import — the import is hosted in a container's body, so the container form above is the one that emits it:
Attribute('mass', value=1000, unit='kg').dump()→attribute mass = 1000[kg];
You can also pass an astropy Quantity and the unit is derived from it:
import astropy.units as u
Attribute('mass', value=1000 * u.kg).dump()
The unit='kg' string path above is pure standard library — no astropy
needed for that. Only the Quantity form requires astropy, and then you must
install it (pip install astropy; it ships only in the dev extra) and
import astropy.units as u yourself, since the sysml2py runtime stays
pure-stdlib.
Provenance & reproducibility
Check which exact inputs produced an install:
from sysml2py import provenance
print(provenance.PROVENANCE)
{'grammar_sha256': '...', 'spec_sha256': '...',
'children_sha256': '...', 'modifiers_sha256': '...',
'corpus_manifest_sha256': '...', 'generator_version': '1.0.1',
'generated_at': '...'}
Also in the package
The generated AST classes and helpers are all public: Node and its
hundreds of subclasses, walk / walk_preorder / walk_postorder /
find_all traversal, print_model / beautify, normalize_text,
canonical_equals for structure-aware comparison, and the *Builder
classes. See the wiki for
tutorials and deeper examples.
Compatibility & migrating from 0.5.x
This is the 1.x line. The builder names you remember — Part,
Attribute, Item, Port — are preserved (0.5.x already exported them),
and _set_name / _set_child / dump() still work. But 1.0.0 is a
ground-up rewrite, so several things changed deliberately:
- No text parser / no
Model. 0.5.x's headline feature was parsing text into a model (load,loads,load_grammar). 1.0.0 is a builder library — it constructs models in Python and dumps them to text; it does not parse text back.Modelis gone. - No runtime deps. 0.5.x bundled astropy; 1.0.0 pulls nothing beyond the
stdlib. Add astropy to your own project only if you use the
Quantityconvenience for units. Packagechanged meaning. In 0.5.xfrom sysml2py import Packagewas a package builder; in 1.0.0 it is the generated AST node class. For a builder usePackageBuilder.- Usage vs. definition chosen at creation via
kind=. (In 0.5.x the first positional argument was adefinitionboolean, soPart('X')bound'X'to that boolean rather than to the name — a silent break.) - Friendly names drop the
Builder/Usage/Definitionsuffixes (PartUsageBuilderis now justPart, etc.). Container names that collide with AST node classes keep*Builder(PackageBuilder, ...). set_value/get_value/set_directionare gone — passvalue=/unit=to the builder constructor instead.get_definitionis onNode, not the builder — usebuild_node().get_definition().
For the full step-by-step migration guide, see
migration-0.5-to-1.0.md
in the sysml2py-lab repo.
Background
sysml2py(this repo) — the generated, distributable library on PyPI.sysml2py-lab— the generation factory that produces this library from the SysML v2 grammar, relationship model, and corpus. Grammar and corpus content, and bug reports against generated behavior, live there.- Windtrader — the independent SysML v2 validator used as the correctness oracle during generation.
License
MIT.
Metadata
Release files for sysml2py 1.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| sysml2py-1.0.1.tar.gz | 85.4 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sysml2py-1.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 175.3 kB
Release files / sysml2py-1.0.1.tar.gz
| Download URL | sysml2py-1.0.1.tar.gz |
|---|---|
| Size | 85.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
402f05a32231f2216988d27f220f1aaee4c3b41663c4da0544d78f6f2ce1035e
|
|
BLAKE2b-256 checksum How to use checksums |
45194a704905521cf4204c95fe1e16e29ca1b5eedeb96e96bd019811a7ac8af1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Release files / sysml2py-1.0.1-py3-none-any.whl
| Download URL | sysml2py-1.0.1-py3-none-any.whl |
|---|---|
| Size | 89.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6115f2bf42490b31935849319540abf3b6da2f3fae4e453d31a5877b960354e4
|
|
BLAKE2b-256 checksum How to use checksums |
420aedf6608949bd0ea0c349d3d527e1e7130519420057d61c06e8d34d82eb80
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Security for sysml2py 1.0.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.