sysmlpy 0.96.2__tar.gz → 0.96.3__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.96.2 → sysmlpy-0.96.3}/PKG-INFO +65 -1
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/README.md +64 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/pyproject.toml +1 -1
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/__init__.py +3 -1
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/__main__.py +29 -1
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/interchange.py +291 -9
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/LICENSE +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/antlr/README.md +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/antlr/SysMLv2Lexer.interp +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/antlr/SysMLv2Lexer.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/antlr/SysMLv2Lexer.tokens +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/antlr/SysMLv2Parser.interp +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/antlr/SysMLv2Parser.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/antlr/SysMLv2Parser.tokens +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/antlr/SysMLv2ParserListener.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/antlr/SysMLv2ParserVisitor.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.interp +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.tokens +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/antlr_parser.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/antlr_visitor.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/boxes_view.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/definition.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/dfa_cache.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/diff.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/evaluator.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/examples/attribute_values.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/examples/part_attributes.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/examples/tuples_sequences.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/formatting.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/grammar/antlr4/README.md +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.g4 +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.interp +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.tokens +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/grammar/antlr4/SysMLv2Parser.g4 +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/grammar/antlr4/SysMLv2Parser.interp +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/grammar/antlr4/SysMLv2Parser.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/grammar/antlr4/SysMLv2Parser.tokens +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/grammar/antlr4/SysMLv2ParserListener.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/grammar/antlr4/SysMLv2ParserVisitor.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/grammar/antlr4/desc.xml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/grammar/antlr4/examples/camera.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/grammar/antlr4/examples/toaster-system.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/grammar/antlr4/examples/vehicle-model.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/grammar/antlr4/pom.xml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/grammar/classes.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/ipython_magic.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/kerml/KerMLLexer.g4 +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/kerml/KerMLLexer.interp +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/kerml/KerMLLexer.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/kerml/KerMLLexer.tokens +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/kerml/KerMLParser.g4 +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/kerml/KerMLParser.interp +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/kerml/KerMLParser.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/kerml/KerMLParser.tokens +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/kerml/KerMLParserVisitor.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/kerml/README.md +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/kerml/__init__.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/kerml/kerml.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/kerml/kerml_visitor.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/LICENSE +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/README.md +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/__init__.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Analysis/AnalysisTooling.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Analysis/SampledFunctions.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Analysis/StateSpaceRepresentation.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Analysis/TradeStudies.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Cause and Effect/CausationConnections.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Cause and Effect/CauseAndEffect.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Geometry/ShapeItems.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Geometry/SpatialItems.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Metadata/ImageMetadata.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Metadata/ModelingMetadata.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Metadata/ParametersOfInterestMetadata.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Metadata/RiskMetadata.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Quantities and Units/ISQ.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Quantities and Units/ISQAcoustics.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Quantities and Units/ISQAtomicNuclear.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Quantities and Units/ISQBase.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Quantities and Units/ISQCharacteristicNumbers.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Quantities and Units/ISQChemistryMolecular.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Quantities and Units/ISQCondensedMatter.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Quantities and Units/ISQElectromagnetism.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Quantities and Units/ISQInformation.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Quantities and Units/ISQLight.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Quantities and Units/ISQMechanics.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Quantities and Units/ISQSpaceTime.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Quantities and Units/ISQThermodynamics.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Quantities and Units/MeasurementRefCalculations.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Quantities and Units/MeasurementReferences.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Quantities and Units/Quantities.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Quantities and Units/QuantityCalculations.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Quantities and Units/SI.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Quantities and Units/SIPrefixes.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Quantities and Units/TensorCalculations.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Quantities and Units/Time.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Quantities and Units/USCustomaryUnits.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Quantities and Units/VectorCalculations.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Requirement Derivation/DerivationConnections.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/domain/Requirement Derivation/RequirementDerivation.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/Base.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/BaseFunctions.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/BooleanFunctions.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/Clocks.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/CollectionFunctions.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/Collections.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/ComplexFunctions.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/ControlFunctions.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/ControlPerformances.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/DataFunctions.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/FeatureReferencingPerformances.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/IntegerFunctions.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/KerML.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/Links.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/Metaobjects.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/NaturalFunctions.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/NumericalFunctions.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/Objects.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/Observation.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/OccurrenceFunctions.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/Occurrences.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/Performances.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/RationalFunctions.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/RealFunctions.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/ScalarFunctions.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/ScalarValues.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/SequenceFunctions.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/SpatialFrames.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/StatePerformances.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/StringFunctions.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/Transfers.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/TransitionPerformances.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/TrigFunctions.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/Triggers.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/VectorFunctions.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/kernel/VectorValues.kerml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/systems/Actions.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/systems/Allocations.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/systems/AnalysisCases.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/systems/Attributes.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/systems/Calculations.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/systems/Cases.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/systems/Connections.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/systems/Constraints.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/systems/Flows.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/systems/Interfaces.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/systems/Items.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/systems/Metadata.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/systems/Parts.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/systems/Ports.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/systems/Requirements.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/systems/StandardViewDefinitions.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/systems/States.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/systems/SysML.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/systems/UseCases.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/systems/VerificationCases.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/library/systems/Views.sysml +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/lsp/__init__.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/lsp/__main__.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/lsp/protocol.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/lsp/server.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/lsp/stdio.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/mdtables.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/navigate.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/plantuml.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/project.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/repl.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/reqif_io.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/semantic.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/sim.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/spreadsheet.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/store.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/traceability.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/us_customary_units.txt +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/usage.py +0 -0
- {sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/validator.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sysmlpy
|
|
3
|
-
Version: 0.96.
|
|
3
|
+
Version: 0.96.3
|
|
4
4
|
Summary:
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Author: Jon R. Fox (mycr0ft)
|
|
@@ -987,6 +987,70 @@ See [`docs/plantuml-examples/`](docs/plantuml-examples/) for all rendered exampl
|
|
|
987
987
|
| 15 | Relationship Matrix (GridView) | Relationship Matrix |
|
|
988
988
|
| 16 | Tabular View — Color | Tabular View (color) |
|
|
989
989
|
|
|
990
|
+
## Element Identity: Stable Interchange IDs
|
|
991
|
+
|
|
992
|
+
Element `@id`s in interchange exports follow one of two schemes, and a
|
|
993
|
+
reconciliation mechanism that follows the model across edits:
|
|
994
|
+
|
|
995
|
+
**Default — position-derived (deterministic per source text).** Every
|
|
996
|
+
`@graph` element gets `uuid5` from its tree position (parent id + key +
|
|
997
|
+
index). Same source text ⇒ byte-identical JSON in any process.
|
|
998
|
+
|
|
999
|
+
```python
|
|
1000
|
+
from sysmlpy import to_interchange
|
|
1001
|
+
document = to_interchange(model) # position-derived @ids
|
|
1002
|
+
```
|
|
1003
|
+
|
|
1004
|
+
**`stable_ids=True` — content-addressed (L2 idempotency).** Named
|
|
1005
|
+
elements hash their declared qualified-name path + metaclass type;
|
|
1006
|
+
unnamed elements hash the nearest named anchor plus the structural path
|
|
1007
|
+
from it. Inserting or reordering unrelated elements elsewhere leaves
|
|
1008
|
+
untouched ids unchanged:
|
|
1009
|
+
|
|
1010
|
+
```python
|
|
1011
|
+
document = to_interchange(model, stable_ids=True)
|
|
1012
|
+
```
|
|
1013
|
+
|
|
1014
|
+
```bash
|
|
1015
|
+
sysmlpy export model.sysml --stable-ids
|
|
1016
|
+
```
|
|
1017
|
+
|
|
1018
|
+
**`reconcile_ids` — a registry sidecar (L3 identity).** Content ids
|
|
1019
|
+
still re-mint when an *ancestor is renamed* (the qualified-name path
|
|
1020
|
+
changes) or an element moves to a different parent. To follow a model
|
|
1021
|
+
across its lifetime, keep the previous export as a **registry file**
|
|
1022
|
+
and reconcile each new export against it: every element whose
|
|
1023
|
+
(declared name, type) matches exactly one registry entry adopts the
|
|
1024
|
+
registry's id. Matching is injective — ambiguous (duplicated) keys
|
|
1025
|
+
match nothing and keep derived ids, so the document stays unique.
|
|
1026
|
+
|
|
1027
|
+
```python
|
|
1028
|
+
from pathlib import Path
|
|
1029
|
+
from sysmlpy import loads, to_interchange, interchange_to_json_text
|
|
1030
|
+
|
|
1031
|
+
registry_path = Path("model.json")
|
|
1032
|
+
document = (to_interchange(model, stable_ids=True)
|
|
1033
|
+
if not registry_path.exists() else
|
|
1034
|
+
to_interchange(model, reconcile_with=registry_path))
|
|
1035
|
+
registry_path.write_text(interchange_to_json_text(document))
|
|
1036
|
+
```
|
|
1037
|
+
|
|
1038
|
+
Or as a one-liner on the CLI — the registry file is typically also the
|
|
1039
|
+
output file, making every export a fixed point after the first:
|
|
1040
|
+
|
|
1041
|
+
```bash
|
|
1042
|
+
sysmlpy export model.sysml --reconcile-with model.json -o model.json
|
|
1043
|
+
```
|
|
1044
|
+
|
|
1045
|
+
Round-trip guarantee: `from_interchange` on a reconciled document
|
|
1046
|
+
rebuilds the live model; re-exporting it *with the reconciled document
|
|
1047
|
+
as its registry* reproduces the identical `@graph`. Elements renamed
|
|
1048
|
+
so thoroughly that no name matches fall back to fresh derived ids
|
|
1049
|
+
(becoming new registry entries on the next write). See
|
|
1050
|
+
`docs/stable-identities.md` for the full analysis and
|
|
1051
|
+
`examples/element_identity.py` for a runnable walkthrough of all
|
|
1052
|
+
identity layers.
|
|
1053
|
+
|
|
990
1054
|
## ReqIF Interchange (Requirements Interchange Format)
|
|
991
1055
|
|
|
992
1056
|
sysmlpy imports and exports **ReqIF 1.0** — the OMG requirements interchange
|
|
@@ -951,6 +951,70 @@ See [`docs/plantuml-examples/`](docs/plantuml-examples/) for all rendered exampl
|
|
|
951
951
|
| 15 | Relationship Matrix (GridView) | Relationship Matrix |
|
|
952
952
|
| 16 | Tabular View — Color | Tabular View (color) |
|
|
953
953
|
|
|
954
|
+
## Element Identity: Stable Interchange IDs
|
|
955
|
+
|
|
956
|
+
Element `@id`s in interchange exports follow one of two schemes, and a
|
|
957
|
+
reconciliation mechanism that follows the model across edits:
|
|
958
|
+
|
|
959
|
+
**Default — position-derived (deterministic per source text).** Every
|
|
960
|
+
`@graph` element gets `uuid5` from its tree position (parent id + key +
|
|
961
|
+
index). Same source text ⇒ byte-identical JSON in any process.
|
|
962
|
+
|
|
963
|
+
```python
|
|
964
|
+
from sysmlpy import to_interchange
|
|
965
|
+
document = to_interchange(model) # position-derived @ids
|
|
966
|
+
```
|
|
967
|
+
|
|
968
|
+
**`stable_ids=True` — content-addressed (L2 idempotency).** Named
|
|
969
|
+
elements hash their declared qualified-name path + metaclass type;
|
|
970
|
+
unnamed elements hash the nearest named anchor plus the structural path
|
|
971
|
+
from it. Inserting or reordering unrelated elements elsewhere leaves
|
|
972
|
+
untouched ids unchanged:
|
|
973
|
+
|
|
974
|
+
```python
|
|
975
|
+
document = to_interchange(model, stable_ids=True)
|
|
976
|
+
```
|
|
977
|
+
|
|
978
|
+
```bash
|
|
979
|
+
sysmlpy export model.sysml --stable-ids
|
|
980
|
+
```
|
|
981
|
+
|
|
982
|
+
**`reconcile_ids` — a registry sidecar (L3 identity).** Content ids
|
|
983
|
+
still re-mint when an *ancestor is renamed* (the qualified-name path
|
|
984
|
+
changes) or an element moves to a different parent. To follow a model
|
|
985
|
+
across its lifetime, keep the previous export as a **registry file**
|
|
986
|
+
and reconcile each new export against it: every element whose
|
|
987
|
+
(declared name, type) matches exactly one registry entry adopts the
|
|
988
|
+
registry's id. Matching is injective — ambiguous (duplicated) keys
|
|
989
|
+
match nothing and keep derived ids, so the document stays unique.
|
|
990
|
+
|
|
991
|
+
```python
|
|
992
|
+
from pathlib import Path
|
|
993
|
+
from sysmlpy import loads, to_interchange, interchange_to_json_text
|
|
994
|
+
|
|
995
|
+
registry_path = Path("model.json")
|
|
996
|
+
document = (to_interchange(model, stable_ids=True)
|
|
997
|
+
if not registry_path.exists() else
|
|
998
|
+
to_interchange(model, reconcile_with=registry_path))
|
|
999
|
+
registry_path.write_text(interchange_to_json_text(document))
|
|
1000
|
+
```
|
|
1001
|
+
|
|
1002
|
+
Or as a one-liner on the CLI — the registry file is typically also the
|
|
1003
|
+
output file, making every export a fixed point after the first:
|
|
1004
|
+
|
|
1005
|
+
```bash
|
|
1006
|
+
sysmlpy export model.sysml --reconcile-with model.json -o model.json
|
|
1007
|
+
```
|
|
1008
|
+
|
|
1009
|
+
Round-trip guarantee: `from_interchange` on a reconciled document
|
|
1010
|
+
rebuilds the live model; re-exporting it *with the reconciled document
|
|
1011
|
+
as its registry* reproduces the identical `@graph`. Elements renamed
|
|
1012
|
+
so thoroughly that no name matches fall back to fresh derived ids
|
|
1013
|
+
(becoming new registry entries on the next write). See
|
|
1014
|
+
`docs/stable-identities.md` for the full analysis and
|
|
1015
|
+
`examples/element_identity.py` for a runnable walkthrough of all
|
|
1016
|
+
identity layers.
|
|
1017
|
+
|
|
954
1018
|
## ReqIF Interchange (Requirements Interchange Format)
|
|
955
1019
|
|
|
956
1020
|
sysmlpy imports and exports **ReqIF 1.0** — the OMG requirements interchange
|
|
@@ -20,6 +20,7 @@ __all__ = [
|
|
|
20
20
|
"as_sequence_view", "as_case_view", "as_browser_view",
|
|
21
21
|
"extract_traceability", "TraceabilityReport", "RequirementTrace",
|
|
22
22
|
"to_interchange", "from_interchange", "interchange_to_json_text",
|
|
23
|
+
"reconcile_ids",
|
|
23
24
|
"reqif_import", "reqif_import_model", "reqif_export",
|
|
24
25
|
"evaluate_expression", "evaluate_calculation", "collect_values",
|
|
25
26
|
"check_constraints", "ConstraintReport", "ConstraintResult",
|
|
@@ -35,7 +36,7 @@ __all__ = [
|
|
|
35
36
|
"render_state_transition_view_svg", "boxes_view",
|
|
36
37
|
]
|
|
37
38
|
__author__ = "Jon Fox"
|
|
38
|
-
__version__ = "0.96.
|
|
39
|
+
__version__ = "0.96.3"
|
|
39
40
|
|
|
40
41
|
from sysmlpy.usage import (
|
|
41
42
|
Item, Attribute, Part, Port, Action, Reference, UseCase, Requirement, Interface, Message,
|
|
@@ -57,6 +58,7 @@ from sysmlpy.traceability import (
|
|
|
57
58
|
)
|
|
58
59
|
from sysmlpy.interchange import (
|
|
59
60
|
to_interchange, from_interchange, interchange_to_json_text,
|
|
61
|
+
reconcile_ids,
|
|
60
62
|
)
|
|
61
63
|
from sysmlpy.reqif_io import (
|
|
62
64
|
reqif_import, reqif_import_model, reqif_export,
|
|
@@ -393,8 +393,22 @@ def cmd_export(args) -> int:
|
|
|
393
393
|
print(f"Parse error: {e}", file=sys.stderr)
|
|
394
394
|
return 2
|
|
395
395
|
|
|
396
|
+
# --reconcile-with <registry>: adopt ids from the previous export
|
|
397
|
+
# (the registry file itself is typically the output path — ids stay
|
|
398
|
+
# stable from then on, even across edits that re-mint derived ids).
|
|
399
|
+
reconcile_with = None
|
|
400
|
+
if getattr(args, "reconcile_with", None):
|
|
401
|
+
reg_path = Path(args.reconcile_with)
|
|
402
|
+
if reg_path.exists():
|
|
403
|
+
reconcile_with = reg_path
|
|
404
|
+
else:
|
|
405
|
+
print(f"Registry file '{reg_path}' not found — minting fresh "
|
|
406
|
+
"stable ids (delete the flag or create the file to "
|
|
407
|
+
"reconcile).", file=sys.stderr)
|
|
408
|
+
|
|
396
409
|
try:
|
|
397
|
-
document = to_interchange(model
|
|
410
|
+
document = to_interchange(model, stable_ids=args.stable_ids,
|
|
411
|
+
reconcile_with=reconcile_with)
|
|
398
412
|
except Exception as e:
|
|
399
413
|
print(f"Error: export failed on this model: {e}", file=sys.stderr)
|
|
400
414
|
return 1
|
|
@@ -967,6 +981,20 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
967
981
|
"--compact", action="store_true",
|
|
968
982
|
help="Emit compact JSON (no indentation)",
|
|
969
983
|
)
|
|
984
|
+
p_export.add_argument(
|
|
985
|
+
"--stable-ids", action="store_true",
|
|
986
|
+
help="Content-address element @ids (L2 idempotency): ids follow "
|
|
987
|
+
"declared qualified-name paths, so inserting/reordering "
|
|
988
|
+
"unrelated elements elsewhere leaves untouched ids unchanged",
|
|
989
|
+
)
|
|
990
|
+
p_export.add_argument(
|
|
991
|
+
"--reconcile-with", metavar="REGISTRY",
|
|
992
|
+
help="Adopt element @ids from a previous export document (implies "
|
|
993
|
+
"--stable-ids); ids of name-matched elements survive edits "
|
|
994
|
+
"(L3 identity): re-export repeatedly with "
|
|
995
|
+
"'--reconcile-with model.json -o model.json' to keep ids "
|
|
996
|
+
"stable across the model's lifetime",
|
|
997
|
+
)
|
|
970
998
|
p_export.add_argument(
|
|
971
999
|
"-l", "--library",
|
|
972
1000
|
help="Path to SysML v2 library files to use for parsing",
|
|
@@ -63,14 +63,18 @@ from __future__ import annotations
|
|
|
63
63
|
|
|
64
64
|
import json as _json
|
|
65
65
|
import uuid as _uuid
|
|
66
|
+
from collections import Counter as _Counter
|
|
67
|
+
from pathlib import Path as _Path
|
|
66
68
|
|
|
67
69
|
__all__ = [
|
|
68
70
|
"INTERCHANGE_CONTEXT",
|
|
69
71
|
"INTERCHANGE_VOCABULARY",
|
|
70
72
|
"INTERCHANGE_NAMESPACE",
|
|
73
|
+
"STABLE_NAMESPACE",
|
|
71
74
|
"to_interchange",
|
|
72
75
|
"from_interchange",
|
|
73
76
|
"interchange_to_json_text",
|
|
77
|
+
"reconcile_ids",
|
|
74
78
|
"build_jsonld_context",
|
|
75
79
|
]
|
|
76
80
|
|
|
@@ -108,33 +112,157 @@ def _child_id(parent_id: str, key: str, index: int) -> str:
|
|
|
108
112
|
)
|
|
109
113
|
|
|
110
114
|
|
|
115
|
+
# ---------------------------------------------------------------------------
|
|
116
|
+
# stable (content-addressed) identifiers — L2 idempotency
|
|
117
|
+
# ---------------------------------------------------------------------------
|
|
118
|
+
|
|
119
|
+
#: UUID namespace for content-addressed ``stable_ids=True`` identifiers
|
|
120
|
+
#: (distinct from :data:`INTERCHANGE_NAMESPACE` so the two schemes never
|
|
121
|
+
#: collide even in theory).
|
|
122
|
+
STABLE_NAMESPACE = _uuid.uuid5(
|
|
123
|
+
_uuid.NAMESPACE_URL,
|
|
124
|
+
"https://github.com/mycr0ft/sysmlpy/interchange/stable-ids",
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _identification_of(node: dict):
|
|
129
|
+
"""The ``Identification`` node declared *by* this element, if any.
|
|
130
|
+
|
|
131
|
+
The parser models names as ``Identification`` dicts nested under
|
|
132
|
+
``declaration``/``definition`` nodes; usages nest theirs under
|
|
133
|
+
``declaration.declaration.identification``. A recursive search
|
|
134
|
+
finds any shape without hard-coding the path — but it must not
|
|
135
|
+
descend through the containment edges (``ownedRelatedElement``,
|
|
136
|
+
``body``): a wrapper (``PackageMember``, ``DefinitionElement``…)
|
|
137
|
+
merely *contains* named elements and must not claim their names.
|
|
138
|
+
Descending into them would let the root claim every name in the
|
|
139
|
+
model, corrupting every identity path.
|
|
140
|
+
"""
|
|
141
|
+
if not isinstance(node, dict):
|
|
142
|
+
return None
|
|
143
|
+
if node.get("name") == "Identification":
|
|
144
|
+
return node
|
|
145
|
+
for k, v in node.items():
|
|
146
|
+
if k in ("ownedRelatedElement", "body"):
|
|
147
|
+
continue # containment edges: names below belong to children
|
|
148
|
+
if isinstance(v, list):
|
|
149
|
+
for item in v:
|
|
150
|
+
found = _identification_of(item)
|
|
151
|
+
if found is not None:
|
|
152
|
+
return found
|
|
153
|
+
elif isinstance(v, dict):
|
|
154
|
+
found = _identification_of(v)
|
|
155
|
+
if found is not None:
|
|
156
|
+
return found
|
|
157
|
+
return None
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class _StableIdAssigner:
|
|
161
|
+
"""Content-addressed ``@id`` assignment over the visitor-dict tree.
|
|
162
|
+
|
|
163
|
+
An element's id hashes its *identity path*: the chain of declared
|
|
164
|
+
names from the root down to the nearest declared ancestor, plus the
|
|
165
|
+
element's own declared name (if any) and ``@type``. Concretely:
|
|
166
|
+
|
|
167
|
+
- Named element: ``uuid5(STABLE_NAMESPACE, qn + "|" + type)``
|
|
168
|
+
where ``qn`` is the dot-joined declared names of the element and
|
|
169
|
+
all its declared ancestors (e.g. ``P.Engine.power``).
|
|
170
|
+
- Unnamed element below a declared ancestor: hashes the anchor QN,
|
|
171
|
+
the structural path from that ancestor (dictionary keys +
|
|
172
|
+
sibling indexes), and the ``@type`` — so it moves only if the
|
|
173
|
+
structure *between* it and its named anchor changes, never when
|
|
174
|
+
unrelated siblings elsewhere shift indexes.
|
|
175
|
+
- Root: hashes the tree path under the fixed ``"root"`` anchor.
|
|
176
|
+
|
|
177
|
+
Same-name sibling collisions (two ``part def Engine`` under one
|
|
178
|
+
parent) share a base hash; the occurrence index among content-key
|
|
179
|
+
siblings is appended so ids stay unique. Elements in a collision
|
|
180
|
+
group shift when a group sibling is inserted before them — the
|
|
181
|
+
documented degradation for L2 (see docs/stable-identities.md).
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
def __init__(self):
|
|
185
|
+
#: base content-key -> next occurrence index
|
|
186
|
+
self._occurrence: dict = {}
|
|
187
|
+
#: node id() -> assigned @id (each dict node is visited once)
|
|
188
|
+
self._assigned: dict = {}
|
|
189
|
+
|
|
190
|
+
def id_for(self, node: dict, qn_path: tuple, struct_path: tuple) -> str:
|
|
191
|
+
ident = _identification_of(node) if isinstance(node, dict) else None
|
|
192
|
+
own_name = (ident or {}).get("declaredName")
|
|
193
|
+
type_name = node.get("name") if isinstance(node, dict) else None
|
|
194
|
+
if own_name:
|
|
195
|
+
qn = "|".join(qn_path + (own_name,))
|
|
196
|
+
key = f"{qn}|{type_name}"
|
|
197
|
+
else:
|
|
198
|
+
key = "root" if not qn_path and not struct_path else (
|
|
199
|
+
"|".join(qn_path) + "||" + _struct_text(struct_path)
|
|
200
|
+
+ f"|{type_name}"
|
|
201
|
+
)
|
|
202
|
+
n = self._occurrence.get(key, 0)
|
|
203
|
+
self._occurrence[key] = n + 1
|
|
204
|
+
if n:
|
|
205
|
+
key = f"{key}|#{n}"
|
|
206
|
+
return "sysml:" + str(_uuid.uuid5(STABLE_NAMESPACE, key))
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _struct_text(struct_path: tuple) -> str:
|
|
210
|
+
return ">".join(f"{key}:{i}" for key, i in struct_path)
|
|
211
|
+
|
|
212
|
+
|
|
111
213
|
def _flatten(node: dict, parent_id: str, key: str, index: int,
|
|
112
|
-
graph: list
|
|
214
|
+
graph: list, assigner: "_StableIdAssigner | None" = None,
|
|
215
|
+
parent_qn: tuple = (), parent_struct: tuple = ()) -> str:
|
|
113
216
|
"""Recursively flatten a visitor-dict node into the graph.
|
|
114
217
|
|
|
218
|
+
With ``assigner=None`` (default) ids are position-derived
|
|
219
|
+
(``_child_id``). With an :class:`_StableIdAssigner`, ids are
|
|
220
|
+
content-addressed: named elements hash their qualified-name path,
|
|
221
|
+
unnamed elements hash the nearest named anchor plus the structural
|
|
222
|
+
path from it — see :class:`_StableIdAssigner`.
|
|
223
|
+
|
|
115
224
|
Returns the ``@id`` assigned to this node.
|
|
116
225
|
"""
|
|
117
|
-
|
|
226
|
+
if assigner is not None:
|
|
227
|
+
struct_path = parent_struct + ((key, index),)
|
|
228
|
+
elem_id = assigner.id_for(node, parent_qn, struct_path)
|
|
229
|
+
ident = _identification_of(node)
|
|
230
|
+
own_name = (ident or {}).get("declaredName")
|
|
231
|
+
child_qn = parent_qn + ((own_name,) if own_name else ())
|
|
232
|
+
child_struct = struct_path
|
|
233
|
+
else:
|
|
234
|
+
elem_id = _child_id(parent_id, key, index)
|
|
235
|
+
child_qn = ()
|
|
236
|
+
child_struct = ()
|
|
118
237
|
elem = {"@id": elem_id, "@type": node.get("name")}
|
|
119
238
|
for k, v in node.items():
|
|
120
239
|
if k == "name":
|
|
121
240
|
continue # captured as @type
|
|
122
|
-
elem[k] = _flatten_value(v, elem_id, k, graph
|
|
241
|
+
elem[k] = _flatten_value(v, elem_id, k, graph,
|
|
242
|
+
assigner=assigner, parent_qn=child_qn,
|
|
243
|
+
parent_struct=child_struct)
|
|
123
244
|
graph.append(elem)
|
|
124
245
|
return elem_id
|
|
125
246
|
|
|
126
247
|
|
|
127
|
-
def _flatten_value(v, elem_id: str, key: str, graph: list
|
|
248
|
+
def _flatten_value(v, elem_id: str, key: str, graph: list,
|
|
249
|
+
assigner: "_StableIdAssigner | None" = None,
|
|
250
|
+
parent_qn: tuple = (), parent_struct: tuple = ()):
|
|
128
251
|
"""Flatten one property value: dicts become @id refs, scalars inline."""
|
|
129
252
|
if v is None or isinstance(v, (str, int, float, bool)):
|
|
130
253
|
return v
|
|
131
254
|
if isinstance(v, dict):
|
|
132
|
-
return {"@id": _flatten(v, elem_id, key, 0, graph
|
|
255
|
+
return {"@id": _flatten(v, elem_id, key, 0, graph,
|
|
256
|
+
assigner=assigner, parent_qn=parent_qn,
|
|
257
|
+
parent_struct=parent_struct)}
|
|
133
258
|
if isinstance(v, list):
|
|
134
259
|
out = []
|
|
135
260
|
for i, item in enumerate(v):
|
|
136
261
|
if isinstance(item, dict):
|
|
137
|
-
out.append({"@id": _flatten(item, elem_id, key, i, graph
|
|
262
|
+
out.append({"@id": _flatten(item, elem_id, key, i, graph,
|
|
263
|
+
assigner=assigner,
|
|
264
|
+
parent_qn=parent_qn,
|
|
265
|
+
parent_struct=parent_struct)})
|
|
138
266
|
else:
|
|
139
267
|
out.append(item)
|
|
140
268
|
return out
|
|
@@ -230,7 +358,8 @@ def _extract_properties(source):
|
|
|
230
358
|
return props, types
|
|
231
359
|
|
|
232
360
|
|
|
233
|
-
def to_interchange(source, *, vocabulary=None, explicit_terms=False
|
|
361
|
+
def to_interchange(source, *, vocabulary=None, explicit_terms=False,
|
|
362
|
+
stable_ids=False, reconcile_with=None):
|
|
234
363
|
"""Export a model to the SysML v2 JSON interchange representation.
|
|
235
364
|
|
|
236
365
|
Parameters
|
|
@@ -246,6 +375,30 @@ def to_interchange(source, *, vocabulary=None, explicit_terms=False):
|
|
|
246
375
|
``property → <vocabulary>#property`` term definitions for
|
|
247
376
|
every property and metaclass in the document (see
|
|
248
377
|
:func:`build_jsonld_context`).
|
|
378
|
+
stable_ids : bool
|
|
379
|
+
When true, ``@id``s are **content-addressed** (L2 idempotency):
|
|
380
|
+
named elements hash their qualified-name path and ``@type``,
|
|
381
|
+
unnamed elements hash the nearest named ancestor plus the
|
|
382
|
+
structural path from it. Inserting or reordering unrelated
|
|
383
|
+
siblings elsewhere in the document then leaves every
|
|
384
|
+
untouched element's id unchanged — with the default
|
|
385
|
+
position-derived ids those ids all shift. Same-name sibling
|
|
386
|
+
groups (two ``part def Engine`` under one parent) are
|
|
387
|
+
disambiguated by occurrence index; ids inside such a group do
|
|
388
|
+
shift on group-local insertion. See
|
|
389
|
+
``docs/stable-identities.md`` for the full analysis.
|
|
390
|
+
reconcile_with : dict or str, optional
|
|
391
|
+
A previous export (document dict or JSON text of one) to adopt
|
|
392
|
+
ids from — see :func:`reconcile_ids`. Implies
|
|
393
|
+
``stable_ids=True`` (reconciled re-exports must replay the
|
|
394
|
+
same id scheme the registry was written with). The typical
|
|
395
|
+
workflow keeps the *previous* export file as the registry::
|
|
396
|
+
|
|
397
|
+
if Path("model.json").exists():
|
|
398
|
+
doc = to_interchange(model, reconcile_with="model.json")
|
|
399
|
+
else:
|
|
400
|
+
doc = to_interchange(model, stable_ids=True)
|
|
401
|
+
Path("model.json").write_text(interchange_to_json_text(doc))
|
|
249
402
|
|
|
250
403
|
Returns
|
|
251
404
|
-------
|
|
@@ -285,7 +438,9 @@ def to_interchange(source, *, vocabulary=None, explicit_terms=False):
|
|
|
285
438
|
)
|
|
286
439
|
|
|
287
440
|
graph: list = []
|
|
288
|
-
|
|
441
|
+
assigner = _StableIdAssigner() if (stable_ids or reconcile_with) else None
|
|
442
|
+
root_id = _flatten(root_dict, "root", "ownedRelationship", 0, graph,
|
|
443
|
+
assigner=assigner)
|
|
289
444
|
context = dict(INTERCHANGE_CONTEXT)
|
|
290
445
|
if vocabulary:
|
|
291
446
|
context["@vocab"] = vocabulary.rstrip("/#") + "#"
|
|
@@ -293,11 +448,138 @@ def to_interchange(source, *, vocabulary=None, explicit_terms=False):
|
|
|
293
448
|
context = build_jsonld_context(
|
|
294
449
|
{"@graph": graph}, vocabulary=vocabulary
|
|
295
450
|
)
|
|
296
|
-
|
|
451
|
+
document = {
|
|
297
452
|
"@context": context,
|
|
298
453
|
"@id": root_id,
|
|
299
454
|
"@graph": graph,
|
|
300
455
|
}
|
|
456
|
+
if reconcile_with is not None:
|
|
457
|
+
document = reconcile_ids(document, reconcile_with)
|
|
458
|
+
return document
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
# ---------------------------------------------------------------------------
|
|
462
|
+
# id registry reconciliation — L3 identity across edits (Phase 2 sidecar)
|
|
463
|
+
# ---------------------------------------------------------------------------
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
def _remap_refs(value, remap: dict):
|
|
467
|
+
"""Rewrite every ``{"@id": old}`` ref to ``{"@id": new}``. Top-level
|
|
468
|
+
element ids are rewritten by :func:`reconcile_ids` directly; this
|
|
469
|
+
walks nested property values (references, chains) so adopted ids
|
|
470
|
+
are consistent everywhere the document is rebuilt from."""
|
|
471
|
+
if isinstance(value, dict):
|
|
472
|
+
if set(value.keys()) == {"@id"}:
|
|
473
|
+
new_id = remap.get(value["@id"])
|
|
474
|
+
if new_id is not None:
|
|
475
|
+
value["@id"] = new_id
|
|
476
|
+
return
|
|
477
|
+
for v in value.values():
|
|
478
|
+
_remap_refs(v, remap)
|
|
479
|
+
elif isinstance(value, list):
|
|
480
|
+
for item in value:
|
|
481
|
+
_remap_refs(item, remap)
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
def reconcile_ids(document: dict, registry) -> dict:
|
|
485
|
+
"""Adopt element ids from a previous interchange export (the registry).
|
|
486
|
+
|
|
487
|
+
The registry is a previously exported document (``to_interchange``
|
|
488
|
+
output, as dict or JSON text) whose ``@graph`` maps ``@id`` to
|
|
489
|
+
``(declaredName, @type)``. Every element of ``document`` whose
|
|
490
|
+
``(declaredName, @type)`` matches **exactly one** registry entry —
|
|
491
|
+
and exactly one same-key element in ``document`` — inherits the
|
|
492
|
+
registry's ``@id`` instead of its freshly derived one. This is the
|
|
493
|
+
mechanism that keeps ids stable across edits that re-mint derived
|
|
494
|
+
ids (ancestor renames, re-nesting): identity follows the declared
|
|
495
|
+
name wherever the name survives the edit.
|
|
496
|
+
|
|
497
|
+
Matching is deliberately **injective**:
|
|
498
|
+
|
|
499
|
+
- the key must be unique on BOTH sides — when the model grows a
|
|
500
|
+
second ``PartDefinition`` while the registry has one, neither is
|
|
501
|
+
matched (a naive many-to-one adoption produced duplicate ids in
|
|
502
|
+
every rename-plus-insert scenario during the design spike);
|
|
503
|
+
- an old id already adopted by an earlier element is never adopted
|
|
504
|
+
again (first match wins in registry order);
|
|
505
|
+
- unmatched elements keep their freshly derived ids and become new
|
|
506
|
+
registry entries when the document is written back out.
|
|
507
|
+
|
|
508
|
+
Intended workflow (the export file doubles as the registry)::
|
|
509
|
+
|
|
510
|
+
document = to_interchange(model, stable_ids=True)
|
|
511
|
+
if Path("model.json").exists():
|
|
512
|
+
document = reconcile_ids(document, Path("model.json"))
|
|
513
|
+
Path("model.json").write_text(interchange_to_json_text(document))
|
|
514
|
+
|
|
515
|
+
Parameters
|
|
516
|
+
----------
|
|
517
|
+
document : dict
|
|
518
|
+
A freshly exported document (``to_interchange`` output).
|
|
519
|
+
registry : dict or str
|
|
520
|
+
The previous export: a document dict or a JSON string (the
|
|
521
|
+
text form loads the sidecar file directly).
|
|
522
|
+
|
|
523
|
+
Returns
|
|
524
|
+
-------
|
|
525
|
+
dict
|
|
526
|
+
``document`` with adopted ids (modified in place and returned).
|
|
527
|
+
"""
|
|
528
|
+
if isinstance(registry, (_Path, str)):
|
|
529
|
+
# Accept a filesystem path to a registry file (including
|
|
530
|
+
# ``Path`` objects), OR the previous export as JSON text /
|
|
531
|
+
# document dict. A str that names an existing, plausibly
|
|
532
|
+
# small file loads from disk; anything else is JSON text.
|
|
533
|
+
# (A str is only treated as a path if its length is a sane
|
|
534
|
+
# filename length — JSON documents can be megabytes.)
|
|
535
|
+
if isinstance(registry, _Path) or (
|
|
536
|
+
isinstance(registry, str) and len(registry) < 4097
|
|
537
|
+
and "\n" not in registry and _Path(registry).exists()):
|
|
538
|
+
registry = _Path(registry).read_text(encoding="utf-8-sig")
|
|
539
|
+
registry = _json.loads(registry)
|
|
540
|
+
if not isinstance(registry, dict):
|
|
541
|
+
raise ValueError(
|
|
542
|
+
"registry must be an interchange document dict or JSON "
|
|
543
|
+
f"string, got {type(registry).__name__}")
|
|
544
|
+
old_graph = registry.get("@graph")
|
|
545
|
+
new_graph = document.get("@graph")
|
|
546
|
+
if not isinstance(old_graph, list) or not isinstance(new_graph, list):
|
|
547
|
+
raise ValueError("both documents need an @graph array")
|
|
548
|
+
|
|
549
|
+
def _key(e):
|
|
550
|
+
return (e.get("declaredName"), e.get("@type"))
|
|
551
|
+
|
|
552
|
+
old_counts = _Counter(_key(e) for e in old_graph)
|
|
553
|
+
new_counts = _Counter(_key(e) for e in new_graph)
|
|
554
|
+
by_key: dict = {}
|
|
555
|
+
for e in old_graph:
|
|
556
|
+
k = _key(e)
|
|
557
|
+
# unique on BOTH sides — injective matching (see docstring)
|
|
558
|
+
if k in old_counts and old_counts[k] == 1 and new_counts.get(k) == 1 \
|
|
559
|
+
and k not in by_key:
|
|
560
|
+
by_key[k] = e["@id"]
|
|
561
|
+
used: set = set()
|
|
562
|
+
|
|
563
|
+
# id re-map for refs: a matched element's *derived* id (assigned
|
|
564
|
+
# during this export) must yield to the adopted registry id in
|
|
565
|
+
# EVERY nested ``{"@id": ...}`` reference too — element ids are
|
|
566
|
+
# only the top level; typed-by references, target refs and child
|
|
567
|
+
# chains all point at the derived ids and would dangle after a
|
|
568
|
+
# top-level-only rewrite.
|
|
569
|
+
remap: dict = {}
|
|
570
|
+
for e in new_graph:
|
|
571
|
+
k = _key(e)
|
|
572
|
+
old_id = by_key.get(k)
|
|
573
|
+
if old_id is not None and old_id not in used:
|
|
574
|
+
used.add(old_id)
|
|
575
|
+
derived_id = e["@id"]
|
|
576
|
+
e["@id"] = old_id
|
|
577
|
+
remap[derived_id] = old_id
|
|
578
|
+
|
|
579
|
+
if remap:
|
|
580
|
+
for e in new_graph:
|
|
581
|
+
_remap_refs(e, remap)
|
|
582
|
+
return document
|
|
301
583
|
|
|
302
584
|
|
|
303
585
|
def interchange_to_json_text(document: dict, *, indent: int = 2) -> str:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.interp
RENAMED
|
File without changes
|
{sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.py
RENAMED
|
File without changes
|
{sysmlpy-0.96.2 → sysmlpy-0.96.3}/src/sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.tokens
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|