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.
Files changed (159) hide show
  1. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/PKG-INFO +1 -1
  2. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/pyproject.toml +1 -1
  3. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/__init__.py +7 -1
  4. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/__main__.py +59 -1
  5. sysmlpy-0.74.0/src/sysmlpy/diff.py +280 -0
  6. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/semantic.py +208 -23
  7. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/LICENSE +0 -0
  8. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/README.md +0 -0
  9. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr/README.md +0 -0
  10. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr/SysMLv2Lexer.interp +0 -0
  11. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr/SysMLv2Lexer.py +0 -0
  12. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr/SysMLv2Lexer.tokens +0 -0
  13. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr/SysMLv2Parser.interp +0 -0
  14. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr/SysMLv2Parser.py +0 -0
  15. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr/SysMLv2Parser.tokens +0 -0
  16. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr/SysMLv2ParserListener.py +0 -0
  17. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr/SysMLv2ParserVisitor.py +0 -0
  18. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.interp +0 -0
  19. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.py +0 -0
  20. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.tokens +0 -0
  21. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr_parser.py +0 -0
  22. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/antlr_visitor.py +0 -0
  23. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/boxes_view.py +0 -0
  24. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/definition.py +0 -0
  25. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/evaluator.py +0 -0
  26. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/examples/attribute_values.py +0 -0
  27. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/examples/part_attributes.py +0 -0
  28. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/examples/tuples_sequences.py +0 -0
  29. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/formatting.py +0 -0
  30. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/README.md +0 -0
  31. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.g4 +0 -0
  32. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.interp +0 -0
  33. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.py +0 -0
  34. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.tokens +0 -0
  35. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/SysMLv2Parser.g4 +0 -0
  36. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/SysMLv2Parser.interp +0 -0
  37. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/SysMLv2Parser.py +0 -0
  38. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/SysMLv2Parser.tokens +0 -0
  39. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/SysMLv2ParserListener.py +0 -0
  40. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/SysMLv2ParserVisitor.py +0 -0
  41. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/desc.xml +0 -0
  42. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/examples/camera.sysml +0 -0
  43. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/examples/toaster-system.sysml +0 -0
  44. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/examples/vehicle-model.sysml +0 -0
  45. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/antlr4/pom.xml +0 -0
  46. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/grammar/classes.py +0 -0
  47. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/interchange.py +0 -0
  48. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/LICENSE +0 -0
  49. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/README.md +0 -0
  50. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/__init__.py +0 -0
  51. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Analysis/AnalysisTooling.sysml +0 -0
  52. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Analysis/SampledFunctions.sysml +0 -0
  53. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Analysis/StateSpaceRepresentation.sysml +0 -0
  54. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Analysis/TradeStudies.sysml +0 -0
  55. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Cause and Effect/CausationConnections.sysml +0 -0
  56. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Cause and Effect/CauseAndEffect.sysml +0 -0
  57. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Geometry/ShapeItems.sysml +0 -0
  58. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Geometry/SpatialItems.sysml +0 -0
  59. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Metadata/ImageMetadata.sysml +0 -0
  60. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Metadata/ModelingMetadata.sysml +0 -0
  61. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Metadata/ParametersOfInterestMetadata.sysml +0 -0
  62. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Metadata/RiskMetadata.sysml +0 -0
  63. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQ.sysml +0 -0
  64. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQAcoustics.sysml +0 -0
  65. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQAtomicNuclear.sysml +0 -0
  66. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQBase.sysml +0 -0
  67. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQCharacteristicNumbers.sysml +0 -0
  68. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQChemistryMolecular.sysml +0 -0
  69. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQCondensedMatter.sysml +0 -0
  70. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQElectromagnetism.sysml +0 -0
  71. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQInformation.sysml +0 -0
  72. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQLight.sysml +0 -0
  73. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQMechanics.sysml +0 -0
  74. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQSpaceTime.sysml +0 -0
  75. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/ISQThermodynamics.sysml +0 -0
  76. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/MeasurementRefCalculations.sysml +0 -0
  77. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/MeasurementReferences.sysml +0 -0
  78. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/Quantities.sysml +0 -0
  79. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/QuantityCalculations.sysml +0 -0
  80. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/SI.sysml +0 -0
  81. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/SIPrefixes.sysml +0 -0
  82. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/TensorCalculations.sysml +0 -0
  83. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/Time.sysml +0 -0
  84. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/USCustomaryUnits.sysml +0 -0
  85. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Quantities and Units/VectorCalculations.sysml +0 -0
  86. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Requirement Derivation/DerivationConnections.sysml +0 -0
  87. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/domain/Requirement Derivation/RequirementDerivation.sysml +0 -0
  88. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/Base.kerml +0 -0
  89. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/BaseFunctions.kerml +0 -0
  90. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/BooleanFunctions.kerml +0 -0
  91. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/Clocks.kerml +0 -0
  92. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/CollectionFunctions.kerml +0 -0
  93. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/Collections.kerml +0 -0
  94. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/ComplexFunctions.kerml +0 -0
  95. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/ControlFunctions.kerml +0 -0
  96. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/ControlPerformances.kerml +0 -0
  97. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/DataFunctions.kerml +0 -0
  98. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/FeatureReferencingPerformances.kerml +0 -0
  99. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/IntegerFunctions.kerml +0 -0
  100. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/KerML.kerml +0 -0
  101. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/Links.kerml +0 -0
  102. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/Metaobjects.kerml +0 -0
  103. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/NaturalFunctions.kerml +0 -0
  104. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/NumericalFunctions.kerml +0 -0
  105. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/Objects.kerml +0 -0
  106. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/Observation.kerml +0 -0
  107. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/OccurrenceFunctions.kerml +0 -0
  108. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/Occurrences.kerml +0 -0
  109. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/Performances.kerml +0 -0
  110. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/RationalFunctions.kerml +0 -0
  111. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/RealFunctions.kerml +0 -0
  112. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/ScalarFunctions.kerml +0 -0
  113. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/ScalarValues.kerml +0 -0
  114. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/SequenceFunctions.kerml +0 -0
  115. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/SpatialFrames.kerml +0 -0
  116. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/StatePerformances.kerml +0 -0
  117. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/StringFunctions.kerml +0 -0
  118. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/Transfers.kerml +0 -0
  119. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/TransitionPerformances.kerml +0 -0
  120. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/TrigFunctions.kerml +0 -0
  121. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/Triggers.kerml +0 -0
  122. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/VectorFunctions.kerml +0 -0
  123. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/kernel/VectorValues.kerml +0 -0
  124. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Actions.sysml +0 -0
  125. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Allocations.sysml +0 -0
  126. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/AnalysisCases.sysml +0 -0
  127. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Attributes.sysml +0 -0
  128. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Calculations.sysml +0 -0
  129. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Cases.sysml +0 -0
  130. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Connections.sysml +0 -0
  131. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Constraints.sysml +0 -0
  132. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Flows.sysml +0 -0
  133. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Interfaces.sysml +0 -0
  134. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Items.sysml +0 -0
  135. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Metadata.sysml +0 -0
  136. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Parts.sysml +0 -0
  137. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Ports.sysml +0 -0
  138. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Requirements.sysml +0 -0
  139. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/StandardViewDefinitions.sysml +0 -0
  140. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/States.sysml +0 -0
  141. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/SysML.sysml +0 -0
  142. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/UseCases.sysml +0 -0
  143. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/VerificationCases.sysml +0 -0
  144. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/library/systems/Views.sysml +0 -0
  145. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/lsp/__init__.py +0 -0
  146. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/lsp/__main__.py +0 -0
  147. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/lsp/protocol.py +0 -0
  148. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/lsp/server.py +0 -0
  149. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/lsp/stdio.py +0 -0
  150. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/navigate.py +0 -0
  151. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/plantuml.py +0 -0
  152. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/project.py +0 -0
  153. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/sim.py +0 -0
  154. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/spreadsheet.py +0 -0
  155. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/store.py +0 -0
  156. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/traceability.py +0 -0
  157. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/us_customary_units.txt +0 -0
  158. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/usage.py +0 -0
  159. {sysmlpy-0.72.0 → sysmlpy-0.74.0}/src/sysmlpy/validator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sysmlpy
3
- Version: 0.72.0
3
+ Version: 0.74.0
4
4
  Summary:
5
5
  License-File: LICENSE
6
6
  Author: Jon R. Fox (mycr0ft)
@@ -6,7 +6,7 @@ build-backend = "poetry.core.masonry.api"
6
6
 
7
7
  [project]
8
8
  name = "sysmlpy"
9
- version = "0.72.0"
9
+ version = "0.74.0"
10
10
  description = ""
11
11
  authors = [{ name = "Jon R. Fox (mycr0ft)", email = "jon.fox@drfox.com" }]
12
12
  readme = "README.md"
@@ -34,7 +34,7 @@ __all__ = [
34
34
  "render_state_transition_view_svg", "boxes_view",
35
35
  ]
36
36
  __author__ = "Jon Fox"
37
- __version__ = "0.72.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)