statable 3.2.1__tar.gz → 3.2.2__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 (166) hide show
  1. {statable-3.2.1 → statable-3.2.2}/PKG-INFO +1 -1
  2. {statable-3.2.1 → statable-3.2.2}/codegen/__init__.py +1 -1
  3. {statable-3.2.1 → statable-3.2.2}/pyproject.toml +1 -1
  4. {statable-3.2.1 → statable-3.2.2}/statable/__init__.py +1 -1
  5. {statable-3.2.1 → statable-3.2.2}/statable/cli.py +1 -1
  6. {statable-3.2.1 → statable-3.2.2}/statable.egg-info/PKG-INFO +1 -1
  7. {statable-3.2.1 → statable-3.2.2}/statable.egg-info/SOURCES.txt +2 -1
  8. {statable-3.2.1 → statable-3.2.2}/statable_gui/main_window.py +2 -1
  9. {statable-3.2.1 → statable-3.2.2}/tests/test_v3_0_s1_packaging.py +9 -9
  10. {statable-3.2.1 → statable-3.2.2}/tests/test_v3_0_s2_public_api.py +4 -4
  11. {statable-3.2.1 → statable-3.2.2}/tests/test_v3_0_s3_cli.py +4 -4
  12. statable-3.2.2/tests/test_v3_2_s2_signals.py +144 -0
  13. {statable-3.2.1 → statable-3.2.2}/README.md +0 -0
  14. {statable-3.2.1 → statable-3.2.2}/codegen/c_code_generator.py +0 -0
  15. {statable-3.2.1 → statable-3.2.2}/codegen/code_merger.py +0 -0
  16. {statable-3.2.1 → statable-3.2.2}/codegen/code_templates.py +0 -0
  17. {statable-3.2.1 → statable-3.2.2}/codegen/config.py +0 -0
  18. {statable-3.2.1 → statable-3.2.2}/codegen/enum_generator.py +0 -0
  19. {statable-3.2.1 → statable-3.2.2}/codegen/event_queue_generator.py +0 -0
  20. {statable-3.2.1 → statable-3.2.2}/codegen/interrupt_generator.py +0 -0
  21. {statable-3.2.1 → statable-3.2.2}/codegen/naming_convention.py +0 -0
  22. {statable-3.2.1 → statable-3.2.2}/codegen/osal_generator.py +0 -0
  23. {statable-3.2.1 → statable-3.2.2}/codegen/role_function_generator.py +0 -0
  24. {statable-3.2.1 → statable-3.2.2}/codegen/sample_data.py +0 -0
  25. {statable-3.2.1 → statable-3.2.2}/codegen/state_actions_generator.py +0 -0
  26. {statable-3.2.1 → statable-3.2.2}/codegen/struct_generator.py +0 -0
  27. {statable-3.2.1 → statable-3.2.2}/codegen/timer_generator.py +0 -0
  28. {statable-3.2.1 → statable-3.2.2}/codegen/transition_generator.py +0 -0
  29. {statable-3.2.1 → statable-3.2.2}/codegen/type_mapper.py +0 -0
  30. {statable-3.2.1 → statable-3.2.2}/codegen/validate/__init__.py +0 -0
  31. {statable-3.2.1 → statable-3.2.2}/codegen/validate/change_actions.py +0 -0
  32. {statable-3.2.1 → statable-3.2.2}/codegen/validate/change_applier.py +0 -0
  33. {statable-3.2.1 → statable-3.2.2}/codegen/validate/clipboard_manager.py +0 -0
  34. {statable-3.2.1 → statable-3.2.2}/codegen/validate/data/__init__.py +0 -0
  35. {statable-3.2.1 → statable-3.2.2}/codegen/validate/data/action_definitions.py +0 -0
  36. {statable-3.2.1 → statable-3.2.2}/codegen/validate/data/keywords.py +0 -0
  37. {statable-3.2.1 → statable-3.2.2}/codegen/validate/data/prompt_templates.py +0 -0
  38. {statable-3.2.1 → statable-3.2.2}/codegen/validate/data/validation_rules.py +0 -0
  39. {statable-3.2.1 → statable-3.2.2}/codegen/validate/items/__init__.py +0 -0
  40. {statable-3.2.1 → statable-3.2.2}/codegen/validate/items/base_validator.py +0 -0
  41. {statable-3.2.1 → statable-3.2.2}/codegen/validate/items/cell_validator.py +0 -0
  42. {statable-3.2.1 → statable-3.2.2}/codegen/validate/items/custom_type_validator.py +0 -0
  43. {statable-3.2.1 → statable-3.2.2}/codegen/validate/items/event_validator.py +0 -0
  44. {statable-3.2.1 → statable-3.2.2}/codegen/validate/items/flag_validator.py +0 -0
  45. {statable-3.2.1 → statable-3.2.2}/codegen/validate/items/interrupt_validator.py +0 -0
  46. {statable-3.2.1 → statable-3.2.2}/codegen/validate/items/queue_validator.py +0 -0
  47. {statable-3.2.1 → statable-3.2.2}/codegen/validate/items/role_function_validator.py +0 -0
  48. {statable-3.2.1 → statable-3.2.2}/codegen/validate/items/state_validator.py +0 -0
  49. {statable-3.2.1 → statable-3.2.2}/codegen/validate/items/timer_validator.py +0 -0
  50. {statable-3.2.1 → statable-3.2.2}/codegen/validate/items/transition_validator.py +0 -0
  51. {statable-3.2.1 → statable-3.2.2}/codegen/validate/items/variable_validator.py +0 -0
  52. {statable-3.2.1 → statable-3.2.2}/codegen/validate/logger.py +0 -0
  53. {statable-3.2.1 → statable-3.2.2}/codegen/validate/models.py +0 -0
  54. {statable-3.2.1 → statable-3.2.2}/codegen/validate/prompt_generator.py +0 -0
  55. {statable-3.2.1 → statable-3.2.2}/codegen/validate/response_parser.py +0 -0
  56. {statable-3.2.1 → statable-3.2.2}/codegen/validate/response_validator.py +0 -0
  57. {statable-3.2.1 → statable-3.2.2}/codegen/validate/validation_dialog.py +0 -0
  58. {statable-3.2.1 → statable-3.2.2}/codegen/validate/validator.py +0 -0
  59. {statable-3.2.1 → statable-3.2.2}/codegen/variable_generator.py +0 -0
  60. {statable-3.2.1 → statable-3.2.2}/setup.cfg +0 -0
  61. {statable-3.2.1 → statable-3.2.2}/statable/__main__.py +0 -0
  62. {statable-3.2.1 → statable-3.2.2}/statable/global_defs.py +0 -0
  63. {statable-3.2.1 → statable-3.2.2}/statable/mermaid_gen.py +0 -0
  64. {statable-3.2.1 → statable-3.2.2}/statable/model.py +0 -0
  65. {statable-3.2.1 → statable-3.2.2}/statable/parser.py +0 -0
  66. {statable-3.2.1 → statable-3.2.2}/statable/sample_data.py +0 -0
  67. {statable-3.2.1 → statable-3.2.2}/statable/shared/__init__.py +0 -0
  68. {statable-3.2.1 → statable-3.2.2}/statable/shared/condition_library.py +0 -0
  69. {statable-3.2.1 → statable-3.2.2}/statable/shared/literal_library.py +0 -0
  70. {statable-3.2.1 → statable-3.2.2}/statable/shared/role_function_library.py +0 -0
  71. {statable-3.2.1 → statable-3.2.2}/statable/state_machine.py +0 -0
  72. {statable-3.2.1 → statable-3.2.2}/statable/xml_io.py +0 -0
  73. {statable-3.2.1 → statable-3.2.2}/statable.egg-info/dependency_links.txt +0 -0
  74. {statable-3.2.1 → statable-3.2.2}/statable.egg-info/entry_points.txt +0 -0
  75. {statable-3.2.1 → statable-3.2.2}/statable.egg-info/requires.txt +0 -0
  76. {statable-3.2.1 → statable-3.2.2}/statable.egg-info/top_level.txt +0 -0
  77. {statable-3.2.1 → statable-3.2.2}/statable_gui/__init__.py +0 -0
  78. {statable-3.2.1 → statable-3.2.2}/statable_gui/action_edit_dialog.py +0 -0
  79. {statable-3.2.1 → statable-3.2.2}/statable_gui/code_generation_dialog.py +0 -0
  80. {statable-3.2.1 → statable-3.2.2}/statable_gui/code_generation_settings_dialog.py +0 -0
  81. {statable-3.2.1 → statable-3.2.2}/statable_gui/common_widgets.py +0 -0
  82. {statable-3.2.1 → statable-3.2.2}/statable_gui/condition_builder_dialog.py +0 -0
  83. {statable-3.2.1 → statable-3.2.2}/statable_gui/condition_edit_dialog.py +0 -0
  84. {statable-3.2.1 → statable-3.2.2}/statable_gui/condition_template_dialog.py +0 -0
  85. {statable-3.2.1 → statable-3.2.2}/statable_gui/config.py +0 -0
  86. {statable-3.2.1 → statable-3.2.2}/statable_gui/dialogs.py +0 -0
  87. {statable-3.2.1 → statable-3.2.2}/statable_gui/event_definition_dialog.py +0 -0
  88. {statable-3.2.1 → statable-3.2.2}/statable_gui/event_delivery_settings_dialog.py +0 -0
  89. {statable-3.2.1 → statable-3.2.2}/statable_gui/event_queue_dialog.py +0 -0
  90. {statable-3.2.1 → statable-3.2.2}/statable_gui/global_defs.py +0 -0
  91. {statable-3.2.1 → statable-3.2.2}/statable_gui/global_defs_dialog.py +0 -0
  92. {statable-3.2.1 → statable-3.2.2}/statable_gui/i18n/__init__.py +0 -0
  93. {statable-3.2.1 → statable-3.2.2}/statable_gui/interrupt_handler_edit_dialog.py +0 -0
  94. {statable-3.2.1 → statable-3.2.2}/statable_gui/layer_settings_dialog.py +0 -0
  95. {statable-3.2.1 → statable-3.2.2}/statable_gui/libcntrl/__init__.py +0 -0
  96. {statable-3.2.1 → statable-3.2.2}/statable_gui/libcntrl/condition_library.py +0 -0
  97. {statable-3.2.1 → statable-3.2.2}/statable_gui/libcntrl/literal_library.py +0 -0
  98. {statable-3.2.1 → statable-3.2.2}/statable_gui/libcntrl/literal_management_dialog.py +0 -0
  99. {statable-3.2.1 → statable-3.2.2}/statable_gui/libcntrl/role_function_edit_dialog.py +0 -0
  100. {statable-3.2.1 → statable-3.2.2}/statable_gui/libcntrl/role_function_library.py +0 -0
  101. {statable-3.2.1 → statable-3.2.2}/statable_gui/literal_definition_dialog.py +0 -0
  102. {statable-3.2.1 → statable-3.2.2}/statable_gui/logger.py +0 -0
  103. {statable-3.2.1 → statable-3.2.2}/statable_gui/matrix_table.py +0 -0
  104. {statable-3.2.1 → statable-3.2.2}/statable_gui/new_project_templates.py +0 -0
  105. {statable-3.2.1 → statable-3.2.2}/statable_gui/new_project_wizard.py +0 -0
  106. {statable-3.2.1 → statable-3.2.2}/statable_gui/preference_keys.py +0 -0
  107. {statable-3.2.1 → statable-3.2.2}/statable_gui/preferences.py +0 -0
  108. {statable-3.2.1 → statable-3.2.2}/statable_gui/role_function_dialog.py +0 -0
  109. {statable-3.2.1 → statable-3.2.2}/statable_gui/sample_data.py +0 -0
  110. {statable-3.2.1 → statable-3.2.2}/statable_gui/state_actions_dialog.py +0 -0
  111. {statable-3.2.1 → statable-3.2.2}/statable_gui/symbol_picker.py +0 -0
  112. {statable-3.2.1 → statable-3.2.2}/statable_gui/traceball.py +0 -0
  113. {statable-3.2.1 → statable-3.2.2}/statable_gui/transition_editor_direct/__init__.py +0 -0
  114. {statable-3.2.1 → statable-3.2.2}/statable_gui/transition_editor_direct/actions_tab.py +0 -0
  115. {statable-3.2.1 → statable-3.2.2}/statable_gui/transition_editor_direct/code_widget.py +0 -0
  116. {statable-3.2.1 → statable-3.2.2}/statable_gui/transition_editor_direct/coverage_analyzer.py +0 -0
  117. {statable-3.2.1 → statable-3.2.2}/statable_gui/transition_editor_direct/dialog.py +0 -0
  118. {statable-3.2.1 → statable-3.2.2}/statable_gui/transition_editor_direct/draft.py +0 -0
  119. {statable-3.2.1 → statable-3.2.2}/statable_gui/transition_editor_direct/edit_dialogs.py +0 -0
  120. {statable-3.2.1 → statable-3.2.2}/statable_gui/transition_editor_direct/overview_tab.py +0 -0
  121. {statable-3.2.1 → statable-3.2.2}/statable_gui/transition_editor_direct/palette_widget.py +0 -0
  122. {statable-3.2.1 → statable-3.2.2}/statable_gui/transition_editor_direct/relations_edit_dialog.py +0 -0
  123. {statable-3.2.1 → statable-3.2.2}/statable_gui/transition_editor_direct/relations_tab.py +0 -0
  124. {statable-3.2.1 → statable-3.2.2}/statable_gui/transition_editor_direct/system_global_dialog.py +0 -0
  125. {statable-3.2.1 → statable-3.2.2}/statable_gui/transition_editor_direct/transition_actions_dialog.py +0 -0
  126. {statable-3.2.1 → statable-3.2.2}/statable_gui/transition_editor_direct/transitions_tab.py +0 -0
  127. {statable-3.2.1 → statable-3.2.2}/statable_gui/validation_dialog.py +0 -0
  128. {statable-3.2.1 → statable-3.2.2}/statable_gui/widgets.py +0 -0
  129. {statable-3.2.1 → statable-3.2.2}/tests/test_readme_consistency.py +0 -0
  130. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_2_p1.py +0 -0
  131. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_2_p12_10.py +0 -0
  132. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_2_p12_2.py +0 -0
  133. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_2_p12_5.py +0 -0
  134. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_2_p12_6.py +0 -0
  135. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_2_p12_7.py +0 -0
  136. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_2_p12_8.py +0 -0
  137. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_2_p12_9.py +0 -0
  138. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_2_p2.py +0 -0
  139. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_2_p3.py +0 -0
  140. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_2_p4a.py +0 -0
  141. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_2_p4b.py +0 -0
  142. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_3_p1.py +0 -0
  143. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_4_p1_merge.py +0 -0
  144. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_5_p1.py +0 -0
  145. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_5_p10.py +0 -0
  146. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_5_p11.py +0 -0
  147. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_5_p2.py +0 -0
  148. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_5_p3.py +0 -0
  149. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_5_p4.py +0 -0
  150. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_5_p5.py +0 -0
  151. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_5_p6.py +0 -0
  152. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_5_p8.py +0 -0
  153. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_5_p9.py +0 -0
  154. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_6_p1.py +0 -0
  155. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_6_p2.py +0 -0
  156. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_6_p3.py +0 -0
  157. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_7_p3.py +0 -0
  158. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_7_p4.py +0 -0
  159. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_7_p5_validation.py +0 -0
  160. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_8_p1_ai_prompt.py +0 -0
  161. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_8_p2_response_parser.py +0 -0
  162. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_8_p3_response_validator.py +0 -0
  163. {statable-3.2.1 → statable-3.2.2}/tests/test_v2_8_p4_gui_integration.py +0 -0
  164. {statable-3.2.1 → statable-3.2.2}/tests/test_v3_0_g1_shared.py +0 -0
  165. {statable-3.2.1 → statable-3.2.2}/tests/test_v3_0_s4_docs.py +0 -0
  166. {statable-3.2.1 → statable-3.2.2}/tests/test_v3_2_s1_wizard.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: statable
