sysmlpy 0.72.0__tar.gz → 0.74.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.72.0 → sysmlpy-0.74.0}/PKG-INFO +1 -1
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/pyproject.toml +1 -1
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/__init__.py +7 -1
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/__main__.py +59 -1
- sysmlpy-0.74.0/src/sysmlpy/diff.py +280 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/semantic.py +208 -23
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/LICENSE +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/README.md +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr/README.md +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr/SysMLv2Lexer.interp +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr/SysMLv2Lexer.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr/SysMLv2Lexer.tokens +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr/SysMLv2Parser.interp +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr/SysMLv2Parser.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr/SysMLv2Parser.tokens +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr/SysMLv2ParserListener.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr/SysMLv2ParserVisitor.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.interp +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.tokens +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr_parser.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr_visitor.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/boxes_view.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/definition.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/evaluator.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/examples/attribute_values.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/examples/part_attributes.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/examples/tuples_sequences.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/formatting.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/README.md +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.g4 +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.interp +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.tokens +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/SysMLv2Parser.g4 +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/SysMLv2Parser.interp +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/SysMLv2Parser.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/SysMLv2Parser.tokens +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/SysMLv2ParserListener.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/SysMLv2ParserVisitor.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/desc.xml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/examples/camera.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/examples/toaster-system.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/examples/vehicle-model.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/pom.xml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/classes.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/interchange.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/LICENSE +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/README.md +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/__init__.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Analysis/AnalysisTooling.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Analysis/SampledFunctions.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Analysis/StateSpaceRepresentation.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Analysis/TradeStudies.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Cause and Effect/CausationConnections.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Cause and Effect/CauseAndEffect.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Geometry/ShapeItems.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Geometry/SpatialItems.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Metadata/ImageMetadata.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Metadata/ModelingMetadata.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Metadata/ParametersOfInterestMetadata.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Metadata/RiskMetadata.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQ.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQAcoustics.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQAtomicNuclear.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQBase.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQCharacteristicNumbers.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQChemistryMolecular.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQCondensedMatter.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQElectromagnetism.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQInformation.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQLight.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQMechanics.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQSpaceTime.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQThermodynamics.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/MeasurementRefCalculations.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/MeasurementReferences.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/Quantities.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/QuantityCalculations.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/SI.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/SIPrefixes.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/TensorCalculations.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/Time.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/USCustomaryUnits.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/VectorCalculations.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Requirement Derivation/DerivationConnections.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Requirement Derivation/RequirementDerivation.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/Base.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/BaseFunctions.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/BooleanFunctions.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/Clocks.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/CollectionFunctions.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/Collections.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/ComplexFunctions.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/ControlFunctions.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/ControlPerformances.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/DataFunctions.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/FeatureReferencingPerformances.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/IntegerFunctions.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/KerML.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/Links.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/Metaobjects.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/NaturalFunctions.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/NumericalFunctions.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/Objects.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/Observation.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/OccurrenceFunctions.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/Occurrences.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/Performances.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/RationalFunctions.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/RealFunctions.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/ScalarFunctions.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/ScalarValues.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/SequenceFunctions.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/SpatialFrames.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/StatePerformances.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/StringFunctions.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/Transfers.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/TransitionPerformances.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/TrigFunctions.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/Triggers.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/VectorFunctions.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/VectorValues.kerml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Actions.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Allocations.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/AnalysisCases.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Attributes.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Calculations.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Cases.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Connections.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Constraints.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Flows.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Interfaces.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Items.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Metadata.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Parts.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Ports.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Requirements.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/StandardViewDefinitions.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/States.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/SysML.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/UseCases.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/VerificationCases.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Views.sysml +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/lsp/__init__.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/lsp/__main__.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/lsp/protocol.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/lsp/server.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/lsp/stdio.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/navigate.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/plantuml.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/project.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/sim.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/spreadsheet.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/store.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/traceability.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/us_customary_units.txt +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/usage.py +0 -0
- {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/validator.py +0 -0
|
@@ -34,7 +34,7 @@ __all__ = [
|
|
|
34
34
|
"render_state_transition_view_svg", "boxes_view",
|
|
35
35
|
]
|
|
36
36
|
__author__ = "Jon Fox"
|
|
37
|
-
__version__ = "0.
|
|
37
|
+
__version__ = "0.74.0"
|
|
38
38
|
|
|
39
39
|
from sysmlpy.usage import (
|
|
40
40
|
Item, Attribute, Part, Port, Action, Reference, UseCase, Requirement, Interface, Message,
|
|
@@ -442,5 +442,11 @@ def __getattr__(name):
|
|
|
442
442
|
fn = _pu.set_stereotype_palette
|
|
443
443
|
globals()[name] = fn
|
|
444
444
|
return fn
|
|
445
|
+
if name in ("ModelDiff", "ElementChange", "FieldChange",
|
|
446
|
+
"diff_models", "diff_files"):
|
|
447
|
+
from sysmlpy import diff as _diff
|
|
448
|
+
fn = getattr(_diff, name)
|
|
449
|
+
globals()[name] = fn
|
|
450
|
+
return fn
|
|
445
451
|
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
446
452
|
|
|
@@ -7,6 +7,7 @@ Subcommands
|
|
|
7
7
|
parse Parse a SysML file and print a representation (default: repr()).
|
|
8
8
|
analyze Run semantic analysis on one or more files; CI-friendly exit codes.
|
|
9
9
|
view Render a PlantUML / Markdown / HTML view of a model.
|
|
10
|
+
diff Semantic diff of two SysML files (review workflows).
|
|
10
11
|
format Canonicalize (pretty-print) SysML files (alias: fmt).
|
|
11
12
|
|
|
12
13
|
Exit codes
|
|
@@ -32,7 +33,7 @@ from sysmlpy import loads
|
|
|
32
33
|
|
|
33
34
|
# Subcommand table, also used to detect the legacy flat invocation form.
|
|
34
35
|
SUBCOMMANDS = ("parse", "analyze", "view", "trace", "export", "import",
|
|
35
|
-
"eval", "xlsx", "sim", "format", "fmt")
|
|
36
|
+
"eval", "xlsx", "sim", "diff", "format", "fmt")
|
|
36
37
|
|
|
37
38
|
|
|
38
39
|
# ---------------------------------------------------------------------------
|
|
@@ -102,6 +103,46 @@ def cmd_parse(args) -> int:
|
|
|
102
103
|
# sysmlpy analyze
|
|
103
104
|
# ---------------------------------------------------------------------------
|
|
104
105
|
|
|
106
|
+
def cmd_diff(args) -> int:
|
|
107
|
+
from sysmlpy.diff import diff_files
|
|
108
|
+
|
|
109
|
+
paths = [Path(f) for f in (args.old, args.new)]
|
|
110
|
+
for p in _missing_files(paths):
|
|
111
|
+
print(f"Error: File '{p}' not found.", file=sys.stderr)
|
|
112
|
+
return 2
|
|
113
|
+
|
|
114
|
+
try:
|
|
115
|
+
d = diff_files(args.old, args.new)
|
|
116
|
+
except Exception as e:
|
|
117
|
+
print(f"Parse error: {e}", file=sys.stderr)
|
|
118
|
+
return 2
|
|
119
|
+
|
|
120
|
+
if args.format == "json":
|
|
121
|
+
import json as _json
|
|
122
|
+
print(_json.dumps({
|
|
123
|
+
"summary": d.summary(),
|
|
124
|
+
"changes": [
|
|
125
|
+
{
|
|
126
|
+
"change": c.change,
|
|
127
|
+
"kind": c.kind,
|
|
128
|
+
"qualified_name": c.qualified_name,
|
|
129
|
+
"fields": [
|
|
130
|
+
{"field": fc.field, "old": fc.old,
|
|
131
|
+
"new": fc.new} for fc in c.fields
|
|
132
|
+
],
|
|
133
|
+
}
|
|
134
|
+
for c in d.changes
|
|
135
|
+
],
|
|
136
|
+
}, indent=2))
|
|
137
|
+
elif args.format == "markdown":
|
|
138
|
+
print(d.as_markdown(), end="")
|
|
139
|
+
else:
|
|
140
|
+
print(d.as_text(), end="")
|
|
141
|
+
|
|
142
|
+
# CI-friendly: differences are findings
|
|
143
|
+
return 1 if not d.is_empty() else 0
|
|
144
|
+
|
|
145
|
+
|
|
105
146
|
def cmd_analyze(args) -> int:
|
|
106
147
|
from sysmlpy import analyze
|
|
107
148
|
|
|
@@ -704,6 +745,23 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
704
745
|
)
|
|
705
746
|
p_analyze.set_defaults(func=cmd_analyze)
|
|
706
747
|
|
|
748
|
+
# -- diff ----------------------------------------------------------------
|
|
749
|
+
p_diff = sub.add_parser(
|
|
750
|
+
"diff",
|
|
751
|
+
help="Semantic diff of two SysML files",
|
|
752
|
+
description="Compare two SysML v2 files element-by-element and "
|
|
753
|
+
"report added / removed / changed elements. Exit 0 "
|
|
754
|
+
"when the models are semantically identical, 1 when "
|
|
755
|
+
"they differ, 2 on parse/load failure.",
|
|
756
|
+
)
|
|
757
|
+
p_diff.add_argument("old", help="Old SysML v2 file")
|
|
758
|
+
p_diff.add_argument("new", help="New SysML v2 file")
|
|
759
|
+
p_diff.add_argument(
|
|
760
|
+
"--format", choices=("text", "markdown", "json"), default="text",
|
|
761
|
+
help="Output format: text (default), markdown or JSON",
|
|
762
|
+
)
|
|
763
|
+
p_diff.set_defaults(func=cmd_diff)
|
|
764
|
+
|
|
707
765
|
# -- view ----------------------------------------------------------------
|
|
708
766
|
p_view = sub.add_parser(
|
|
709
767
|
"view",
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
"""Semantic model diff for review workflows (Goal 8).
|
|
2
|
+
|
|
3
|
+
Compares two loaded models element-by-element and reports what
|
|
4
|
+
changed, so review workflows (PR gates, design comparisons) can see
|
|
5
|
+
*semantic* changes rather than whitespace/formatting noise.
|
|
6
|
+
|
|
7
|
+
Element identity is ``(kind, qualified name)`` — ``Fleet::Vehicle``
|
|
8
|
+
as a ``PartDef`` is a different identity from a ``PartUsage`` with
|
|
9
|
+
the same name. Every element carries a *signature* — the semantic
|
|
10
|
+
attributes the library tracks reliably on tree objects:
|
|
11
|
+
|
|
12
|
+
- ``typing`` — the declared type name (``typed_by_name``, qualified)
|
|
13
|
+
- ``subject`` — requirement subjects (``(name, type)`` tuples)
|
|
14
|
+
- ``doc`` — documentation text when present
|
|
15
|
+
|
|
16
|
+
Change kinds:
|
|
17
|
+
|
|
18
|
+
- ``added`` — element exists only in the new model
|
|
19
|
+
- ``removed`` — element exists only in the old model
|
|
20
|
+
- ``changed`` — same identity, different signature; ``fields`` lists
|
|
21
|
+
the field-level old/new values
|
|
22
|
+
|
|
23
|
+
Renames surface as a removed/added pair (rename detection needs
|
|
24
|
+
heuristic matching — a tracked follow-up, not MVP).
|
|
25
|
+
|
|
26
|
+
Excluded by design:
|
|
27
|
+
|
|
28
|
+
- ``Model`` objects carry a random UUID name per parse, so they are
|
|
29
|
+
never compared.
|
|
30
|
+
- Transitions are not tree objects (like verify members, they ride
|
|
31
|
+
the grammar dicts); state-machine diffs need the simulator's
|
|
32
|
+
machine descriptors — a tracked follow-up.
|
|
33
|
+
- Values, multiplicities and directions live in grammar dicts, not
|
|
34
|
+
on tree objects — grammar-level diffs are a follow-up batch.
|
|
35
|
+
|
|
36
|
+
Usage::
|
|
37
|
+
|
|
38
|
+
from sysmlpy.diff import diff_models, diff_files
|
|
39
|
+
|
|
40
|
+
d = diff_models(old_model, new_model)
|
|
41
|
+
print(d.as_text())
|
|
42
|
+
d2 = diff_files("old.sysml", "new.sysml")
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
from __future__ import annotations
|
|
46
|
+
|
|
47
|
+
from dataclasses import dataclass, field
|
|
48
|
+
from pathlib import Path
|
|
49
|
+
from typing import Any
|
|
50
|
+
|
|
51
|
+
__all__ = [
|
|
52
|
+
"FieldChange",
|
|
53
|
+
"ElementChange",
|
|
54
|
+
"ModelDiff",
|
|
55
|
+
"diff_models",
|
|
56
|
+
"diff_files",
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# ---------------------------------------------------------------------------
|
|
61
|
+
# data types
|
|
62
|
+
# ---------------------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass
|
|
66
|
+
class FieldChange:
|
|
67
|
+
"""One changed field within an element."""
|
|
68
|
+
|
|
69
|
+
field: str
|
|
70
|
+
old: str | None
|
|
71
|
+
new: str | None
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@dataclass
|
|
75
|
+
class ElementChange:
|
|
76
|
+
"""One element-level semantic change."""
|
|
77
|
+
|
|
78
|
+
change: str # "added" | "removed" | "changed"
|
|
79
|
+
kind: str # e.g. "PartDef", "PartUsage", "Package"
|
|
80
|
+
name: str
|
|
81
|
+
qualified_name: str
|
|
82
|
+
fields: list = field(default_factory=list) # list[FieldChange]
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@dataclass
|
|
86
|
+
class ModelDiff:
|
|
87
|
+
"""The semantic diff between two models."""
|
|
88
|
+
|
|
89
|
+
changes: list = field(default_factory=list) # list[ElementChange]
|
|
90
|
+
|
|
91
|
+
# ------------------------------------------------------------------
|
|
92
|
+
# queries
|
|
93
|
+
# ------------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def added(self) -> list:
|
|
97
|
+
return [c for c in self.changes if c.change == "added"]
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def removed(self) -> list:
|
|
101
|
+
return [c for c in self.changes if c.change == "removed"]
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def changed(self) -> list:
|
|
105
|
+
return [c for c in self.changes if c.change == "changed"]
|
|
106
|
+
|
|
107
|
+
def is_empty(self) -> bool:
|
|
108
|
+
"""True when the models are semantically identical."""
|
|
109
|
+
return not self.changes
|
|
110
|
+
|
|
111
|
+
def summary(self) -> str:
|
|
112
|
+
"""One-line human summary, e.g. ``2 added, 1 removed, 3 changed``."""
|
|
113
|
+
counts = [f"{len(self.added)} added",
|
|
114
|
+
f"{len(self.removed)} removed",
|
|
115
|
+
f"{len(self.changed)} changed"]
|
|
116
|
+
if self.is_empty():
|
|
117
|
+
return "models are semantically identical"
|
|
118
|
+
return ", ".join(counts)
|
|
119
|
+
|
|
120
|
+
# ------------------------------------------------------------------
|
|
121
|
+
# rendering
|
|
122
|
+
# ------------------------------------------------------------------
|
|
123
|
+
|
|
124
|
+
def as_text(self) -> str:
|
|
125
|
+
"""Plain-text rendering (monochrome — review-log friendly)."""
|
|
126
|
+
if self.is_empty():
|
|
127
|
+
return "models are semantically identical\n"
|
|
128
|
+
marker = {"added": "+", "removed": "-", "changed": "~"}
|
|
129
|
+
out = [f"Model diff: {self.summary()}", ""]
|
|
130
|
+
for c in self.changes:
|
|
131
|
+
out.append(f"{marker[c.change]} {c.kind} {c.qualified_name}")
|
|
132
|
+
for fc in c.fields:
|
|
133
|
+
old_v = fc.old if fc.old is not None else "(none)"
|
|
134
|
+
new_v = fc.new if fc.new is not None else "(none)"
|
|
135
|
+
out.append(f" {fc.field}: {old_v} -> {new_v}")
|
|
136
|
+
return "\n".join(out) + "\n"
|
|
137
|
+
|
|
138
|
+
def as_markdown(self) -> str:
|
|
139
|
+
"""Markdown rendering for review workflows (PR comments etc.)."""
|
|
140
|
+
if self.is_empty():
|
|
141
|
+
return "Models are semantically identical.\n"
|
|
142
|
+
out = [f"## Model diff", "",
|
|
143
|
+
f"**{self.summary()}**", ""]
|
|
144
|
+
sections = [
|
|
145
|
+
("added", "### Added"),
|
|
146
|
+
("removed", "### Removed"),
|
|
147
|
+
("changed", "### Changed"),
|
|
148
|
+
]
|
|
149
|
+
for kind, header in sections:
|
|
150
|
+
items = [c for c in self.changes if c.change == kind]
|
|
151
|
+
if not items:
|
|
152
|
+
continue
|
|
153
|
+
out.append(header)
|
|
154
|
+
out.append("")
|
|
155
|
+
for c in items:
|
|
156
|
+
out.append(f"- `{c.qualified_name}` ({c.kind})")
|
|
157
|
+
for fc in c.fields:
|
|
158
|
+
old_v = fc.old if fc.old is not None else "—"
|
|
159
|
+
new_v = fc.new if fc.new is not None else "—"
|
|
160
|
+
out.append(
|
|
161
|
+
f" - `{fc.field}`: `{old_v}` → `{new_v}`")
|
|
162
|
+
out.append("")
|
|
163
|
+
return "\n".join(out).rstrip() + "\n"
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
# ---------------------------------------------------------------------------
|
|
167
|
+
# collection
|
|
168
|
+
# ---------------------------------------------------------------------------
|
|
169
|
+
|
|
170
|
+
_FIELDS = ("typing", "subject", "doc")
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _kind(obj) -> str:
|
|
174
|
+
"""Element kind with a definition/usage suffix.
|
|
175
|
+
|
|
176
|
+
``PartDef`` / ``PartUsage`` — the same qualified name under both
|
|
177
|
+
roles is a different identity, so e.g. turning ``part def X`` into
|
|
178
|
+
an inline ``part x : X`` reports as removed + added, not changed.
|
|
179
|
+
"""
|
|
180
|
+
base = type(obj).__name__
|
|
181
|
+
return base + ("Def" if getattr(obj, "is_definition", False)
|
|
182
|
+
else "Usage")
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _signature(obj) -> dict:
|
|
186
|
+
"""The comparable semantic attributes of a tree object."""
|
|
187
|
+
sig = {}
|
|
188
|
+
|
|
189
|
+
typing = getattr(obj, "typed_by_name", None)
|
|
190
|
+
sig["typing"] = str(typing) if typing else None
|
|
191
|
+
|
|
192
|
+
subject = getattr(obj, "subject", None)
|
|
193
|
+
if subject:
|
|
194
|
+
name, type_name = subject
|
|
195
|
+
sig["subject"] = f"{name} : {type_name}" if type_name else str(name)
|
|
196
|
+
else:
|
|
197
|
+
sig["subject"] = None
|
|
198
|
+
|
|
199
|
+
doc = getattr(obj, "doc", None)
|
|
200
|
+
sig["doc"] = str(doc) if doc else None
|
|
201
|
+
|
|
202
|
+
return sig
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _collect(model) -> dict:
|
|
206
|
+
"""Map ``(kind, qualified name)`` -> ``(name, signature)``."""
|
|
207
|
+
from sysmlpy.traceability import _walk, _qualified_name
|
|
208
|
+
|
|
209
|
+
out = {}
|
|
210
|
+
for obj in _walk(model):
|
|
211
|
+
if type(obj).__name__ == "Model":
|
|
212
|
+
continue # random UUID name — not comparable
|
|
213
|
+
qname = _qualified_name(obj)
|
|
214
|
+
if not qname:
|
|
215
|
+
continue
|
|
216
|
+
key = (_kind(obj), qname)
|
|
217
|
+
out[key] = (getattr(obj, "name", ""), _signature(obj))
|
|
218
|
+
return out
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
# ---------------------------------------------------------------------------
|
|
222
|
+
# public API
|
|
223
|
+
# ---------------------------------------------------------------------------
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def diff_models(old_model: Any, new_model: Any) -> ModelDiff:
|
|
227
|
+
"""Diff two loaded models semantically.
|
|
228
|
+
|
|
229
|
+
Parameters
|
|
230
|
+
----------
|
|
231
|
+
old_model, new_model : Model
|
|
232
|
+
Two models loaded via :func:`sysmlpy.loads` /
|
|
233
|
+
:func:`sysmlpy.load_files` (or any Model tree).
|
|
234
|
+
"""
|
|
235
|
+
old = _collect(old_model)
|
|
236
|
+
new = _collect(new_model)
|
|
237
|
+
|
|
238
|
+
changes = []
|
|
239
|
+
for key in sorted(set(old) | set(new)):
|
|
240
|
+
in_old = key in old
|
|
241
|
+
in_new = key in new
|
|
242
|
+
kind, qname = key
|
|
243
|
+
if in_old and not in_new:
|
|
244
|
+
changes.append(ElementChange(
|
|
245
|
+
change="removed", kind=kind,
|
|
246
|
+
name=old[key][0], qualified_name=qname))
|
|
247
|
+
elif in_new and not in_old:
|
|
248
|
+
changes.append(ElementChange(
|
|
249
|
+
change="added", kind=kind,
|
|
250
|
+
name=new[key][0], qualified_name=qname))
|
|
251
|
+
else:
|
|
252
|
+
old_sig = old[key][1]
|
|
253
|
+
new_sig = new[key][1]
|
|
254
|
+
field_changes = [
|
|
255
|
+
FieldChange(field=f, old=old_sig.get(f),
|
|
256
|
+
new=new_sig.get(f))
|
|
257
|
+
for f in _FIELDS
|
|
258
|
+
if old_sig.get(f) != new_sig.get(f)
|
|
259
|
+
]
|
|
260
|
+
if field_changes:
|
|
261
|
+
changes.append(ElementChange(
|
|
262
|
+
change="changed", kind=kind,
|
|
263
|
+
name=new[key][0], qualified_name=qname,
|
|
264
|
+
fields=field_changes))
|
|
265
|
+
return ModelDiff(changes=changes)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def diff_files(old_path, new_path) -> ModelDiff:
|
|
269
|
+
"""Diff two SysML source files.
|
|
270
|
+
|
|
271
|
+
Parameters
|
|
272
|
+
----------
|
|
273
|
+
old_path, new_path : str | Path
|
|
274
|
+
Paths to ``.sysml`` files.
|
|
275
|
+
"""
|
|
276
|
+
import sysmlpy
|
|
277
|
+
|
|
278
|
+
old_model = sysmlpy.loads(Path(old_path).read_text(encoding="utf-8"))
|
|
279
|
+
new_model = sysmlpy.loads(Path(new_path).read_text(encoding="utf-8"))
|
|
280
|
+
return diff_models(old_model, new_model)
|