sysmlpy 0.65.0__tar.gz → 0.67.0__tar.gz

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