3
- Version: 3.2.1
3
+ Version: 3.2.2
4
4
  Summary: MISRA C:2012-aware state machine design and C code generation for embedded systems
5
5
  Author: Hideki Akashi
6
6
  License-Expression: Apache-2.0
@@ -8,7 +8,7 @@ StaTable code generation layer
8
8
  so it is not re-exported at top level (to reduce startup time)
9
9
  """
10
10
 
11
- __version__ = "3.2.1"
11
+ __version__ = "3.2.2"
12
12
 
13
13
  __all__ = [
14
14
  'CCodeGenerator',
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
 
7
7
  [project]
8
8
  name = "statable"
9
- version = "3.2.1"
9
+ version = "3.2.2"
10
10
  description = "MISRA C:2012-aware state machine design and C code generation for embedded systems"
11
11
  readme = "README.md"
12
12
  requires-python = ">=3.10"
@@ -40,7 +40,7 @@ from .global_defs import (
40
40
  StructMemberDef,
41
41
  )
42
42
 
43
- __version__ = "3.2.1"
43
+ __version__ = "3.2.2"
44
44
 
45
45
  __all__ = [
46
46
  # model
@@ -27,7 +27,7 @@ import json
27
27
  import sys
28
28
  from pathlib import Path
29
29
 
30
- CLI_VERSION = "3.2.1"
30
+ CLI_VERSION = "3.2.2"
31
31
 
32
32
 
33
33
  def _err(msg: str) -> None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: statable
3
- Version: 3.2.1
3
+ Version: 3.2.2
4
4
  Summary: MISRA C:2012-aware state machine design and C code generation for embedded systems
5
5
  Author: Hideki Akashi
6
6
  License-Expression: Apache-2.0
@@ -160,4 +160,5 @@ tests/test_v3_0_s1_packaging.py
160
160
  tests/test_v3_0_s2_public_api.py
161
161
  tests/test_v3_0_s3_cli.py
162
162
  tests/test_v3_0_s4_docs.py
163
- tests/test_v3_2_s1_wizard.py
163
+ tests/test_v3_2_s1_wizard.py
164
+ tests/test_v3_2_s2_signals.py
@@ -334,7 +334,8 @@ class MainWindow(QMainWindow):
334
334
 
335
335
  open_btn = QAction(self.tr("Open"), self)
336
336
  open_btn.setToolTip(self.tr("Open project"))
337
- open_btn.triggered.connect(self.open_project)
337
+ # [v3.2.2 fix] lambda: avoid passing triggered(bool) as filepath
338
+ open_btn.triggered.connect(lambda: self.open_project())
338
339
  toolbar.addAction(open_btn)
339
340
 
340
341
  save_btn = QAction(self.tr("Save"), self)
@@ -3,11 +3,11 @@
3
3
  Phase S-1 (v3.0) tests: packaging foundation.
4
4
 
5
5
  Verifies:
6
- - code/pyproject.toml exists and declares statable/3.2.1
6
+ - code/pyproject.toml exists and declares statable/3.2.2
7
7
  - statable-cli entry point is defined
8
8
  - extras: gui / dev
9
- - statable.__version__ == "3.2.1"
10
- - codegen.__version__ == "3.2.1"
9
+ - statable.__version__ == "3.2.2"
10
+ - codegen.__version__ == "3.2.2"
11
11
  """
