sysmlpy 0.65.0__tar.gz → 0.67.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/PKG-INFO +3 -1
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/pyproject.toml +3 -1
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/__init__.py +9 -1
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/__main__.py +89 -3
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/antlr_visitor.py +50 -1
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/plantuml.py +228 -30
- sysmlpy-0.67.0/src/sysmlpy/spreadsheet.py +259 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/LICENSE +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/README.md +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/antlr/README.md +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/antlr/SysMLv2Lexer.interp +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/antlr/SysMLv2Lexer.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/antlr/SysMLv2Lexer.tokens +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/antlr/SysMLv2Parser.interp +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/antlr/SysMLv2Parser.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/antlr/SysMLv2Parser.tokens +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/antlr/SysMLv2ParserListener.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/antlr/SysMLv2ParserVisitor.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.interp +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.tokens +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/antlr_parser.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/boxes_view.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/definition.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/evaluator.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/examples/attribute_values.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/examples/part_attributes.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/examples/tuples_sequences.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/formatting.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/grammar/antlr4/README.md +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.g4 +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.interp +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.tokens +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/grammar/antlr4/SysMLv2Parser.g4 +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/grammar/antlr4/SysMLv2Parser.interp +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/grammar/antlr4/SysMLv2Parser.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/grammar/antlr4/SysMLv2Parser.tokens +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/grammar/antlr4/SysMLv2ParserListener.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/grammar/antlr4/SysMLv2ParserVisitor.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/grammar/antlr4/desc.xml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/grammar/antlr4/examples/camera.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/grammar/antlr4/examples/toaster-system.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/grammar/antlr4/examples/vehicle-model.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/grammar/antlr4/pom.xml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/grammar/classes.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/interchange.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/LICENSE +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/README.md +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/__init__.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Analysis/AnalysisTooling.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Analysis/SampledFunctions.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Analysis/StateSpaceRepresentation.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Analysis/TradeStudies.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Cause and Effect/CausationConnections.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Cause and Effect/CauseAndEffect.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Geometry/ShapeItems.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Geometry/SpatialItems.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Metadata/ImageMetadata.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Metadata/ModelingMetadata.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Metadata/ParametersOfInterestMetadata.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Metadata/RiskMetadata.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Quantities and Units/ISQ.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Quantities and Units/ISQAcoustics.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Quantities and Units/ISQAtomicNuclear.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Quantities and Units/ISQBase.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Quantities and Units/ISQCharacteristicNumbers.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Quantities and Units/ISQChemistryMolecular.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Quantities and Units/ISQCondensedMatter.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Quantities and Units/ISQElectromagnetism.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Quantities and Units/ISQInformation.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Quantities and Units/ISQLight.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Quantities and Units/ISQMechanics.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Quantities and Units/ISQSpaceTime.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Quantities and Units/ISQThermodynamics.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Quantities and Units/MeasurementRefCalculations.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Quantities and Units/MeasurementReferences.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Quantities and Units/Quantities.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Quantities and Units/QuantityCalculations.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Quantities and Units/SI.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Quantities and Units/SIPrefixes.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Quantities and Units/TensorCalculations.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Quantities and Units/Time.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Quantities and Units/USCustomaryUnits.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Quantities and Units/VectorCalculations.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Requirement Derivation/DerivationConnections.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/domain/Requirement Derivation/RequirementDerivation.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/Base.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/BaseFunctions.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/BooleanFunctions.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/Clocks.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/CollectionFunctions.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/Collections.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/ComplexFunctions.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/ControlFunctions.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/ControlPerformances.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/DataFunctions.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/FeatureReferencingPerformances.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/IntegerFunctions.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/KerML.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/Links.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/Metaobjects.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/NaturalFunctions.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/NumericalFunctions.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/Objects.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/Observation.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/OccurrenceFunctions.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/Occurrences.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/Performances.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/RationalFunctions.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/RealFunctions.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/ScalarFunctions.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/ScalarValues.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/SequenceFunctions.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/SpatialFrames.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/StatePerformances.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/StringFunctions.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/Transfers.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/TransitionPerformances.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/TrigFunctions.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/Triggers.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/VectorFunctions.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/kernel/VectorValues.kerml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/systems/Actions.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/systems/Allocations.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/systems/AnalysisCases.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/systems/Attributes.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/systems/Calculations.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/systems/Cases.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/systems/Connections.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/systems/Constraints.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/systems/Flows.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/systems/Interfaces.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/systems/Items.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/systems/Metadata.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/systems/Parts.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/systems/Ports.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/systems/Requirements.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/systems/StandardViewDefinitions.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/systems/States.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/systems/SysML.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/systems/UseCases.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/systems/VerificationCases.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/library/systems/Views.sysml +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/lsp/__init__.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/lsp/__main__.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/lsp/protocol.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/lsp/server.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/lsp/stdio.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/navigate.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/project.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/semantic.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/store.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/traceability.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/us_customary_units.txt +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/usage.py +0 -0
- {sysmlpy-0.65.0 → sysmlpy-0.67.0}/src/sysmlpy/validator.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sysmlpy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.67.0
|
|
4
4
|
Summary:
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Author: Jon R. Fox (mycr0ft)
|
|
@@ -15,10 +15,12 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.14
|
|
16
16
|
Provides-Extra: graph
|
|
17
17
|
Provides-Extra: kuzu
|
|
18
|
+
Provides-Extra: xlsx
|
|
18
19
|
Requires-Dist: antlr4-python3-runtime (>=4.13)
|
|
19
20
|
Requires-Dist: arrow (>=1.4.0,<2.0.0)
|
|
20
21
|
Requires-Dist: kuzu (>=0.7,<0.12) ; extra == "kuzu"
|
|
21
22
|
Requires-Dist: networkx (>=3.0) ; extra == "graph"
|
|
23
|
+
Requires-Dist: openpyxl (>=3.1) ; extra == "xlsx"
|
|
22
24
|
Requires-Dist: pint (>=0.24)
|
|
23
25
|
Requires-Dist: pyyaml (>=6.0)
|
|
24
26
|
Description-Content-Type: text/markdown
|
|
@@ -6,7 +6,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "sysmlpy"
|
|
9
|
-
version = "0.
|
|
9
|
+
version = "0.67.0"
|
|
10
10
|
description = ""
|
|
11
11
|
authors = [{ name = "Jon R. Fox (mycr0ft)", email = "jon.fox@drfox.com" }]
|
|
12
12
|
readme = "README.md"
|
|
@@ -19,11 +19,13 @@ pyyaml = ">=6.0"
|
|
|
19
19
|
antlr4-python3-runtime = ">=4.13"
|
|
20
20
|
networkx = { version = ">=3.0", optional = true }
|
|
21
21
|
kuzu = { version = ">=0.7,<0.12", optional = true }
|
|
22
|
+
openpyxl = { version = ">=3.1", optional = true }
|
|
22
23
|
arrow = "^1.4.0"
|
|
23
24
|
|
|
24
25
|
[tool.poetry.extras]
|
|
25
26
|
graph = ["networkx"]
|
|
26
27
|
kuzu = ["kuzu"]
|
|
28
|
+
xlsx = ["openpyxl"]
|
|
27
29
|
|
|
28
30
|
[tool.poetry.scripts]
|
|
29
31
|
sysmlpy = "sysmlpy.__main__:main"
|
|
@@ -22,6 +22,9 @@ __all__ = [
|
|
|
22
22
|
"to_interchange", "from_interchange", "interchange_to_json_text",
|
|
23
23
|
"evaluate_expression", "evaluate_calculation", "collect_values",
|
|
24
24
|
"check_constraints", "ConstraintReport", "ConstraintResult",
|
|
25
|
+
"tabular_view_to_csv", "data_value_tabular_to_csv",
|
|
26
|
+
"relationship_matrix_to_csv", "write_xlsx",
|
|
27
|
+
"import_values_csv", "import_values_xlsx", "parse_value_literal",
|
|
25
28
|
"analyze", "AnalysisResult", "SemanticIssue", "SemanticAnalyzer",
|
|
26
29
|
"SysMLSyntaxError", "PartialParseError",
|
|
27
30
|
"loads_partial", "load_partial",
|
|
@@ -30,7 +33,7 @@ __all__ = [
|
|
|
30
33
|
"render_state_transition_view_svg", "boxes_view",
|
|
31
34
|
]
|
|
32
35
|
__author__ = "Jon Fox"
|
|
33
|
-
__version__ = "0.
|
|
36
|
+
__version__ = "0.67.0"
|
|
34
37
|
|
|
35
38
|
from sysmlpy.usage import (
|
|
36
39
|
Item, Attribute, Part, Port, Action, Reference, UseCase, Requirement, Interface, Message,
|
|
@@ -57,6 +60,11 @@ from sysmlpy.evaluator import (
|
|
|
57
60
|
evaluate_expression, evaluate_calculation, collect_values,
|
|
58
61
|
check_constraints, ConstraintReport, ConstraintResult,
|
|
59
62
|
)
|
|
63
|
+
from sysmlpy.spreadsheet import (
|
|
64
|
+
tabular_view_to_csv, data_value_tabular_to_csv,
|
|
65
|
+
relationship_matrix_to_csv, write_xlsx,
|
|
66
|
+
import_values_csv, import_values_xlsx, parse_value_literal,
|
|
67
|
+
)
|
|
60
68
|
|
|
61
69
|
|
|
62
70
|
class PartialParseError(Exception):
|
|
@@ -31,7 +31,7 @@ import sysmlpy
|
|
|
31
31
|
from sysmlpy import loads
|
|
32
32
|
|
|
33
33
|
# Subcommand table, also used to detect the legacy flat invocation form.
|
|
34
|
-
SUBCOMMANDS = ("parse", "analyze", "view", "trace", "export", "import", "eval", "format", "fmt")
|
|
34
|
+
SUBCOMMANDS = ("parse", "analyze", "view", "trace", "export", "import", "eval", "xlsx", "format", "fmt")
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
# ---------------------------------------------------------------------------
|
|
@@ -433,6 +433,26 @@ def cmd_eval(args) -> int:
|
|
|
433
433
|
name, _, raw = spec.partition("=")
|
|
434
434
|
bindings[name.strip()] = _parse_eval_value(raw)
|
|
435
435
|
|
|
436
|
+
if getattr(args, "set_file", None):
|
|
437
|
+
from sysmlpy.spreadsheet import import_values_csv, import_values_xlsx
|
|
438
|
+
setfile = Path(args.set_file)
|
|
439
|
+
if not setfile.exists():
|
|
440
|
+
print(f"Error: --set-file '{setfile}' not found.", file=sys.stderr)
|
|
441
|
+
return 2
|
|
442
|
+
try:
|
|
443
|
+
if setfile.suffix.lower() == ".xlsx":
|
|
444
|
+
file_bindings = import_values_xlsx(setfile)
|
|
445
|
+
else:
|
|
446
|
+
# utf-8-sig tolerates Excel's BOM on CSV exports
|
|
447
|
+
file_bindings = import_values_csv(
|
|
448
|
+
setfile.read_text(encoding="utf-8-sig")
|
|
449
|
+
)
|
|
450
|
+
except (ValueError, ImportError, OSError) as e:
|
|
451
|
+
print(f"Error: --set-file: {e}", file=sys.stderr)
|
|
452
|
+
return 1
|
|
453
|
+
# --set flags win over spreadsheet values
|
|
454
|
+
bindings = {**file_bindings, **bindings}
|
|
455
|
+
|
|
436
456
|
if args.expr is not None:
|
|
437
457
|
try:
|
|
438
458
|
value = evaluate_expression(
|
|
@@ -478,6 +498,36 @@ def _format_value(value):
|
|
|
478
498
|
return str(value)
|
|
479
499
|
|
|
480
500
|
|
|
501
|
+
def cmd_xlsx(args) -> int:
|
|
502
|
+
"""Export tabular views to an Excel workbook (v0.66.0, Goal 7)."""
|
|
503
|
+
from sysmlpy.spreadsheet import write_xlsx
|
|
504
|
+
|
|
505
|
+
paths = [Path(f) for f in args.files]
|
|
506
|
+
for p in _missing_files(paths):
|
|
507
|
+
print(f"Error: File '{p}' not found.", file=sys.stderr)
|
|
508
|
+
return 2
|
|
509
|
+
|
|
510
|
+
try:
|
|
511
|
+
model = sysmlpy.load_files(paths, library=args.library)
|
|
512
|
+
except Exception as e:
|
|
513
|
+
print(f"Parse error: {e}", file=sys.stderr)
|
|
514
|
+
return 2
|
|
515
|
+
|
|
516
|
+
include = tuple(s.strip() for s in args.sheets.split(",") if s.strip())
|
|
517
|
+
try:
|
|
518
|
+
out = write_xlsx(model, args.output, include=include,
|
|
519
|
+
focus=args.focus)
|
|
520
|
+
except ImportError as e:
|
|
521
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
522
|
+
return 1
|
|
523
|
+
except Exception as e:
|
|
524
|
+
print(f"Error: xlsx export failed: {type(e).__name__}: {e}",
|
|
525
|
+
file=sys.stderr)
|
|
526
|
+
return 1
|
|
527
|
+
print(f"Wrote {out}")
|
|
528
|
+
return 0
|
|
529
|
+
|
|
530
|
+
|
|
481
531
|
# ---------------------------------------------------------------------------
|
|
482
532
|
# legacy flat invocation (kept for backward compatibility)
|
|
483
533
|
# ---------------------------------------------------------------------------
|
|
@@ -642,8 +692,9 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
642
692
|
help="Graph direction (graph views only)",
|
|
643
693
|
)
|
|
644
694
|
p_view.add_argument(
|
|
645
|
-
"--format", choices=("plantuml", "markdown", "html"),
|
|
646
|
-
help="Output format for tabular/datavalue/matrix views"
|
|
695
|
+
"--format", choices=("plantuml", "markdown", "html", "csv"),
|
|
696
|
+
help="Output format for tabular/datavalue/matrix views "
|
|
697
|
+
"(csv added in v0.66.0)",
|
|
647
698
|
)
|
|
648
699
|
p_view.add_argument(
|
|
649
700
|
"-l", "--library",
|
|
@@ -757,6 +808,12 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
757
808
|
help="Override a name for this evaluation (repeatable; values "
|
|
758
809
|
"may be numbers, true/false, or unit strings like '80 km/h')",
|
|
759
810
|
)
|
|
811
|
+
p_eval.add_argument(
|
|
812
|
+
"--set-file", metavar="FILE",
|
|
813
|
+
help="Load bindings from a CSV/XLSX values file "
|
|
814
|
+
"(v0.66.0; headers: Name,Value[,Unit] or "
|
|
815
|
+
"Element,Attribute,Value[,Unit])",
|
|
816
|
+
)
|
|
760
817
|
p_eval.add_argument(
|
|
761
818
|
"--element",
|
|
762
819
|
help="Qualified element name whose scope to evaluate in",
|
|
@@ -775,6 +832,35 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
775
832
|
)
|
|
776
833
|
p_eval.set_defaults(func=cmd_eval)
|
|
777
834
|
|
|
835
|
+
# -- xlsx (v0.66.0, Goal 7) -------------------------------------------------
|
|
836
|
+
p_xlsx = sub.add_parser(
|
|
837
|
+
"xlsx",
|
|
838
|
+
help="Export tabular views to an Excel workbook",
|
|
839
|
+
description="Write the Tabular / DataValues / Matrix views into "
|
|
840
|
+
"an .xlsx workbook (requires the 'openpyxl' extra: "
|
|
841
|
+
"pip install 'sysmlpy[xlsx]'). Exit codes: 0 clean, "
|
|
842
|
+
"1 error, 2 parse error.",
|
|
843
|
+
)
|
|
844
|
+
p_xlsx.add_argument("files", nargs="+", help="SysML v2 file(s)")
|
|
845
|
+
p_xlsx.add_argument(
|
|
846
|
+
"-o", "--output", required=True,
|
|
847
|
+
help="Output .xlsx file path",
|
|
848
|
+
)
|
|
849
|
+
p_xlsx.add_argument(
|
|
850
|
+
"--sheets", default="tabular,data_value,matrix",
|
|
851
|
+
help="Comma-separated sheet selection: tabular,data_value,matrix "
|
|
852
|
+
"(default: all)",
|
|
853
|
+
)
|
|
854
|
+
p_xlsx.add_argument(
|
|
855
|
+
"--focus",
|
|
856
|
+
help="Focus element for the tabular views",
|
|
857
|
+
)
|
|
858
|
+
p_xlsx.add_argument(
|
|
859
|
+
"-l", "--library",
|
|
860
|
+
help="Path to SysML v2 library files to use for parsing",
|
|
861
|
+
)
|
|
862
|
+
p_xlsx.set_defaults(func=cmd_xlsx)
|
|
863
|
+
|
|
778
864
|
return parser
|
|
779
865
|
|
|
780
866
|
|
|
@@ -6550,6 +6550,55 @@ def _make_requirement_usage_dict(ctx, prefix=None):
|
|
|
6550
6550
|
def _make_connection_usage_dict(ctx, prefix=None):
|
|
6551
6551
|
"""Create a ConnectionUsage dictionary."""
|
|
6552
6552
|
name, shortname = _get_usage_identification(ctx)
|
|
6553
|
+
|
|
6554
|
+
# Parse the connector part (``connect X to Y``) so endpoints survive
|
|
6555
|
+
# into the grammar object tree (v0.67.0 — fixes the "part": None stub)
|
|
6556
|
+
# connectionUsage: occurrenceUsagePrefix ( CONNECTION usageDeclaration?
|
|
6557
|
+
# valuePart? ( CONNECT connectorPart )? | CONNECT connectorPart ) usageBody
|
|
6558
|
+
# — the connectorPart sits directly on the connectionUsage ctx.
|
|
6559
|
+
conn_part_dict = None
|
|
6560
|
+
if ctx is not None:
|
|
6561
|
+
cp = None
|
|
6562
|
+
if hasattr(ctx, 'connectorPart') and ctx.connectorPart():
|
|
6563
|
+
cp = ctx.connectorPart()
|
|
6564
|
+
if isinstance(cp, list):
|
|
6565
|
+
cp = cp[0] if cp else None
|
|
6566
|
+
if cp:
|
|
6567
|
+
end_members = []
|
|
6568
|
+
if hasattr(cp, 'binaryConnectorPart') and cp.binaryConnectorPart():
|
|
6569
|
+
bcp = cp.binaryConnectorPart()
|
|
6570
|
+
if isinstance(bcp, list):
|
|
6571
|
+
bcp = bcp[0] if bcp else None
|
|
6572
|
+
if bcp and hasattr(bcp, 'connectorEndMember') and bcp.connectorEndMember():
|
|
6573
|
+
ems = bcp.connectorEndMember()
|
|
6574
|
+
if isinstance(ems, list):
|
|
6575
|
+
for em in ems:
|
|
6576
|
+
end = _visit_connector_end_member(em)
|
|
6577
|
+
if end:
|
|
6578
|
+
end_members.append(end)
|
|
6579
|
+
elif hasattr(cp, 'naryConnectorPart') and cp.naryConnectorPart():
|
|
6580
|
+
ncp = cp.naryConnectorPart()
|
|
6581
|
+
if isinstance(ncp, list):
|
|
6582
|
+
ncp = ncp[0] if ncp else None
|
|
6583
|
+
if ncp and hasattr(ncp, 'connectorEndMember') and ncp.connectorEndMember():
|
|
6584
|
+
ems = ncp.connectorEndMember()
|
|
6585
|
+
if isinstance(ems, list):
|
|
6586
|
+
for em in ems:
|
|
6587
|
+
end = _visit_connector_end_member(em)
|
|
6588
|
+
if end:
|
|
6589
|
+
end_members.append(end)
|
|
6590
|
+
if end_members:
|
|
6591
|
+
conn_part_dict = {
|
|
6592
|
+
"name": "ConnectorPart",
|
|
6593
|
+
"part": {
|
|
6594
|
+
"name": "BinaryConnectorPart",
|
|
6595
|
+
"ownedRelationship": end_members,
|
|
6596
|
+
},
|
|
6597
|
+
}
|
|
6598
|
+
else:
|
|
6599
|
+
conn_part_dict = None
|
|
6600
|
+
else:
|
|
6601
|
+
conn_part_dict = None
|
|
6553
6602
|
|
|
6554
6603
|
return {
|
|
6555
6604
|
"name": "PackageMember",
|
|
@@ -6575,7 +6624,7 @@ def _make_connection_usage_dict(ctx, prefix=None):
|
|
|
6575
6624
|
"specialization": None
|
|
6576
6625
|
}
|
|
6577
6626
|
},
|
|
6578
|
-
"part":
|
|
6627
|
+
"part": conn_part_dict,
|
|
6579
6628
|
"body": {
|
|
6580
6629
|
"name": "UsageBody",
|
|
6581
6630
|
"body": {
|
|
@@ -21,7 +21,10 @@ from sysmlpy.definition import Model, Package
|
|
|
21
21
|
|
|
22
22
|
# ============================================================
|
|
23
23
|
# Relationship Arrow Mappings
|
|
24
|
-
#
|
|
24
|
+
# Ground truth: the official org.omg.sysml.plantuml reference impl
|
|
25
|
+
# (SysML2PlantUMLStyle.StyleRelDefaultSwitch, B&W default style) and
|
|
26
|
+
# the OMG "Intro to the SysML v2 Language — Graphical Notation"
|
|
27
|
+
# figures (v0.67.0 — Goal 6 notation fidelity).
|
|
25
28
|
# ============================================================
|
|
26
29
|
|
|
27
30
|
ARROW_STYLES = {
|
|
@@ -30,28 +33,46 @@ ARROW_STYLES = {
|
|
|
30
33
|
"shared": "o--", # hollow diamond (isComposite=False)
|
|
31
34
|
"owning": "+--", # plus sign (owning membership)
|
|
32
35
|
"non_owning": "+..", # dotted plus (non-owning membership)
|
|
36
|
+
"variant": "+---", # variant membership (official: +---)
|
|
33
37
|
|
|
34
38
|
# Typing / Specialization
|
|
35
39
|
"typing": "--:|>", # colon + block arrow (feature typing)
|
|
36
40
|
"specialization": "--|>", # block arrow (general specialization)
|
|
37
|
-
"redefinition": "--||>", # double bar + block arrow
|
|
41
|
+
"redefinition": "--||>", # double bar + block arrow (distinct!)
|
|
38
42
|
|
|
39
43
|
# Connections
|
|
40
|
-
"binding": "-[thickness=
|
|
41
|
-
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
"binding": "-[thickness=5]-", # thick plain line: «bind»/=
|
|
45
|
+
# (both sides asserted equal)
|
|
46
|
+
"connector": "-[thickness=3]-", # thick plain line, no arrowhead
|
|
47
|
+
# (arrow only when it carries
|
|
48
|
+
# metadata — official convention)
|
|
49
|
+
"connector_directed": "-[thickness=3]->",
|
|
50
|
+
"flow": "-->", # solid thin arrow (item flow)
|
|
51
|
+
"succession": "-->", # solid thin arrow (succession)
|
|
52
|
+
"succession_flow": "..>", # dotted arrow (SuccessionFlowUsage)
|
|
53
|
+
"allocation": "-[thickness=5,dotted]->", # thick dotted arrow
|
|
45
54
|
|
|
46
55
|
# Dependencies
|
|
47
56
|
"dependency": "..>>", # dotted double-arrow
|
|
48
|
-
"satisfy": "-->", #
|
|
49
|
-
"verify": "-->", #
|
|
50
|
-
"derive": "
|
|
57
|
+
"satisfy": "-->", # solid arrow (SatisfyRequirementUsage)
|
|
58
|
+
"verify": "-->", # solid arrow
|
|
59
|
+
"derive": "..>", # dashed line, «derive»/#derive label
|
|
60
|
+
# (spec figure; official pilot omits it)
|
|
51
61
|
"refine": "..>>", # dotted double-arrow
|
|
62
|
+
"perform": "-->", # PerformActionUsage
|
|
63
|
+
"exhibit": "-->", # ExhibitStateUsage
|
|
64
|
+
"include_use_case": "-->", # IncludeUseCaseUsage
|
|
65
|
+
|
|
66
|
+
# Actions (asynchronous messages)
|
|
67
|
+
"send_action": "..>>", # send action: dotted double-arrow out
|
|
68
|
+
"accept_action": "<<..", # accept action: reversed (arrives here)
|
|
69
|
+
|
|
70
|
+
# Requirements extras
|
|
71
|
+
"objective": "-->>", # objective membership (req objectives)
|
|
52
72
|
|
|
53
73
|
# Comments / Annotations
|
|
54
74
|
"comment": "..", # dotted line, no arrowhead
|
|
75
|
+
"metadata": "..@", # metadata annotation
|
|
55
76
|
"import": "..>", # dotted arrow
|
|
56
77
|
}
|
|
57
78
|
|
|
@@ -1384,10 +1405,11 @@ def as_interconnection_diagram(model, focus=None, elements=None, style="bw",
|
|
|
1384
1405
|
"legend right",
|
|
1385
1406
|
" <b>Interconnection Legend</b>",
|
|
1386
1407
|
" Relationship: Notation",
|
|
1387
|
-
" Binding: -[thickness=
|
|
1388
|
-
"
|
|
1408
|
+
" Binding (==): -[thickness=5]-",
|
|
1409
|
+
" Connection: -[thickness=3]-",
|
|
1389
1410
|
" Flow Transfer: -->",
|
|
1390
|
-
"
|
|
1411
|
+
" Succession Flow: ..>",
|
|
1412
|
+
" Allocation: -[thickness=5,dotted]->",
|
|
1391
1413
|
" Feature Typing: --:|> ",
|
|
1392
1414
|
" Composite (owns): *--",
|
|
1393
1415
|
" | Specialization | --|> |",
|
|
@@ -1662,7 +1684,9 @@ def as_action_flow_view(model, focus=None, elements=None, style="bw", direction=
|
|
|
1662
1684
|
" <b>Action Flow Legend</b>",
|
|
1663
1685
|
" Relationship: Notation",
|
|
1664
1686
|
" Flow Transfer: -->",
|
|
1665
|
-
"
|
|
1687
|
+
" Succession Flow: ..>",
|
|
1688
|
+
" Binding (==): -[thickness=5]-",
|
|
1689
|
+
" Connection: -[thickness=3]-",
|
|
1666
1690
|
" Composite (owns): *--",
|
|
1667
1691
|
" Feature Typing: --:|> ",
|
|
1668
1692
|
"endlegend",
|
|
@@ -1805,6 +1829,79 @@ def _extract_flow_connections(model):
|
|
|
1805
1829
|
return connections
|
|
1806
1830
|
|
|
1807
1831
|
|
|
1832
|
+
def _extract_connection_endpoints(conn_element):
|
|
1833
|
+
"""Extract (from_names, to_names) from a ConnectionUsage's grammar.
|
|
1834
|
+
|
|
1835
|
+
Navigates ConnectionUsage → ConnectorPart → BinaryConnectorPart →
|
|
1836
|
+
ConnectorEndMember → ConnectorEnd → OwnedReferenceSubsetting to
|
|
1837
|
+
recover the endpoint reference name paths (v0.67.0).
|
|
1838
|
+
|
|
1839
|
+
Returns (from_names, to_names) as lists of name segments, or
|
|
1840
|
+
(None, None) when the grammar does not carry both ends.
|
|
1841
|
+
"""
|
|
1842
|
+
g = getattr(conn_element, 'grammar', None)
|
|
1843
|
+
if g is None:
|
|
1844
|
+
return None, None
|
|
1845
|
+
part = getattr(g, 'part', None)
|
|
1846
|
+
binpart = getattr(part, 'part', None) if part is not None else None
|
|
1847
|
+
if binpart is None or binpart.__class__.__name__ != 'BinaryConnectorPart':
|
|
1848
|
+
return None, None
|
|
1849
|
+
members = getattr(binpart, 'children', None) or []
|
|
1850
|
+
ends = []
|
|
1851
|
+
for member in members:
|
|
1852
|
+
names = None
|
|
1853
|
+
for end in (getattr(member, 'children', None) or []):
|
|
1854
|
+
for sub in (getattr(end, 'children', None) or []):
|
|
1855
|
+
if sub.__class__.__name__ == 'OwnedReferenceSubsetting':
|
|
1856
|
+
seg = []
|
|
1857
|
+
ref = getattr(sub, 'referencedFeature', None)
|
|
1858
|
+
if ref is not None and getattr(ref, 'names', None):
|
|
1859
|
+
seg.extend(ref.names)
|
|
1860
|
+
for chain in (getattr(sub, 'elements', None) or []):
|
|
1861
|
+
fc = getattr(chain, 'feature', None)
|
|
1862
|
+
for oc in (getattr(fc, 'children', None) or []):
|
|
1863
|
+
cf = getattr(oc, 'chainingFeature', None)
|
|
1864
|
+
if cf is not None and getattr(cf, 'names', None):
|
|
1865
|
+
seg.extend(cf.names)
|
|
1866
|
+
if seg:
|
|
1867
|
+
names = seg
|
|
1868
|
+
ends.append(names)
|
|
1869
|
+
if len(ends) >= 2 and ends[0] and ends[1]:
|
|
1870
|
+
return ends[0], ends[1]
|
|
1871
|
+
return None, None
|
|
1872
|
+
|
|
1873
|
+
|
|
1874
|
+
def _extract_connections(model):
|
|
1875
|
+
"""Scan the model for all connector usages (``connection`` elements).
|
|
1876
|
+
|
|
1877
|
+
Returns a list of ``(from_names, to_names, connection_name)``
|
|
1878
|
+
tuples. Connection edges render as a thick plain line
|
|
1879
|
+
(``-[thickness=3]-``) per the official notation (v0.67.0).
|
|
1880
|
+
"""
|
|
1881
|
+
connections = []
|
|
1882
|
+
visited = set()
|
|
1883
|
+
|
|
1884
|
+
def _scan(element):
|
|
1885
|
+
elem_id = id(element)
|
|
1886
|
+
if elem_id in visited:
|
|
1887
|
+
return
|
|
1888
|
+
visited.add(elem_id)
|
|
1889
|
+
|
|
1890
|
+
if getattr(element, 'sysml_type', '') == 'connection':
|
|
1891
|
+
from_names, to_names = _extract_connection_endpoints(element)
|
|
1892
|
+
if from_names and to_names:
|
|
1893
|
+
connections.append((from_names, to_names,
|
|
1894
|
+
getattr(element, 'name', None)))
|
|
1895
|
+
|
|
1896
|
+
for child in getattr(element, 'children', []) or []:
|
|
1897
|
+
_scan(child)
|
|
1898
|
+
|
|
1899
|
+
for child in getattr(model, 'children', []) or []:
|
|
1900
|
+
_scan(child)
|
|
1901
|
+
|
|
1902
|
+
return connections
|
|
1903
|
+
|
|
1904
|
+
|
|
1808
1905
|
def _find_state_in_children(element, state_name):
|
|
1809
1906
|
"""Find a child state element by name within a parent's children tree."""
|
|
1810
1907
|
if state_name is None:
|
|
@@ -2040,18 +2137,58 @@ def as_state_transition_view(model, focus=None, elements=None, style="bw",
|
|
|
2040
2137
|
terminal_states = []
|
|
2041
2138
|
focus_name = getattr(focus, 'name', None) if focus else None
|
|
2042
2139
|
|
|
2043
|
-
# Render state elements using PlantUML state keyword for proper state
|
|
2140
|
+
# Render state elements using PlantUML state keyword for proper state
|
|
2141
|
+
# diagram support; composite states nest their children via state blocks
|
|
2142
|
+
# (official stv notation — v0.67.0)
|
|
2143
|
+
type_by_alias = {}
|
|
2144
|
+
name_by_alias = {}
|
|
2044
2145
|
for alias, name, stereotype, elem, is_included in elements_list:
|
|
2045
|
-
|
|
2146
|
+
type_by_alias[alias] = getattr(elem, 'sysml_type', '')
|
|
2147
|
+
name_by_alias[alias] = name
|
|
2148
|
+
|
|
2149
|
+
# State containment hierarchy (composite edges between states)
|
|
2150
|
+
state_children = {}
|
|
2151
|
+
for src, arrow, dst, label, is_external in relationships:
|
|
2152
|
+
if (not is_external and arrow == ARROW_STYLES["composite"]
|
|
2153
|
+
and type_by_alias.get(src) == 'state'
|
|
2154
|
+
and type_by_alias.get(dst) == 'state'):
|
|
2155
|
+
state_children.setdefault(src, []).append(dst)
|
|
2156
|
+
|
|
2157
|
+
nested_child_aliases = {c for kids in state_children.values()
|
|
2158
|
+
for c in kids}
|
|
2159
|
+
emitted = set()
|
|
2160
|
+
|
|
2161
|
+
def _emit_state_block(alias, depth):
|
|
2162
|
+
"""Recursively emit a state and its nested child states."""
|
|
2163
|
+
emitted.add(alias)
|
|
2164
|
+
nonlocal first_state_alias
|
|
2165
|
+
name = name_by_alias.get(alias, alias)
|
|
2166
|
+
pad = " " * depth
|
|
2167
|
+
kids = state_children.get(alias, [])
|
|
2168
|
+
if kids:
|
|
2169
|
+
lines.append(f'{pad}state "{name}" as {alias} {{')
|
|
2170
|
+
for kid in kids:
|
|
2171
|
+
_emit_state_block(kid, depth + 1)
|
|
2172
|
+
lines.append(f'{pad}}}')
|
|
2173
|
+
else:
|
|
2174
|
+
lines.append(f'{pad}state "{name}" as {alias}')
|
|
2175
|
+
# Track terminal states for final marker
|
|
2176
|
+
if name in ('Stopped', 'Error', 'Final'):
|
|
2177
|
+
terminal_states.append(alias)
|
|
2178
|
+
# Skip the focus element itself (parent container); find first
|
|
2179
|
+
# child state for the initial marker
|
|
2180
|
+
if (first_state_alias is None
|
|
2181
|
+
and name not in ('Stopped', 'Error', 'Final')
|
|
2182
|
+
and name != focus_name):
|
|
2183
|
+
first_state_alias = alias
|
|
2184
|
+
|
|
2185
|
+
for alias, name, stereotype, elem, is_included in elements_list:
|
|
2186
|
+
sysml_type = type_by_alias[alias]
|
|
2046
2187
|
if sysml_type == 'state':
|
|
2188
|
+
if alias in emitted or alias in nested_child_aliases:
|
|
2189
|
+
continue
|
|
2047
2190
|
# Use state keyword for proper state diagram support (initial/final markers)
|
|
2048
|
-
|
|
2049
|
-
# Skip the focus element itself (parent container), find first child state
|
|
2050
|
-
if first_state_alias is None and name not in ('Stopped', 'Error', 'Final') and name != focus_name:
|
|
2051
|
-
first_state_alias = alias
|
|
2052
|
-
# Track terminal states for final marker
|
|
2053
|
-
if name in ('Stopped', 'Error', 'Final'):
|
|
2054
|
-
terminal_states.append(alias)
|
|
2191
|
+
_emit_state_block(alias, 0)
|
|
2055
2192
|
elif sysml_type in stv_types:
|
|
2056
2193
|
lines.append(f'rectangle "{name}" as {alias}')
|
|
2057
2194
|
else:
|
|
@@ -2064,8 +2201,11 @@ def as_state_transition_view(model, focus=None, elements=None, style="bw",
|
|
|
2064
2201
|
lines.append("[*] --> " + first_state_alias)
|
|
2065
2202
|
lines.append("")
|
|
2066
2203
|
|
|
2067
|
-
# Render containment relationships
|
|
2204
|
+
# Render non-containment relationships (state hierarchy is expressed by
|
|
2205
|
+
# nesting above, not by transition arrows — v0.67.0)
|
|
2068
2206
|
for src, arrow, dst, label, is_external in relationships:
|
|
2207
|
+
if arrow == ARROW_STYLES["composite"]:
|
|
2208
|
+
continue # containment shown as nested state blocks
|
|
2069
2209
|
if is_external and not show_external:
|
|
2070
2210
|
continue
|
|
2071
2211
|
if is_external:
|
|
@@ -2105,8 +2245,8 @@ def as_state_transition_view(model, focus=None, elements=None, style="bw",
|
|
|
2105
2245
|
lines.extend([
|
|
2106
2246
|
"legend right",
|
|
2107
2247
|
" <b>State Transition Legend</b>",
|
|
2108
|
-
" State Transition:
|
|
2109
|
-
"
|
|
2248
|
+
" State Transition: -->",
|
|
2249
|
+
" Composite states: nested state blocks",
|
|
2110
2250
|
"endlegend",
|
|
2111
2251
|
])
|
|
2112
2252
|
lines.append("")
|
|
@@ -2492,7 +2632,8 @@ def as_textual_notation(model, focus=None, style="bw", custom_style=None):
|
|
|
2492
2632
|
|
|
2493
2633
|
def as_general_view(model, focus=None, elements=None, style="bw", direction="TB",
|
|
2494
2634
|
include_legend=True, max_depth=None, show_external=False,
|
|
2495
|
-
custom_style=None, show_multiplicity=False
|
|
2635
|
+
custom_style=None, show_multiplicity=False,
|
|
2636
|
+
auto_include_connections=False):
|
|
2496
2637
|
"""Generate a General View (GV) diagram.
|
|
2497
2638
|
|
|
2498
2639
|
Corresponds to SysML v2 ``GeneralView`` (short name ``gv``).
|
|
@@ -2511,6 +2652,9 @@ def as_general_view(model, focus=None, elements=None, style="bw", direction="TB"
|
|
|
2511
2652
|
show_external: Show relationships to elements outside selection
|
|
2512
2653
|
custom_style: Optional PlantUML style lines to append
|
|
2513
2654
|
show_multiplicity: Append multiplicity like [4] to element labels
|
|
2655
|
+
auto_include_connections: Render connector edges (``-[thickness=3]-``
|
|
2656
|
+
thick plain lines, official notation) between parts whose both
|
|
2657
|
+
endpoints are already in the view (v0.67.0)
|
|
2514
2658
|
|
|
2515
2659
|
Returns:
|
|
2516
2660
|
str: PlantUML text
|
|
@@ -2754,6 +2898,30 @@ def as_general_view(model, focus=None, elements=None, style="bw", direction="TB"
|
|
|
2754
2898
|
|
|
2755
2899
|
lines.append("")
|
|
2756
2900
|
|
|
2901
|
+
# Render connector edges between parts already in the view
|
|
2902
|
+
# (official: thick plain line, no arrowhead — ConnectionUsage)
|
|
2903
|
+
if auto_include_connections:
|
|
2904
|
+
for from_names, to_names, conn_name in _extract_connections(model):
|
|
2905
|
+
from_elem = (_find_element_by_qualified_name(model, from_names)
|
|
2906
|
+
if from_names else None)
|
|
2907
|
+
to_elem = (_find_element_by_qualified_name(model, to_names)
|
|
2908
|
+
if to_names else None)
|
|
2909
|
+
# NOTE: grammar objects may be falsy (they define __len__ for
|
|
2910
|
+
# children) — membership checks must use "is not None" (v0.67.0)
|
|
2911
|
+
from_alias = (id_map.get(id(from_elem))
|
|
2912
|
+
if from_elem is not None else None)
|
|
2913
|
+
to_alias = (id_map.get(id(to_elem))
|
|
2914
|
+
if to_elem is not None else None)
|
|
2915
|
+
if from_alias and to_alias:
|
|
2916
|
+
if conn_name:
|
|
2917
|
+
lines.append(
|
|
2918
|
+
f'{from_alias} {ARROW_STYLES["connector"]} '
|
|
2919
|
+
f'{to_alias} : {conn_name}')
|
|
2920
|
+
else:
|
|
2921
|
+
lines.append(
|
|
2922
|
+
f'{from_alias} {ARROW_STYLES["connector"]} {to_alias}')
|
|
2923
|
+
lines.append("")
|
|
2924
|
+
|
|
2757
2925
|
if include_legend:
|
|
2758
2926
|
lines.extend([
|
|
2759
2927
|
"legend right",
|
|
@@ -2765,11 +2933,16 @@ def as_general_view(model, focus=None, elements=None, style="bw", direction="TB"
|
|
|
2765
2933
|
" Feature Typing: --:|> ",
|
|
2766
2934
|
" | Specialization | --|> |",
|
|
2767
2935
|
" | Redefinition | --||> |",
|
|
2768
|
-
" Binding: -[thickness=
|
|
2769
|
-
"
|
|
2936
|
+
" Binding (==): -[thickness=5]-",
|
|
2937
|
+
" Connection: -[thickness=3]-",
|
|
2770
2938
|
" Flow Transfer: -->",
|
|
2771
|
-
"
|
|
2939
|
+
" Succession Flow: ..>",
|
|
2940
|
+
" Allocation: -[thickness=5,dotted]->",
|
|
2772
2941
|
" Dependency: ..>>",
|
|
2942
|
+
" Send Action: ..>>",
|
|
2943
|
+
" Accept Action: <<..",
|
|
2944
|
+
" Variant: +---",
|
|
2945
|
+
" Metadata: ..@",
|
|
2773
2946
|
"endlegend",
|
|
2774
2947
|
])
|
|
2775
2948
|
lines.append("")
|
|
@@ -3139,6 +3312,22 @@ def _format_table_rows_plantuml(header, rows):
|
|
|
3139
3312
|
return lines
|
|
3140
3313
|
|
|
3141
3314
|
|
|
3315
|
+
def _format_table_rows_csv(header, rows):
|
|
3316
|
+
"""Format header + rows as CSV text (v0.66.0, Goal 7).
|
|
3317
|
+
|
|
3318
|
+
Uses the ``csv`` module for proper quoting/escaping. Returns a
|
|
3319
|
+
trailing-newline-free string with LF line endings.
|
|
3320
|
+
"""
|
|
3321
|
+
import csv as _csv
|
|
3322
|
+
import io as _io
|
|
3323
|
+
|
|
3324
|
+
buf = _io.StringIO()
|
|
3325
|
+
writer = _csv.writer(buf, lineterminator="\n")
|
|
3326
|
+
writer.writerow(header)
|
|
3327
|
+
writer.writerows(rows)
|
|
3328
|
+
return buf.getvalue().rstrip("\n")
|
|
3329
|
+
|
|
3330
|
+
|
|
3142
3331
|
def _format_table_rows_markdown(header, rows, align=None):
|
|
3143
3332
|
"""Format a markdown table from header and rows."""
|
|
3144
3333
|
if align is None:
|
|
@@ -3250,6 +3439,9 @@ def as_tabular_view(model, focus=None, style="bw", output_format="markdown",
|
|
|
3250
3439
|
row.append("")
|
|
3251
3440
|
rows.append(row)
|
|
3252
3441
|
|
|
3442
|
+
if output_format == "csv":
|
|
3443
|
+
return _format_table_rows_csv(header, rows)
|
|
3444
|
+
|
|
3253
3445
|
if output_format == "markdown":
|
|
3254
3446
|
align = []
|
|
3255
3447
|
for col in columns:
|
|
@@ -3349,6 +3541,9 @@ def as_data_value_tabular_view(model, focus=None, style="bw",
|
|
|
3349
3541
|
row = [parent_name, name, val_str, unit_str, typed_by]
|
|
3350
3542
|
rows.append(row)
|
|
3351
3543
|
|
|
3544
|
+
if output_format == "csv":
|
|
3545
|
+
return _format_table_rows_csv(header, rows)
|
|
3546
|
+
|
|
3352
3547
|
if output_format == "markdown":
|
|
3353
3548
|
align = [":---", ":---", ":--", ":--", ":---"]
|
|
3354
3549
|
parts = _format_table_rows_markdown(header, rows, align=align)
|
|
@@ -3516,6 +3711,9 @@ def as_relationship_matrix_view(model, focus=None, style="bw",
|
|
|
3516
3711
|
row.append("")
|
|
3517
3712
|
matrix_rows.append(row)
|
|
3518
3713
|
|
|
3714
|
+
if output_format == "csv":
|
|
3715
|
+
return _format_table_rows_csv(header, matrix_rows)
|
|
3716
|
+
|
|
3519
3717
|
if output_format == "markdown":
|
|
3520
3718
|
parts = _format_table_rows_markdown(header, matrix_rows, align=None)
|
|
3521
3719
|
if custom_style:
|