sysmlpy vs PySysML2: Comparison¶
A detailed comparison of two Python libraries for SysML v2 textual modeling.
| Feature | sysmlpy | PySysML2 |
|---|---|---|
| Version | 0.31.3 | 0.1.1 |
| Maintainer | Jon Fox (mycr0ft) | Keith Lucas (DAF Digital Transformation Office) |
| License | MIT | Apache 2.0 |
| Repository | https://github.com/mycr0ft/sysmlpy | https://github.com/DAF-Digital-Transformation-Office/PySysML2 |
| Language | Python 3.9+ | Python 3.8+ |
| Parser | ANTLR4 (full SysML v2 grammar) | ANTLR4 (subset grammar) |
| Stars | ~250+ | ~60 |
| Commits | 1000+ | 2 (initial commit + 1 update) |
| Core source | ~37K lines across 11 modules | ~1.5K lines across 4 modules |
1. Parsing Coverage¶
sysmlpy¶
Full SysML v2 / KerML metamodel via ANTLR4 grammar. 319 grammar classes in grammar/classes.py covering all SysML v2 constructs:
Structural: packages, parts, items, ports, interfaces, connections, flows (with flow definitions), attributes (with redefinition), enumerations, references, allocations, messages
Behavioral: actions (definition usage, shorthand, succession, decomposition), states (with transitions), calculations, constraints
Control flow: if/else, while loops, for loops, fork, join, decision, send, accept, terminate
Requirements & analysis: requirements, use cases, objectives, analysis cases, verification cases, trade studies
Views & cross-cutting: views (general, action flow, interconnection, state transition, package), viewpoints, concerns, metadata, renderings, individuals
Full round-trip: all 77 grammar tests pass (parse -> grammar object -> dump() -> re-parse -> identical)
# sysmlpy parsing examples
from sysmlpy import loads, parse, load_grammar
# Parse from string — returns Model
model = loads("""
package Simple {
part def Motor;
part myMotor : Motor;
}
""")
# Parse with error handling (never raises)
m, errors = parse("invalid syntax {{}")
# returns (None, [error lines])
# Raw dict access
d = load_grammar("part def A;")
PySysML2¶
Partial SysML v2 grammar with ~15 element types, 4 relationship types:
Elements: package, part definition, attribute, comment, doc, import, include, item, objective, port, use case, connection end part
Relationships: connect, specializes, redefines, message
Not implemented (raise NotImplementedError): actor, about
Not covered at all: actions, states, constraints, calculations, flows (no flow def), interfaces, requirements, views/viewpoints, control flow, analysis cases, enumerations, references, allocations, messages (partially), metadata, renderings
# PySysML2 parsing examples
from pysysml2.modeling import Model
model = Model()
model.from_sysml2_file("model.sysml2")
# No string-based API; only file-based
# No load_grammar/loads equivalent — must write to file first
# No error handling API
2. Programmatic API¶
sysmlpy¶
Full programmatic construction of SysML models with type checking, unit support, and round-trip text generation:
from sysmlpy import Part, Item, Attribute, Action, Port, Connection, State, \
Requirement, Case, Constraint, Calculation, Enumeration, ureg
# Structure
sensor = Part(name="sensor")
camera = Part(name="camera")
lens = Item(name="lens")
mass = Attribute(name="mass")
mass.set_value(100 * ureg.kilogram)
camera.add_child(mass)
sensor.add_child(camera)
sensor.add_child(lens)
print(sensor.dump())
# part sensor {
# part camera {
# attribute mass = 100 kg;
# }
# item lens;
# }
# Behavior
action = Action(definition=True, name='Focus')
action.add_input('scene', 'Scene')
action.add_output('image', 'Image')
# Ports & connections
port1 = Port(name='data')
port2 = Port(name='power')
conn = Connection(name='bus', source=port1, target=port2)
# States
state = State(name='Idle')
state.add_transition('Active', trigger='start')
# Requirements
req = Requirement(name='Performance')
req.add_text('The system shall respond in < 100ms')
# Analysis
case = Case(name='ThermalCase')
constraint = Constraint(name='PowerBudget')
calc = Calculation(name='Sum')
# Typed values with units
mass.set_value(5.0 * ureg.kilogram)
length = Attribute(name='length')
length.set_value(10.0 * ureg.meter)
# Enumeration
status = Enumeration(name='Status', values=['ON', 'OFF', 'STANDBY'])
PySysML2¶
Minimal programmatic API. Elements are constructed by the parser only — no factory-style Part(name=...) construction. Elements carry parsed properties as dict-like attributes:
# PySysML2 — no programmatic construction API
# Elements only exist after parsing a .sysml2 file
model = Model()
model.from_sysml2_file("model.sysml2")
# Traversal via anytree RenderTree
from anytree import RenderTree
for pre, fill, node in RenderTree(model):
if hasattr(node, 'name'):
print(f"{pre}{node.name}")
# Element properties (populated by parser)
part = some_node # from tree
print(part.name) # str
print(part.sysml2_type) # e.g. 'part'
print(part.idx) # int index
print(part.keywords) # list of matched keywords
print(part.value_types) # typing info
print(part.constants) # constant values
print(part.multiplicity) # multiplicity strings
# No set_value(), add_child(), dump(), etc.
# No unit support (units not modeled)
3. Diagram Generation¶
sysmlpy (14 view functions)¶
# PlantUML generation with 14 view types
from sysmlpy import loads, as_general_view, as_action_flow_view, \
as_state_transition_view, as_interconnection_view, as_package_view, \
as_tabular_view, as_data_value_tabular_view, as_relationship_matrix_view
model = loads("""
package Sys {
part def Motor;
part myMotor : Motor;
}
""")
# General view — full graph
print(as_general_view(model))
# @startuml
# skinparam style strictuml
# ...
# @enduml
# Action flow view — control/object flows
print(as_action_flow_view(model))
# State transition view — hierarchical states
print(as_state_transition_view(model))
# Tabular output in multiple formats
print(as_tabular_view(model, output_format="markdown"))
print(as_tabular_view(model, output_format="html"))
# Relationship matrix
print(as_relationship_matrix_view(model))
# Style options: "bw" (default) or "color"
print(as_general_view(model, style="color"))
# Direction: "left_to_right" or "top_to_bottom"
print(as_general_view(model, direction="left_to_right"))
# Custom PlantUML styling
custom = "skinparam backgroundColor #lightyellow"
print(as_general_view(model, custom_style=custom))
PySysML2 (basic graphviz output)¶
# Single graphviz dot/png export — not SysML-aware views
model = Model()
model.from_sysml2_file("model.sysml2")
# Export to graphviz DOT
model.to_dot(out_dir="./output")
# Export to PNG (requires graphviz `dot` command)
model.to_png(out_dir="./output")
# What the output shows: a generic anytree node-link diagram
# No SysML-specific view types (no BDD, IBD, parametric, etc.)
# No PlantUML support
# No tabular/matrix views
# No style customization
4. Semantic Analysis¶
sysmlpy¶
from sysmlpy import loads, analyze
model = loads("""
package Test {
part def Motor;
part myMotor : Motor;
}
""")
result = analyze(model)
# Check for errors
if result.errors:
for issue in result.errors:
print(f"[{issue.severity}] {issue.code}: {issue.message}")
print(f" Element: {issue.element}")
# Raise on errors
result.raise_on_errors() # ValueError if any errors found
# Check severity
print(result.errors) # filter error-severity
print(result.warnings) # filter warning-severity
print(bool(result)) # True if no errors (warnings OK)
9 OCL well-formedness checks:
| Code | Rule |
|---|---|
DUPLICATE_NAME |
No duplicate names in scope |
CYCLIC_SPECIALIZATION |
No cyclic specialization |
INCOMPATIBLE_SUBSETTING |
Subsetting references defined feature |
INCOMPATIBLE_REDEFINITION |
Redefinition references defined feature |
INCOMPATIBLE_PART_DEFINITION |
Part usage typed by PartDefinition |
INCOMPATIBLE_PORT_DEFINITION |
Port usage typed by PortDefinition |
INCOMPATIBLE_FEATURE_CHAIN |
Chained features compatible types |
INVALID_MULTIPLICITY_BOUNDS |
Lower <= upper bound |
UNRESOLVED_IMPORT |
Import target exists |
Import resolution: 5 patterns (private *, specific name, recursive ::*::**, public, protected), hierarchical scope resolution, qualified name lookup (P::A).
Library symbol index: scans 88 bundled .kerml/.sysml library files (~1,417 symbols).
# Semantic analysis with import resolution
from sysmlpy import load_with_dependencies
model, _ = load_with_dependencies("main.sysml", search_paths=["./libraries"])
result = analyze(model)
# Symbol table access
from sysmlpy import SemanticAnalyzer
analyzer = SemanticAnalyzer(model)
scope = analyzer.current_scope
for name, symbol in scope.symbols.items():
print(f"{name}: {symbol}")
PySysML2¶
No semantic analysis module. No validation, no OCL checks, no symbol resolution, no import resolution beyond recording the import element in the tree.
# PySysML2 — no analyze() equivalent
# The parser records imports as elements but does not resolve them
5. Storage Backends¶
sysmlpy¶
from sysmlpy import InMemoryStore, NetworkXStore, KuzuStore, CayleyStore, \
create_store
# Abstract Store protocol with 4 implementations
# In-memory (stdlib, no deps)
store = InMemoryStore()
store.put("part1", {"name": "Motor", "type": "Part"})
store.put("part2", {"name": "Sensor", "type": "Part"})
store.put_edge("part1", "connects", "part2")
# Get/query
print(store.get("part1"))
print(store.query("type", "Part"))
print(store.neighbors("part1", "connects"))
# NetworkX graph analysis
store = NetworkXStore()
# ... populate ...
store.connected_components()
store.centrality()
store.cycles()
store.stats()
store.export_graphml()
# KuzuDB (embedded, persistent, Cypher queries)
store = KuzuStore(database_path="./my_model.kuzu")
# Cayley (remote graph DB via HTTP)
store = CayleyStore(base_url="http://localhost:64210")
# Factory
store = create_store("memory")
store = create_store("networkx")
# Graph traversal
store.descendants("part1")
store.ancestors("part1")
store.path("part1", "part5")
PySysML2¶
No storage abstraction. Models live in memory as anytree NodeMixin trees. No persistence, no graph DB integration, no query language support.
# PySysML2 — only in-memory anytree trees
# Export via to_JSON(), to_csv(), to_excel() — file-based serialization only
model.to_JSON("./output.json") # anytree JSON export
model.to_csv("./output.csv") # pandas DataFrame export
model.to_excel("./output.xlsx") # Excel via openpyxl
6. Output Formats¶
| Format | sysmlpy | PySysML2 |
|---|---|---|
SysML v2 text (dump()) |
Yes (full round-trip) | No |
| PlantUML | Yes (14 view types) | No |
| Graphviz DOT | No (uses PlantUML) | Yes (basic tree) |
| PNG | Via PlantUML renderer | Via graphviz |
| JSON | Yes (get_definition()) |
Yes (anytree) |
| CSV | Via pandas export | Yes (via anytree->DataFrame) |
| Excel | Via pandas export | Yes (via openpyxl) |
| Markdown tables | Yes (GridView output) | No |
| HTML tables | Yes (GridView output) | No |
Python repr() |
Yes | No |
| In-place formatting | Yes (--in-place CLI flag) |
No |
7. CLI¶
sysmlpy¶
# Format/analyze SysML files from command line
sysmlpy model.sysml2 # Print model repr
sysmlpy model.sysml2 --dump # Print SysML text
sysmlpy model.sysml2 --json # Print JSON/dict
sysmlpy model.sysml2 --python # Print Python repr
sysmlpy model.sysml2 -i # Format in-place
sysmlpy model.sysml2 --check # Check formatting, exit 1 if unformatted
sysmlpy model.sysml2 -l ./libraries # Use library path
PySysML2¶
# Export model to various data formats
pysysml2 export model.sysml2 --format json,txt,csv,xlsx,dot,png
8. Test Suite¶
| Metric | sysmlpy | PySysML2 |
|---|---|---|
| Total tests | 545+ | ~14 |
| Test files | 14 | 2 |
| Grammar round-trip | 77 (100% pass) | 0 |
| Public API | 54 | 0 |
| PlantUML views | 108 | 0 |
| Semantic analysis | 107 | 0 |
| Navigation | 33 | 0 |
| Validator (ISQ) | 34 | 0 |
| Storage backends | 46 | 0 |
| Conformance (OMG) | 123 (100% pass) | 0 |
| Project loading | 14 | 0 |
| Imports | 16 | 0 |
| Model serialization | — | ~14 |
9. Standard Library¶
sysmlpy¶
Bundles 88 library files (.kerml + .sysml) across kernel, systems, and domain packages:
- kernel/ (36 files): Base, ScalarValues, Collections, Functions (Boolean, Integer, Real, String, Trig, Vector), Occurrences, Performances, Transfers, Clocks, Links, Controls, SpatialFrames
- systems/ (21 files): Actions, Allocations, AnalysisCases, Attributes, Calculations, Cases, Connections, Constraints, Flows, Interfaces, Items, Metadata, Parts, Ports, Requirements, StandardViewDefinitions, States, SysML, UseCases, VerificationCases, Views
- domain/ (30+ files): Analysis, CauseEffect, Geometry, Metadata, QuantitiesUnits, RequirementDerivation
All 88 files indexed (~1,417 symbols) for import resolution.
PySysML2¶
No bundled standard library. No library symbol indexing.
10. Unit Validation¶
sysmlpy¶
from sysmlpy import loads
from sysmlpy.validator import validate_unit_conformance
model = loads("""
package Test {
part def Sensor {
attribute mass : MassValue;
}
part mySensor : Sensor {
attribute mass = 100 kg;
}
}
""")
issues = validate_unit_conformance(model)
# Checks pint unit dimensions against 300+ ISQ type mappings
# Reports mismatches like "Expected dimension [mass], got [length]"
300+ ISQ type-to-dimension mappings across base quantities, space & time, mechanics, electromagnetism, thermodynamics, photometry. 21 US Customary unit definitions (inch, foot, psi, horsepower, BTU, gallon, etc.).
Known issue: antlr_visitor.py hardcodes specialization=None for top-level attributes, so unit validation may miss some type information at the top level.
PySysML2¶
No ISQ validation, no unit system, no dimensional analysis.
11. Multi-file Project Support¶
sysmlpy¶
from sysmlpy import load_files, load_project, load_with_dependencies
# Load specific files (same-named packages merge)
model = load_files(["a.sysml", "b.sysml"])
# Load all .sysml/.kerml in a directory
model = load_project("./models")
# Auto-resolve imports from search paths
model, _ = load_with_dependencies("main.sysml", search_paths=["./libs", "./components"])
PySysML2¶
Single-file loading only. No project resolution.
12. Model Navigation¶
sysmlpy¶
from sysmlpy import loads
model = loads("""
package Sys {
part def Motor;
part sensor : Sensor;
part myMotor : Motor {
attribute power : Real;
}
}
""")
# Typed accessors
print(model.parts) # [Part('sensor'), Part('myMotor')]
print(model.attributes) # [Attribute('power')]
print(model.packages) # [Package('Sys')]
# Searchable mixin
model.find("Motor") # by name (recursive)
model.find(sysml_type="Part") # by type
model.find(name="sensor", recursive=True)
model.find_one("Motor") # raises if 0 or >1
model.all("Part") # shortcut for find(sysml_type="Part")
model.all("Attribute")
# Container protocol
len(model) # number of direct children
"Motor" in model # name containment check
for child in model: # iterate over children
print(child)
# String representation
str(model) # SysML text
# Dictionary round-trip
model.get_definition() # dict form
PySysML2¶
# Only anytree RenderTree traversal
from anytree import RenderTree
for pre, fill, node in RenderTree(model):
if isinstance(node, Element):
print(f"{pre}[{node.idx}] {node.name}")
# No typed accessors (no model.parts, model.attributes, etc.)
# No find/find_one/all search
# No container protocol (len, in, iteration not on Model)
# No dump()/get_definition() round-trip
Summary¶
| Capability | sysmlpy | PySysML2 |
|---|---|---|
| Full SysML v2 grammar coverage | ✓ (319 classes) | ✗ (~15 elements) |
| Round-trip text output | ✓ dump() → parseable SysML |
✗ |
| Programmatic model construction | ✓ 30+ public API classes | ✗ No factory API |
| Type relationships | ✓ (set_type, typing) | ✗ |
| Unit system (pint ISQ) | ✓ 300+ mappings | ✗ |
| Unit validation | ✓ dimensional analysis | ✗ |
| PlantUML diagrams | ✓ 14 view types | ✗ |
| Graphviz output | ✗ (uses PlantUML) | ✓ basic DOT/PNG |
| Semantic analysis | ✓ 9 OCL checks | ✗ |
| Symbol resolution | ✓ hierarchical + libraries | ✗ |
| Import resolution | ✓ 5 patterns | ✗ (records imports only) |
| Storage backends | ✓ 4 (memory/networkx/kuzu/cayley) | ✗ (in-memory only) |
| Standard library | ✓ 88 bundled files | ✗ |
| Multi-file projects | ✓ load_files/project/dependencies | ✗ single file |
| Model navigation/search | ✓ Searchable mixin, 29 typed accessors | ✗ anytree RenderTree only |
| CLI | ✓ format/check/dump/python/in-place | ✓ export (6 formats) |
| CSV/Excel/JSON export | ✓ via pandas | ✓ |
| Control flow grammar | ✓ if/while/for/fork/join/decision/send/accept/terminate | ✗ |
| Requirements | ✓ Requirement, UseCase | ✗ partial use case |
| States | ✓ State with transitions | ✗ |
| Actions | ✓ Action with I/O | ✗ |
| Constraints | ✓ Constraint | ✗ |
| Calculations | ✓ Calculation | ✗ |
| Interfaces | ✓ Interface | ✗ |
| Flow connections | ✓ Flow, FlowDef | ✗ |
| Allocations | ✓ Allocation | ✗ |
| Views/Viewpoints | ✓ View, Viewpoint, Concern | ✗ |
| Analysis cases | ✓ Case, AnalysisCase, VerificationCase | ✗ |
| Enumerations | ✓ Enumeration | ✗ |
| Message passing | ✓ Message | ✓ (partial) |
| ISQ library files | ✓ 88 indexed files | ✗ |
| OMG conformance tests | ✓ 123/123 pass | ✗ |
| Test suite size | 545+ tests | ~14 tests |
| Maturity | Active development (v0.31) | Early prototype (v0.1) |
Verdict¶
sysmlpy provides comprehensive, production-grade SysML v2 support with full grammar coverage, programmatic model construction, semantic analysis, 14 PlantUML view types, 4 storage backends, ISQ unit validation, multi-file project support, and a complete standard library. It supports round-trip text output (dump()) from all grammar objects.
PySysML2 is a lightweight early-prototype focused on data-science-oriented model export (CSV, Excel, JSON, graphviz). It parses a subset of SysML v2 (~15 element types) into an anytree hierarchy and can export to pandas-friendly formats. It has no semantic analysis, no programmatic construction API, no round-trip text output, no unit validation, and no diagram generation beyond basic anytree graphviz trees.
Choose sysmlpy if you need: full SysML v2 modeling, diagram generation, semantic validation, multi-file projects, or programmatic model construction.
Choose PySysML2 if you need: a lightweight parse-to-DataFrame pipeline for simple SysML models with basic element types, and do not need round-trip, validation, diagrams, or behavioral elements.
Comparison generated 2026-06-06 based on sysmlpy v0.31.3 and PySysML2 v0.1.1.