12
12
  from __future__ import annotations
13
13
 
@@ -64,8 +64,8 @@ def main() -> int:
64
64
  check("project.name == 'statable'",
65
65
  project.get("name") == "statable",
66
66
  f"got: {project.get('name')!r}")
67
- check("project.version == '3.2.1'",
68
- project.get("version") == "3.2.1",
67
+ check("project.version == '3.2.2'",
68
+ project.get("version") == "3.2.2",
69
69
  f"got: {project.get('version')!r}")
70
70
 
71
71
  # --- T4: requires-python ---------------------------------------
@@ -101,16 +101,16 @@ def main() -> int:
101
101
  # --- T8: runtime __version__ -----------------------------------
102
102
  try:
103
103
  import statable
104
- check("statable.__version__ == '3.2.1'",
105
- getattr(statable, "__version__", None) == "3.2.1",
104
+ check("statable.__version__ == '3.2.2'",
105
+ getattr(statable, "__version__", None) == "3.2.2",
106
106
  f"got: {getattr(statable, '__version__', None)!r}")
107
107
  except Exception as e:
108
108
  check("statable importable", False, repr(e))
109
109
 
110
110
  try:
111
111
  import codegen
112
- check("codegen.__version__ == '3.2.1'",
113
- getattr(codegen, "__version__", None) == "3.2.1",
112
+ check("codegen.__version__ == '3.2.2'",
113
+ getattr(codegen, "__version__", None) == "3.2.2",
114
114
  f"got: {getattr(codegen, '__version__', None)!r}")
115
115
  except Exception as e:
116
116
  check("codegen importable", False, repr(e))
@@ -47,8 +47,8 @@ def main() -> int:
47
47
  _summary()
48
48
  return 1
49
49
 
50
- check("statable has __version__ == '3.2.1'",
51
- getattr(statable, "__version__", None) == "3.2.1",
50
+ check("statable has __version__ == '3.2.2'",
51
+ getattr(statable, "__version__", None) == "3.2.2",
52
52
  f"got: {getattr(statable, '__version__', None)!r}")
53
53
  check("'__version__' in statable.__all__",
54
54
  "__version__" in statable.__all__,
@@ -69,8 +69,8 @@ def main() -> int:
69
69
  _summary()
70
70
  return 1
71
71
 
72
- check("codegen has __version__ == '3.2.1'",
73
- getattr(codegen, "__version__", None) == "3.2.1",
72
+ check("codegen has __version__ == '3.2.2'",
73
+ getattr(codegen, "__version__", None) == "3.2.2",
74
74
  f"got: {getattr(codegen, '__version__', None)!r}")
75
75
  check("'validate' in codegen.__all__",
76
76
  "validate" in codegen.__all__,
@@ -60,8 +60,8 @@ def main() -> int:
60
60
  try:
61
61
  from statable import cli
62
62
  check("statable.cli importable", True)
63
- check("CLI_VERSION == '3.2.1'",
64
- cli.CLI_VERSION == "3.2.1",
63
+ check("CLI_VERSION == '3.2.2'",
64
+ cli.CLI_VERSION == "3.2.2",
65
65
  f"got: {cli.CLI_VERSION!r}")
66
66
  except Exception as e:
67
67
  check("statable.cli importable", False, repr(e))
@@ -91,8 +91,8 @@ def main() -> int:
91
91
  # --- T3: version --------------------------------------------------
92
92
  rc, out, _ = run_cli(["version"])
93
93
  check("version: exit 0", rc == 0, f"rc={rc}")
94
- check("version: prints 'statable-cli 3.2.1'",
95
- "statable-cli 3.2.1" in out,
94
+ check("version: prints 'statable-cli 3.2.2'",
95
+ "statable-cli 3.2.2" in out,
96
96
  f"out={out!r}")
97
97
 
98
98
  # --- T4: validate on real XML (json) ------------------------------
@@ -0,0 +1,144 @@
1
+ # tests/test_v3_2_s2_signals.py
2
+ """Test Qt signal-slot safety (v3.2.2).
3
+
4
+ Regression test for the v3.2.0 bug where `open_project(filepath=None)`
5
+ received Qt's `triggered(bool)` argument, resulting in
6
+ `project_from_xml(False)` and a hang on `open(False, "rb")`.
7
+
8
+ Key insight: PySide6 auto-truncates args for slots with fixed signatures,
9
+ so most `triggered.connect(self.slot)` are safe. The bug only occurs
10
+ when the slot has an OPTIONAL parameter that receives the bool.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import os
15
+ import re
16
+ import sys
17
+ from pathlib import Path
18
+
19
+ os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
20
+ sys.path.insert(0, str(Path(__file__).parent.parent))
21
+
22
+
23
+ def main():
24
+ passed = 0
25
+ failed = 0
26
+
27
+ def check(name, cond):
28
+ nonlocal passed, failed
29
+ if cond:
30
+ passed += 1
31
+ print(f"[PASS] {name}")
32
+ else:
33
+ failed += 1
34
+ print(f"[FAIL] {name}")
35
+
36
+ # ================================================================
37
+ # T1: open_project normalizes bool arguments
38
+ # ================================================================
39
+ print("=" * 70)
40
+ print(" T1: open_project(bool) normalization")
41
+ print("=" * 70)
42
+
43
+ try:
44
+ from PySide6.QtWidgets import QApplication
45
+ app = QApplication.instance() or QApplication(sys.argv)
46
+
47
+ from statable_gui.main_window import MainWindow
48
+
49
+ win = MainWindow()
50
+
51
+ import statable_gui.main_window as mw_mod
52
+ original_pfx = mw_mod.project_from_xml
53
+ called_with = []
54
+
55
+ def fake_pfx(path):
56
+ called_with.append(path)
57
+ raise RuntimeError("TEST_STOP")
58
+
59
+ mw_mod.project_from_xml = fake_pfx
60
+
61
+ from PySide6.QtWidgets import QFileDialog
62
+ original_dialog = QFileDialog.getOpenFileName
63
+ QFileDialog.getOpenFileName = staticmethod(lambda *a, **k: ("", ""))
64
+
65
+ try:
66
+ win.open_project(False)
67
+ check("open_project(False) does not hang", True)
68
+ except Exception as e:
69
+ check(f"open_project(False) raised: {e}", False)
70
+
71
+ check("project_from_xml not called with False",
72
+ False not in called_with)
73
+
74
+ called_with.clear()
75
+ try:
76
+ win.open_project(True)
77
+ check("open_project(True) does not hang", True)
78
+ except Exception as e:
79
+ check(f"open_project(True) raised: {e}", False)
80
+ check("project_from_xml not called with True",
81
+ True not in called_with)
82
+
83
+ mw_mod.project_from_xml = original_pfx
84
+ QFileDialog.getOpenFileName = original_dialog
85
+
86
+ except Exception as e:
87
+ import traceback
88
+ print(traceback.format_exc())
89
+ check(f"T1 setup failed: {e}", False)
90
+
91
+ # ================================================================
92
+ # T2: open_project is connected via lambda (the actual bug)
93
+ # ================================================================
94
+ print()
95
+ print("=" * 70)
96
+ print(" T2: open_project is lambda-wrapped")
97
+ print("=" * 70)
98
+
99
+ mw_path = Path(__file__).parent.parent / "statable_gui" / "main_window.py"
100
+ src = mw_path.read_text(encoding="utf-8")
101
+
102
+ # The bug: `triggered.connect(self.open_project)` without lambda
103
+ # would pass triggered(bool) as filepath.
104
+ # FIX: must use `triggered.connect(lambda: self.open_project())`
105
+ bug_pattern = re.compile(
106
+ r'triggered\.connect\(\s*self\.open_project\s*\)'
107
+ )
108
+ check("open_project not connected without lambda",
109
+ not bug_pattern.search(src))
110
+
111
+ fix_pattern = re.compile(
112
+ r'triggered\.connect\(\s*lambda\s*:\s*self\.open_project\(\)\s*\)'
113
+ )
114
+ check("open_project connected via lambda",
115
+ bool(fix_pattern.search(src)))
116
+
117
+ # ================================================================
118
+ # T3: open_project has bool guard in body
119
+ # ================================================================
120
+ print()
121
+ print("=" * 70)
122
+ print(" T3: open_project has bool normalization guard")
123
+ print("=" * 70)
124
+
125
+ check("open_project has isinstance(filepath, bool) guard",
126
+ "isinstance(filepath, bool)" in src)
127
+
128
+ # Verify that the signature accepts Optional[str]
129
+ check("open_project signature has filepath parameter",
130
+ re.search(r'def open_project\(self,\s*filepath', src) is not None)
131
+
132
+ # ================================================================
133
+ # Summary
134
+ # ================================================================
135
+ print()
136
+ print("=" * 70)
137
+ print(f" TOTAL: {passed + failed} PASSED: {passed} FAILED: {failed}")
138
+ print("=" * 70)
139
+
140
+ return 0 if failed == 0 else 1
141
+
142
+
143
+ if __name__ == "__main__":
144
+ sys.exit(main())
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes