behavex 4.2.2__tar.gz → 4.2.4__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 (247) hide show
  1. {behavex-4.2.2/behavex.egg-info → behavex-4.2.4}/PKG-INFO +2 -2
  2. behavex-4.2.4/behavex/__pycache__/__init__.cpython-310.pyc +0 -0
  3. behavex-4.2.4/behavex/__pycache__/__main__.cpython-310.pyc +0 -0
  4. behavex-4.2.4/behavex/__pycache__/arguments.cpython-310.pyc +0 -0
  5. {behavex-4.2.2 → behavex-4.2.4}/behavex/__pycache__/arguments.cpython-39.pyc +0 -0
  6. behavex-4.2.4/behavex/__pycache__/conf_mgr.cpython-310.pyc +0 -0
  7. {behavex-4.2.2 → behavex-4.2.4}/behavex/__pycache__/conf_mgr.cpython-39.pyc +0 -0
  8. behavex-4.2.4/behavex/__pycache__/environment.cpython-310.pyc +0 -0
  9. {behavex-4.2.2 → behavex-4.2.4}/behavex/__pycache__/environment.cpython-39.pyc +0 -0
  10. behavex-4.2.4/behavex/__pycache__/execution_singleton.cpython-310.pyc +0 -0
  11. behavex-4.2.4/behavex/__pycache__/global_vars.cpython-310.pyc +0 -0
  12. behavex-4.2.4/behavex/__pycache__/progress_bar.cpython-310.pyc +0 -0
  13. behavex-4.2.4/behavex/__pycache__/runner.cpython-310.pyc +0 -0
  14. behavex-4.2.4/behavex/__pycache__/runner.cpython-39.pyc +0 -0
  15. behavex-4.2.4/behavex/__pycache__/utils.cpython-310.pyc +0 -0
  16. behavex-4.2.4/behavex/__pycache__/utils.cpython-39.pyc +0 -0
  17. {behavex-4.2.2 → behavex-4.2.4}/behavex/arguments.py +0 -1
  18. {behavex-4.2.2 → behavex-4.2.4}/behavex/conf_logging.cfg +2 -2
  19. {behavex-4.2.2 → behavex-4.2.4}/behavex/environment.py +9 -9
  20. behavex-4.2.4/behavex/outputs/__pycache__/__init__.cpython-310.pyc +0 -0
  21. behavex-4.2.4/behavex/outputs/__pycache__/formatter_manager.cpython-310.pyc +0 -0
  22. behavex-4.2.4/behavex/outputs/__pycache__/formatter_manager.cpython-39.pyc +0 -0
  23. behavex-4.2.4/behavex/outputs/__pycache__/jinja_mgr.cpython-310.pyc +0 -0
  24. behavex-4.2.4/behavex/outputs/__pycache__/output_strings.cpython-310.pyc +0 -0
  25. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/__pycache__/output_strings.cpython-39.pyc +0 -0
  26. behavex-4.2.4/behavex/outputs/__pycache__/report_html.cpython-310.pyc +0 -0
  27. behavex-4.2.4/behavex/outputs/__pycache__/report_json.cpython-310.pyc +0 -0
  28. behavex-4.2.4/behavex/outputs/__pycache__/report_utils.cpython-310.pyc +0 -0
  29. behavex-4.2.4/behavex/outputs/__pycache__/report_xml.cpython-310.pyc +0 -0
  30. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/formatter_manager.py +25 -22
  31. behavex-4.2.4/behavex/outputs/formatters/__pycache__/allure_behavex_formatter.cpython-310.pyc +0 -0
  32. behavex-4.2.4/behavex/outputs/formatters/__pycache__/allure_behavex_formatter.cpython-39.pyc +0 -0
  33. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/formatters/allure_behavex_formatter.py +22 -13
  34. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/output_strings.py +8 -5
  35. {behavex-4.2.2 → behavex-4.2.4}/behavex/runner.py +84 -28
  36. {behavex-4.2.2 → behavex-4.2.4}/behavex/utils.py +2 -8
  37. {behavex-4.2.2 → behavex-4.2.4/behavex.egg-info}/PKG-INFO +2 -2
  38. behavex-4.2.4/behavex.egg-info/SOURCES.txt +239 -0
  39. {behavex-4.2.2 → behavex-4.2.4}/behavex.egg-info/requires.txt +1 -1
  40. behavex-4.2.4/output/focused_test_run/outputs/logs/image_attachments_utils/__init__.py +4 -0
  41. behavex-4.2.4/output/full_test_run/outputs/logs/image_attachments_utils/__init__.py +4 -0
  42. behavex-4.2.4/output/image_test_verification/outputs/logs/image_attachments_utils/__init__.py +4 -0
  43. behavex-4.2.4/output/output_001349/outputs/logs/image_attachments_utils/__init__.py +4 -0
  44. behavex-4.2.4/output/output_003713/outputs/logs/image_attachments_utils/__init__.py +4 -0
  45. behavex-4.2.4/output/output_004697/outputs/logs/image_attachments_utils/__init__.py +4 -0
  46. behavex-4.2.4/output/output_006245/outputs/logs/image_attachments_utils/__init__.py +4 -0
  47. behavex-4.2.4/output/output_008581/outputs/logs/image_attachments_utils/__init__.py +4 -0
  48. behavex-4.2.4/output/output_014810/outputs/logs/image_attachments_utils/__init__.py +4 -0
  49. behavex-4.2.4/output/output_034257/outputs/logs/image_attachments_utils/__init__.py +4 -0
  50. behavex-4.2.4/output/output_049842/outputs/logs/image_attachments_utils/__init__.py +4 -0
  51. behavex-4.2.4/output/output_057264/outputs/logs/image_attachments_utils/__init__.py +4 -0
  52. behavex-4.2.4/output/output_065267/outputs/logs/image_attachments_utils/__init__.py +4 -0
  53. behavex-4.2.4/output/output_071651/outputs/logs/image_attachments_utils/__init__.py +4 -0
  54. behavex-4.2.4/output/output_080858/outputs/logs/image_attachments_utils/__init__.py +4 -0
  55. behavex-4.2.4/output/output_107483/outputs/logs/image_attachments_utils/__init__.py +4 -0
  56. behavex-4.2.4/output/output_116700/outputs/logs/image_attachments_utils/__init__.py +4 -0
  57. behavex-4.2.4/output/output_120092/outputs/logs/image_attachments_utils/__init__.py +4 -0
  58. behavex-4.2.4/output/output_140259/outputs/logs/image_attachments_utils/__init__.py +4 -0
  59. behavex-4.2.4/output/output_143213/outputs/logs/image_attachments_utils/__init__.py +4 -0
  60. behavex-4.2.4/output/output_146320/outputs/logs/image_attachments_utils/__init__.py +4 -0
  61. behavex-4.2.4/output/output_146403/outputs/logs/image_attachments_utils/__init__.py +4 -0
  62. behavex-4.2.4/output/output_154070/outputs/logs/image_attachments_utils/__init__.py +4 -0
  63. behavex-4.2.4/output/output_159339/outputs/logs/image_attachments_utils/__init__.py +4 -0
  64. behavex-4.2.4/output/output_164111/outputs/logs/image_attachments_utils/__init__.py +4 -0
  65. behavex-4.2.4/output/output_188075/outputs/logs/image_attachments_utils/__init__.py +4 -0
  66. behavex-4.2.4/output/output_189804/outputs/logs/image_attachments_utils/__init__.py +4 -0
  67. behavex-4.2.4/output/output_195102/outputs/logs/image_attachments_utils/__init__.py +4 -0
  68. behavex-4.2.4/output/output_206620/outputs/logs/image_attachments_utils/__init__.py +4 -0
  69. behavex-4.2.4/output/output_238600/outputs/logs/image_attachments_utils/__init__.py +4 -0
  70. behavex-4.2.4/output/output_251312/outputs/logs/image_attachments_utils/__init__.py +4 -0
  71. behavex-4.2.4/output/output_260546/outputs/logs/image_attachments_utils/__init__.py +4 -0
  72. behavex-4.2.4/output/output_288178/outputs/logs/image_attachments_utils/__init__.py +4 -0
  73. behavex-4.2.4/output/output_304378/outputs/logs/image_attachments_utils/__init__.py +4 -0
  74. behavex-4.2.4/output/output_323270/outputs/logs/image_attachments_utils/__init__.py +4 -0
  75. behavex-4.2.4/output/output_328507/outputs/logs/image_attachments_utils/__init__.py +4 -0
  76. behavex-4.2.4/output/output_344685/outputs/logs/image_attachments_utils/__init__.py +4 -0
  77. behavex-4.2.4/output/output_344776/outputs/logs/image_attachments_utils/__init__.py +4 -0
  78. behavex-4.2.4/output/output_344852/outputs/logs/image_attachments_utils/__init__.py +4 -0
  79. behavex-4.2.4/output/output_348748/outputs/logs/image_attachments_utils/__init__.py +4 -0
  80. behavex-4.2.4/output/output_372668/outputs/logs/image_attachments_utils/__init__.py +4 -0
  81. behavex-4.2.4/output/output_376424/outputs/logs/image_attachments_utils/__init__.py +4 -0
  82. behavex-4.2.4/output/output_382185/outputs/logs/image_attachments_utils/__init__.py +4 -0
  83. behavex-4.2.4/output/output_405504/outputs/logs/image_attachments_utils/__init__.py +4 -0
  84. behavex-4.2.4/output/output_422012/outputs/logs/image_attachments_utils/__init__.py +4 -0
  85. behavex-4.2.4/output/output_423021/outputs/logs/image_attachments_utils/__init__.py +4 -0
  86. behavex-4.2.4/output/output_426722/outputs/logs/image_attachments_utils/__init__.py +4 -0
  87. behavex-4.2.4/output/output_434999/outputs/logs/image_attachments_utils/__init__.py +4 -0
  88. behavex-4.2.4/output/output_435402/outputs/logs/image_attachments_utils/__init__.py +4 -0
  89. behavex-4.2.4/output/output_440809/outputs/logs/image_attachments_utils/__init__.py +4 -0
  90. behavex-4.2.4/output/output_449330/outputs/logs/image_attachments_utils/__init__.py +4 -0
  91. behavex-4.2.4/output/output_450637/outputs/logs/image_attachments_utils/__init__.py +4 -0
  92. behavex-4.2.4/output/output_450881/outputs/logs/image_attachments_utils/__init__.py +4 -0
  93. behavex-4.2.4/output/output_465338/outputs/logs/image_attachments_utils/__init__.py +4 -0
  94. behavex-4.2.4/output/output_475651/outputs/logs/image_attachments_utils/__init__.py +4 -0
  95. behavex-4.2.4/output/output_479506/outputs/logs/image_attachments_utils/__init__.py +4 -0
  96. behavex-4.2.4/output/output_489113/outputs/logs/image_attachments_utils/__init__.py +4 -0
  97. behavex-4.2.4/output/output_495088/outputs/logs/image_attachments_utils/__init__.py +4 -0
  98. behavex-4.2.4/output/output_496889/outputs/logs/image_attachments_utils/__init__.py +4 -0
  99. behavex-4.2.4/output/output_497394/outputs/logs/image_attachments_utils/__init__.py +4 -0
  100. behavex-4.2.4/output/output_540969/outputs/logs/image_attachments_utils/__init__.py +4 -0
  101. behavex-4.2.4/output/output_574360/outputs/logs/image_attachments_utils/__init__.py +4 -0
  102. behavex-4.2.4/output/output_577418/outputs/logs/image_attachments_utils/__init__.py +4 -0
  103. behavex-4.2.4/output/output_579891/outputs/logs/image_attachments_utils/__init__.py +4 -0
  104. behavex-4.2.4/output/output_588144/outputs/logs/image_attachments_utils/__init__.py +4 -0
  105. behavex-4.2.4/output/output_590886/outputs/logs/image_attachments_utils/__init__.py +4 -0
  106. behavex-4.2.4/output/output_596667/outputs/logs/image_attachments_utils/__init__.py +4 -0
  107. behavex-4.2.4/output/output_601328/outputs/logs/image_attachments_utils/__init__.py +4 -0
  108. behavex-4.2.4/output/output_602191/outputs/logs/image_attachments_utils/__init__.py +4 -0
  109. behavex-4.2.4/output/output_631745/outputs/logs/image_attachments_utils/__init__.py +4 -0
  110. behavex-4.2.4/output/output_634385/outputs/logs/image_attachments_utils/__init__.py +4 -0
  111. behavex-4.2.4/output/output_646388/outputs/logs/image_attachments_utils/__init__.py +4 -0
  112. behavex-4.2.4/output/output_654650/outputs/logs/image_attachments_utils/__init__.py +4 -0
  113. behavex-4.2.4/output/output_656366/outputs/logs/image_attachments_utils/__init__.py +4 -0
  114. behavex-4.2.4/output/output_678770/outputs/logs/image_attachments_utils/__init__.py +4 -0
  115. behavex-4.2.4/output/output_680470/outputs/logs/image_attachments_utils/__init__.py +4 -0
  116. behavex-4.2.4/output/output_683436/outputs/logs/image_attachments_utils/__init__.py +4 -0
  117. behavex-4.2.4/output/output_685176/outputs/logs/image_attachments_utils/__init__.py +4 -0
  118. behavex-4.2.4/output/output_689341/outputs/logs/image_attachments_utils/__init__.py +4 -0
  119. behavex-4.2.4/output/output_691007/outputs/logs/image_attachments_utils/__init__.py +4 -0
  120. behavex-4.2.4/output/output_695521/outputs/logs/image_attachments_utils/__init__.py +4 -0
  121. behavex-4.2.4/output/output_703468/outputs/logs/image_attachments_utils/__init__.py +4 -0
  122. behavex-4.2.4/output/output_704571/outputs/logs/image_attachments_utils/__init__.py +4 -0
  123. behavex-4.2.4/output/output_710568/outputs/logs/image_attachments_utils/__init__.py +4 -0
  124. behavex-4.2.4/output/output_711677/outputs/logs/image_attachments_utils/__init__.py +4 -0
  125. behavex-4.2.4/output/output_719414/outputs/logs/image_attachments_utils/__init__.py +4 -0
  126. behavex-4.2.4/output/output_723957/outputs/logs/image_attachments_utils/__init__.py +4 -0
  127. behavex-4.2.4/output/output_724106/outputs/logs/image_attachments_utils/__init__.py +4 -0
  128. behavex-4.2.4/output/output_725084/outputs/logs/image_attachments_utils/__init__.py +4 -0
  129. behavex-4.2.4/output/output_732485/outputs/logs/image_attachments_utils/__init__.py +4 -0
  130. behavex-4.2.4/output/output_732517/outputs/logs/image_attachments_utils/__init__.py +4 -0
  131. behavex-4.2.4/output/output_742134/outputs/logs/image_attachments_utils/__init__.py +4 -0
  132. behavex-4.2.4/output/output_756176/outputs/logs/image_attachments_utils/__init__.py +4 -0
  133. behavex-4.2.4/output/output_767286/outputs/logs/image_attachments_utils/__init__.py +4 -0
  134. behavex-4.2.4/output/output_773557/outputs/logs/image_attachments_utils/__init__.py +4 -0
  135. behavex-4.2.4/output/output_787703/outputs/logs/image_attachments_utils/__init__.py +4 -0
  136. behavex-4.2.4/output/output_798702/outputs/logs/image_attachments_utils/__init__.py +4 -0
  137. behavex-4.2.4/output/output_803377/outputs/logs/image_attachments_utils/__init__.py +4 -0
  138. behavex-4.2.4/output/output_818000/outputs/logs/image_attachments_utils/__init__.py +4 -0
  139. behavex-4.2.4/output/output_821244/outputs/logs/image_attachments_utils/__init__.py +4 -0
  140. behavex-4.2.4/output/output_841973/outputs/logs/image_attachments_utils/__init__.py +4 -0
  141. behavex-4.2.4/output/output_843772/outputs/logs/image_attachments_utils/__init__.py +4 -0
  142. behavex-4.2.4/output/output_851821/outputs/logs/image_attachments_utils/__init__.py +4 -0
  143. behavex-4.2.4/output/output_852322/outputs/logs/image_attachments_utils/__init__.py +4 -0
  144. behavex-4.2.4/output/output_857146/outputs/logs/image_attachments_utils/__init__.py +4 -0
  145. behavex-4.2.4/output/output_858906/outputs/logs/image_attachments_utils/__init__.py +4 -0
  146. behavex-4.2.4/output/output_867706/outputs/logs/image_attachments_utils/__init__.py +4 -0
  147. behavex-4.2.4/output/output_868972/outputs/logs/image_attachments_utils/__init__.py +4 -0
  148. behavex-4.2.4/output/output_886897/outputs/logs/image_attachments_utils/__init__.py +4 -0
  149. behavex-4.2.4/output/output_891058/outputs/logs/image_attachments_utils/__init__.py +4 -0
  150. behavex-4.2.4/output/output_892919/outputs/logs/image_attachments_utils/__init__.py +4 -0
  151. behavex-4.2.4/output/output_907134/outputs/logs/image_attachments_utils/__init__.py +4 -0
  152. behavex-4.2.4/output/output_909239/outputs/logs/image_attachments_utils/__init__.py +4 -0
  153. behavex-4.2.4/output/output_932996/outputs/logs/image_attachments_utils/__init__.py +4 -0
  154. behavex-4.2.4/output/output_936717/outputs/logs/image_attachments_utils/__init__.py +4 -0
  155. behavex-4.2.4/output/output_940684/outputs/logs/image_attachments_utils/__init__.py +4 -0
  156. behavex-4.2.4/output/output_945860/outputs/logs/image_attachments_utils/__init__.py +4 -0
  157. behavex-4.2.4/output/output_947760/outputs/logs/image_attachments_utils/__init__.py +4 -0
  158. behavex-4.2.4/output/output_952517/outputs/logs/image_attachments_utils/__init__.py +4 -0
  159. behavex-4.2.4/output/output_955858/outputs/logs/image_attachments_utils/__init__.py +4 -0
  160. behavex-4.2.4/output/output_959703/outputs/logs/image_attachments_utils/__init__.py +4 -0
  161. behavex-4.2.4/output/output_969405/outputs/logs/image_attachments_utils/__init__.py +4 -0
  162. behavex-4.2.4/output/output_972545/outputs/logs/image_attachments_utils/__init__.py +4 -0
  163. behavex-4.2.4/output/outputs/logs/image_attachments_utils/__init__.py +4 -0
  164. {behavex-4.2.2 → behavex-4.2.4}/pyproject.toml +2 -2
  165. {behavex-4.2.2 → behavex-4.2.4}/setup.py +2 -2
  166. behavex-4.2.4/tests/features/environment.py +13 -0
  167. behavex-4.2.4/tests/features/secondary_features/environment.py +16 -0
  168. {behavex-4.2.2 → behavex-4.2.4}/tests/features/secondary_features/steps/generic_secondary_steps.py +39 -0
  169. {behavex-4.2.2 → behavex-4.2.4}/tests/features/steps/execution_steps.py +114 -0
  170. behavex-4.2.2/behavex/__pycache__/runner.cpython-39.pyc +0 -0
  171. behavex-4.2.2/behavex/__pycache__/utils.cpython-39.pyc +0 -0
  172. behavex-4.2.2/behavex/outputs/__pycache__/formatter_manager.cpython-39.pyc +0 -0
  173. behavex-4.2.2/behavex/outputs/formatters/__pycache__/allure_behavex_formatter.cpython-39.pyc +0 -0
  174. behavex-4.2.2/behavex.egg-info/SOURCES.txt +0 -96
  175. behavex-4.2.2/tests/features/environment.py +0 -3
  176. behavex-4.2.2/tests/features/secondary_features/environment.py +0 -6
  177. {behavex-4.2.2 → behavex-4.2.4}/LICENSE +0 -0
  178. {behavex-4.2.2 → behavex-4.2.4}/MANIFEST.in +0 -0
  179. {behavex-4.2.2 → behavex-4.2.4}/README.md +0 -0
  180. {behavex-4.2.2 → behavex-4.2.4}/behavex/.DS_Store +0 -0
  181. {behavex-4.2.2 → behavex-4.2.4}/behavex/__init__.py +0 -0
  182. {behavex-4.2.2 → behavex-4.2.4}/behavex/__main__.py +0 -0
  183. {behavex-4.2.2 → behavex-4.2.4}/behavex/__pycache__/__init__.cpython-39.pyc +0 -0
  184. {behavex-4.2.2 → behavex-4.2.4}/behavex/__pycache__/__main__.cpython-39.pyc +0 -0
  185. {behavex-4.2.2 → behavex-4.2.4}/behavex/__pycache__/execution_singleton.cpython-39.pyc +0 -0
  186. {behavex-4.2.2 → behavex-4.2.4}/behavex/__pycache__/global_vars.cpython-39.pyc +0 -0
  187. {behavex-4.2.2 → behavex-4.2.4}/behavex/__pycache__/progress_bar.cpython-39.pyc +0 -0
  188. {behavex-4.2.2 → behavex-4.2.4}/behavex/conf_behavex.cfg +0 -0
  189. {behavex-4.2.2 → behavex-4.2.4}/behavex/conf_mgr.py +0 -0
  190. {behavex-4.2.2 → behavex-4.2.4}/behavex/execution_singleton.py +0 -0
  191. {behavex-4.2.2 → behavex-4.2.4}/behavex/global_vars.py +0 -0
  192. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/__init__.py +0 -0
  193. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/__pycache__/__init__.cpython-39.pyc +0 -0
  194. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/__pycache__/jinja_mgr.cpython-39.pyc +0 -0
  195. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/__pycache__/report_html.cpython-39.pyc +0 -0
  196. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/__pycache__/report_json.cpython-39.pyc +0 -0
  197. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/__pycache__/report_utils.cpython-39.pyc +0 -0
  198. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/__pycache__/report_xml.cpython-39.pyc +0 -0
  199. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/bootstrap/css/behavex.css +0 -0
  200. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/bootstrap/css/bootstrap-theme.css +0 -0
  201. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/bootstrap/css/bootstrap-theme.min.css +0 -0
  202. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/bootstrap/css/bootstrap-theme.min.css.map +0 -0
  203. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/bootstrap/css/bootstrap.css +0 -0
  204. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/bootstrap/css/bootstrap.min.css +0 -0
  205. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/bootstrap/css/bootstrap.min.css.map +0 -0
  206. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/bootstrap/css/pace-theme-loading-bar.min.css +0 -0
  207. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/bootstrap/fonts/glyphicons-halflings-regular.eot +0 -0
  208. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/bootstrap/fonts/glyphicons-halflings-regular.svg +0 -0
  209. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/bootstrap/fonts/glyphicons-halflings-regular.ttf +0 -0
  210. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/bootstrap/fonts/glyphicons-halflings-regular.woff +0 -0
  211. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/bootstrap/fonts/glyphicons-halflings-regular.woff2 +0 -0
  212. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/bootstrap/js/bootstrap.js +0 -0
  213. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/bootstrap/js/bootstrap.min.js +0 -0
  214. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/bootstrap/js/chart.js +0 -0
  215. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/bootstrap/js/jquery-1.11.0.min.js +0 -0
  216. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/bootstrap/js/jquery-ui.min.js +0 -0
  217. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/bootstrap/js/jquery.tablesorter.min.js +0 -0
  218. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/bootstrap/js/jquery.tooltipster.min.js +0 -0
  219. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/bootstrap/js/pace.min.js +0 -0
  220. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/jinja/base.jinja2 +0 -0
  221. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/jinja/main.jinja2 +0 -0
  222. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/jinja/manifest.jinja2 +0 -0
  223. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/jinja/xml.jinja2 +0 -0
  224. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/jinja/xml_json.jinja2 +0 -0
  225. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/jinja_mgr.py +0 -0
  226. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/report_html.py +0 -0
  227. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/report_json.py +0 -0
  228. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/report_utils.py +0 -0
  229. {behavex-4.2.2 → behavex-4.2.4}/behavex/outputs/report_xml.py +0 -0
  230. {behavex-4.2.2 → behavex-4.2.4}/behavex/progress_bar.py +0 -0
  231. {behavex-4.2.2 → behavex-4.2.4}/behavex/steps/__init__.py +0 -0
  232. {behavex-4.2.2 → behavex-4.2.4}/behavex.egg-info/dependency_links.txt +0 -0
  233. {behavex-4.2.2 → behavex-4.2.4}/behavex.egg-info/entry_points.txt +0 -0
  234. {behavex-4.2.2 → behavex-4.2.4}/behavex.egg-info/top_level.txt +0 -0
  235. {behavex-4.2.2 → behavex-4.2.4}/docs/conf.py +0 -0
  236. {behavex-4.2.2 → behavex-4.2.4}/setup.cfg +0 -0
  237. {behavex-4.2.2 → behavex-4.2.4}/tests/features/crashing_features/environment.py +0 -0
  238. {behavex-4.2.2 → behavex-4.2.4}/tests/features/crashing_features/steps/__init__.py +0 -0
  239. {behavex-4.2.2 → behavex-4.2.4}/tests/features/crashing_features/steps/crashing_steps.py +0 -0
  240. {behavex-4.2.2 → behavex-4.2.4}/tests/features/secondary_features/__init__.py +0 -0
  241. {behavex-4.2.2 → behavex-4.2.4}/tests/features/secondary_features/steps/__init__.py +0 -0
  242. {behavex-4.2.2 → behavex-4.2.4}/tests/features/secondary_features/steps/allure_formatter_secondary_steps.py +0 -0
  243. {behavex-4.2.2 → behavex-4.2.4}/tests/features/steps/__init__.py +0 -0
  244. {behavex-4.2.2 → behavex-4.2.4}/tests/features/steps/allure_formatter_steps.py +0 -0
  245. {behavex-4.2.2 → behavex-4.2.4}/tests/features/steps/allure_formatter_unit_steps.py +0 -0
  246. {behavex-4.2.2 → behavex-4.2.4}/tests/features/steps/install_steps.py +0 -0
  247. {behavex-4.2.2 → behavex-4.2.4}/venv/bin/activate_this.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: behavex
3
- Version: 4.2.2
3
+ Version: 4.2.4
4
4
  Summary: Agile testing framework on top of Behave (BDD).
5
5
  Home-page: https://github.com/hrcorval/behavex
6
6
  Author: Hernan Rey
@@ -29,7 +29,7 @@ Requires-Python: >=3.5
29
29
  Description-Content-Type: text/markdown
30
30
  License-File: LICENSE
31
31
  Requires-Dist: behave==1.2.6
32
- Requires-Dist: behavex-images>=3.0.10
32
+ Requires-Dist: behavex-images>=3.2.1
33
33
  Requires-Dist: allure-python-commons
34
34
  Requires-Dist: jinja2
35
35
  Requires-Dist: configobj
@@ -333,7 +333,6 @@ def parse_arguments(args):
333
333
  '--formatter-outdir',
334
334
  help="Specify the output directory for the custom formatter relative to the output folder",
335
335
  metavar='DIR',
336
- default='report_artifacts',
337
336
  required=False,
338
337
  )
339
338
 
@@ -11,12 +11,12 @@ keys=simpleFormatter
11
11
  encoding=utf-8-sig
12
12
 
13
13
  [logger_root]
14
- level=DEBUG
14
+ level=INFO
15
15
  handlers=consoleHandler
16
16
 
17
17
  [handler_consoleHandler]
18
18
  class=StreamHandler
19
- level=DEBUG
19
+ level=INFO
20
20
  encoding=utf-8-sig
21
21
  formatter=simpleFormatter
22
22
  args=(sys.stdout, )
@@ -11,18 +11,18 @@ from datetime import datetime
11
11
 
12
12
  from behave.contrib.scenario_autoretry import patch_scenario_with_autoretry
13
13
  from behave.log_capture import capture
14
- from behave.model import ScenarioOutline
14
+ from behave.model import ScenarioOutline # pyright: ignore[reportUnusedImport]
15
15
  from behave.runner import Context, ModelRunner
16
16
 
17
17
  from behavex import conf_mgr
18
18
  from behavex.conf_mgr import get_env, get_param
19
19
  from behavex.global_vars import global_vars
20
20
  from behavex.outputs import report_json, report_xml
21
- from behavex.outputs.report_utils import create_log_path, strip_ansi_codes
21
+ from behavex.outputs.report_utils import (create_log_path, get_string_hash,
22
+ strip_ansi_codes)
22
23
  from behavex.utils import (LOGGING_CFG, create_custom_log_when_called,
23
24
  get_autoretry_attempts, get_logging_level,
24
- get_scenario_tags, get_scenarios_instances,
25
- get_string_hash)
25
+ get_scenario_tags, get_scenarios_instances)
26
26
 
27
27
  Context.__getattribute__ = create_custom_log_when_called
28
28
 
@@ -186,19 +186,19 @@ def before_scenario(context, scenario):
186
186
  scenario.start = _get_current_timestamp_ms()
187
187
 
188
188
 
189
- def before_step(context, step):
189
+ def before_step(context, step): # pyright: ignore[reportUnusedParameter]
190
190
  step.start = _get_current_timestamp_ms()
191
191
 
192
192
 
193
- def before_tag(context, tag):
193
+ def before_tag(context, tag): # pyright: ignore[reportUnusedParameter]
194
194
  pass
195
195
 
196
196
 
197
- def after_tag(context, tag):
197
+ def after_tag(context, tag): # pyright: ignore[reportUnusedParameter]
198
198
  pass
199
199
 
200
200
 
201
- def after_step(context, step):
201
+ def after_step(context, step): # pyright: ignore[reportUnusedParameter]
202
202
  step.stop = _get_current_timestamp_ms()
203
203
  try:
204
204
  if step.exception:
@@ -230,7 +230,7 @@ def after_scenario(context, scenario):
230
230
 
231
231
 
232
232
  # noinspection PyUnusedLocal
233
- def after_feature(context, feature):
233
+ def after_feature(context, feature): # pyright: ignore[reportUnusedParameter]
234
234
  try:
235
235
  if get_env('multiprocessing') and get_param('parallel_scheme') == 'scenario':
236
236
  return
@@ -9,16 +9,20 @@ Formatter manager module to handle custom formatters.
9
9
  import importlib
10
10
  import os
11
11
  import sys
12
- from typing import Optional, Type
12
+ from typing import (Any, Dict, Optional, # pyright: ignore[reportDeprecated]
13
+ Type)
13
14
 
14
15
  from behavex.conf_mgr import get_env, get_param
15
16
 
17
+ # Formatter Constants
18
+ DEFAULT_FORMATTER_DIR = 'report-artifacts'
19
+
16
20
 
17
21
  class FormatterManager:
18
22
  """Manager class for handling custom formatters in BehaveX."""
19
23
 
20
24
  @staticmethod
21
- def load_formatter(formatter_spec: str) -> Optional[Type]:
25
+ def load_formatter(formatter_spec: str) -> Optional[Type[Any]]: #pyright: ignore[reportDeprecated]
22
26
  """
23
27
  Load a formatter class from a module path specification.
24
28
 
@@ -64,25 +68,27 @@ class FormatterManager:
64
68
  return None
65
69
 
66
70
  @staticmethod
67
- def get_formatter_output_dir() -> str:
71
+ def get_formatter_output_dir(formatter_spec: str) -> Optional[str]: #pyright: ignore[reportDeprecated]
68
72
  """
69
- Get the output directory for the custom formatter.
73
+ Get the preferred output directory from a formatter class.
74
+
75
+ Args:
76
+ formatter_spec: String in format "module_path:FormatterClass"
70
77
 
71
78
  Returns:
72
- Absolute path to the formatter output directory, or None if no custom directory is specified
79
+ The formatter's preferred output directory, or None if not found
73
80
  """
74
- base_output = get_env('OUTPUT', 'output')
75
- formatter_outdir = get_param('formatter_outdir', '')
76
-
77
- # Return None if no custom formatter output directory is specified
78
- # This allows formatters to use their own default directories
79
- if not formatter_outdir or formatter_outdir == 'report_artifacts':
80
- return None
81
+ try:
82
+ formatter_class = FormatterManager.load_formatter(formatter_spec)
83
+ if formatter_class and hasattr(formatter_class, 'DEFAULT_OUTPUT_DIR'):
84
+ return formatter_class.DEFAULT_OUTPUT_DIR
85
+ except Exception as e:
86
+ print(f"Warning: Could not load formatter {formatter_spec} to get output directory: {str(e)}")
81
87
 
82
- return os.path.join(base_output, formatter_outdir)
88
+ return None
83
89
 
84
90
  @staticmethod
85
- def format_results(json_output: dict) -> None:
91
+ def format_results(json_output: Dict[str, Any]) -> None: #pyright: ignore[reportDeprecated]
86
92
  """
87
93
  Format test results using the specified custom formatter.
88
94
 
@@ -97,20 +103,17 @@ class FormatterManager:
97
103
  if not formatter_class:
98
104
  return
99
105
 
100
- output_dir = FormatterManager.get_formatter_output_dir()
101
-
102
- # Create the output directory only if a custom one is specified
103
- if output_dir:
104
- os.makedirs(output_dir, exist_ok=True)
106
+ # The formatter will handle its own output directory setup
107
+ # No need to create directories here since formatters manage their own paths
105
108
 
106
109
  try:
107
110
  formatter = formatter_class()
108
111
  # Check if the formatter has the expected method
109
- if not hasattr(formatter, 'parse_json_to_allure'):
110
- print(f"Formatter {formatter_class.__name__} does not implement required method 'parse_json_to_allure'")
112
+ if not hasattr(formatter, 'launch_json_formatter'):
113
+ print(f"Formatter {formatter_class.__name__} does not implement required method 'launch_json_formatter'")
111
114
  return
112
115
 
113
- formatter.parse_json_to_allure(json_output)
116
+ formatter.launch_json_formatter(json_output)
114
117
  except Exception as e:
115
118
  print(f"Error running formatter {formatter_spec}: {str(e)}")
116
119
  import traceback
@@ -31,7 +31,6 @@ import os
31
31
  import re
32
32
  import sys
33
33
  import uuid
34
- from datetime import datetime
35
34
  from pathlib import Path
36
35
 
37
36
  from allure_commons.model2 import (Attachment, Parameter, TestResult,
@@ -40,13 +39,15 @@ from allure_commons.types import AttachmentType
40
39
  from allure_commons.utils import now
41
40
 
42
41
  from behavex.conf_mgr import get_env, get_param
43
- from behavex.outputs.formatter_manager import FormatterManager
44
42
  from behavex.outputs.report_utils import get_string_hash
45
43
 
46
44
 
47
45
  class AllureBehaveXFormatter:
48
46
  """Allure formatter for BehaveX test results."""
49
47
 
48
+ # Default output directory for this formatter
49
+ DEFAULT_OUTPUT_DIR = 'allure-results'
50
+
50
51
  def _get_step_line_from_image(self, filename):
51
52
  """Extract step line number from image filename.
52
53
 
@@ -243,7 +244,11 @@ class AllureBehaveXFormatter:
243
244
  TestResult: The updated test case with evidence attachments
244
245
  """
245
246
 
246
- evidence_dir = os.path.join(get_env('logs', 'output/allure-results'), str(scenario_hash), 'evidence')
247
+ logs_dir = get_env('logs')
248
+ if logs_dir is None:
249
+ # Handle standalone script execution case
250
+ logs_dir = os.path.join(os.path.dirname(output_dir), 'outputs', 'logs')
251
+ evidence_dir = os.path.join(logs_dir, str(scenario_hash), 'evidence')
247
252
 
248
253
  if not os.path.exists(evidence_dir):
249
254
  return test_case
@@ -297,19 +302,20 @@ class AllureBehaveXFormatter:
297
302
 
298
303
  return test_case
299
304
 
300
- def parse_json_to_allure(self, json_data):
305
+ def launch_json_formatter(self, json_data):
301
306
  """
302
- Parses the JSON report data and converts it into Allure-compatible results.
307
+ Generic method to launch the formatter and process JSON test results.
303
308
 
304
309
  Args:
305
310
  json_data (dict): Dictionary containing the test results data.
306
311
  """
307
- # Get the formatter output directory from FormatterManager
308
- formatter_output_dir = FormatterManager.get_formatter_output_dir()
312
+ # Use the LOGS environment variable which is set consistently by the runner
313
+ # This ensures evidence, logs, and allure results are all in the same location
314
+ output_dir = get_env('logs')
309
315
 
310
- # If a custom output directory is specified, use it.
311
- # Otherwise, default to 'output/allure-results'.
312
- output_dir = formatter_output_dir or os.path.join(get_env('OUTPUT', 'output'), 'allure-results')
316
+ # Fallback if LOGS is not set (for standalone script execution)
317
+ if not output_dir:
318
+ output_dir = os.path.join(get_env('OUTPUT') or 'output', self.DEFAULT_OUTPUT_DIR)
313
319
 
314
320
  os.makedirs(output_dir, exist_ok=True)
315
321
 
@@ -487,7 +493,11 @@ class AllureBehaveXFormatter:
487
493
  # Process scenario logs as attachment
488
494
  if get_param('formatter_attach_logs'):
489
495
  scenario_hash = scenario.get('identifier_hash', get_string_hash(f"{str(feature['filename'])}-{str(scenario['line'])}"))
490
- log_path = os.path.join(get_env('logs', 'output/allure-results'), str(scenario_hash), 'scenario.log')
496
+ logs_dir_for_log = get_env('logs')
497
+ if logs_dir_for_log is None:
498
+ # Handle standalone script execution case
499
+ logs_dir_for_log = os.path.join(os.path.dirname(output_dir), 'outputs', 'logs')
500
+ log_path = os.path.join(logs_dir_for_log, str(scenario_hash), 'scenario.log')
491
501
  if os.path.exists(log_path):
492
502
  # Use relative path for the source if possible to avoid duplication
493
503
  rel_path = os.path.relpath(log_path, output_dir) if output_dir in log_path else log_path
@@ -637,7 +647,6 @@ class AllureBehaveXFormatter:
637
647
  with open(os.path.join(output_dir, "environment-categories.json"), "w") as f:
638
648
  json.dump(env_categories, f, indent=2)
639
649
 
640
-
641
650
  def main():
642
651
  parser = argparse.ArgumentParser(description='Parse BehaveX report.json into Allure format')
643
652
  parser.add_argument('--report-path',
@@ -651,7 +660,7 @@ def main():
651
660
 
652
661
  try:
653
662
  with open(args.report_path) as f:
654
- formatter.parse_json_to_allure(json.load(f))
663
+ formatter.launch_json_formatter(json.load(f))
655
664
  except FileNotFoundError:
656
665
  print(f"Error: Could not find report file at {args.report_path}")
657
666
  sys.exit(1)
@@ -45,8 +45,8 @@ TEXTS = {
45
45
  'description': '',
46
46
  'modal': {
47
47
  'title': '',
48
- 'body': '<b>Test Automation Rate:</b> % of automated scenarios.<br>'
49
- '<b>Pass Rate:</b>: % of passed scenarios.<br>',
48
+ 'body': ('<b>Test Automation Rate:</b> % of automated scenarios.<br>'
49
+ '<b>Pass Rate:</b>: % of passed scenarios.<br>'),
50
50
  },
51
51
  },
52
52
  'joined': {'title': '', 'description': '', 'modal': {'title': '', 'modal': 'the'}},
@@ -61,9 +61,9 @@ TEXTS = {
61
61
  'serial_execution': u'\n{0}\nRunning serial scenarios (tagged as @SERIAL).\n{0}\n'.format('*' * 60),
62
62
  'running_parallels': u'\n{0}\nRunning parallel scenarios\n{0}\n'.format('*' * 60),
63
63
  'run_behave': u"Running feature '{}' with scenario '{}'.",
64
- 'duplicated_scenarios': '{0}\nThere are duplicate scenario names to run.\n'
65
- 'Parallel test execution by scenario cannot be performed.\n'
66
- 'Duplicated scenario names: \n{1}.\n{0}\n'.format('*' * 60, {}),
64
+ 'duplicated_scenarios': ('{0}\nThere are duplicate scenario names to run.\n'
65
+ 'Parallel test execution by scenario cannot be performed.\n'
66
+ 'Duplicated scenario names: \n{1}.\n{0}\n').format('*' * 60, {}),
67
67
  'execution_crashed': 'Execution crashed or was interrupted when executing the scenario. No outputs could be generated.'
68
68
  },
69
69
  'folder': {'run_behave': u"Running folder: '{}' and feature '{}'."},
@@ -77,6 +77,9 @@ TEXTS = {
77
77
  'execution': {
78
78
  'callback': {
79
79
  'error': u'\nError occurred during execution callback: {}'
80
+ },
81
+ 'dry_run': {
82
+ 'completed': u'\nDry run completed. Please, see the report in {0} folder.\n\n'
80
83
  }
81
84
  },
82
85
  'parallel': {
@@ -12,37 +12,39 @@ including setup, execution, and reporting.
12
12
  from __future__ import absolute_import, print_function
13
13
 
14
14
  import codecs
15
- import concurrent
15
+ import concurrent # pyright: ignore[reportUnusedImport]
16
16
  import copy
17
17
  import io
18
18
  import json
19
- import logging.config
19
+ import logging.config # pyright: ignore[reportUnusedImport]
20
20
  import multiprocessing
21
21
  import os
22
22
  import os.path
23
23
  import platform
24
- import re
25
24
  import signal
26
25
  import sys
27
26
  import time
28
27
  import traceback
29
28
  from concurrent.futures import ProcessPoolExecutor
29
+ from concurrent.futures.process import BrokenProcessPool
30
30
  from multiprocessing import active_children
31
31
  from multiprocessing.managers import DictProxy
32
32
  from tempfile import gettempdir
33
+ from typing import Any, Dict
33
34
 
34
35
  from behave import __main__ as behave_script
35
36
  from behave.model import Feature, Scenario, ScenarioOutline
36
37
 
37
38
  # noinspection PyUnresolvedReferences
38
- import behavex.outputs.report_json
39
39
  from behavex import conf_mgr
40
40
  from behavex.arguments import BEHAVE_ARGS, BEHAVEX_ARGS, parse_arguments
41
- from behavex.conf_mgr import ConfigRun, get_env, get_param, set_env
41
+ from behavex.conf_mgr import ConfigRun, get_env, get_param
42
42
  from behavex.environment import extend_behave_hooks
43
43
  from behavex.execution_singleton import ExecutionSingleton
44
44
  from behavex.global_vars import global_vars
45
45
  from behavex.outputs import report_xml
46
+ from behavex.outputs.formatter_manager import (DEFAULT_FORMATTER_DIR,
47
+ FormatterManager)
46
48
  from behavex.outputs.report_json import generate_execution_info
47
49
  from behavex.outputs.report_utils import (get_overall_status,
48
50
  match_for_execution,
@@ -53,13 +55,12 @@ from behavex.utils import (IncludeNameMatch, IncludePathsMatch, MatchInclude,
53
55
  cleanup_folders, configure_logging,
54
56
  copy_bootstrap_html_generator,
55
57
  create_execution_complete_callback_function,
56
- expand_paths, explore_features, generate_hash,
57
- generate_reports, get_feature_and_scenario_line,
58
- get_json_results, get_logging_level,
59
- get_scenario_tags, get_scenarios_instances,
60
- get_text, join_feature_reports,
61
- join_scenario_reports, len_scenarios,
62
- print_env_variables, print_parallel,
58
+ expand_paths, explore_features, generate_reports,
59
+ get_feature_and_scenario_line, get_json_results,
60
+ get_logging_level, get_scenario_tags,
61
+ get_scenarios_instances, get_text,
62
+ join_feature_reports, join_scenario_reports,
63
+ len_scenarios, print_env_variables, print_parallel,
63
64
  set_behave_tags, set_env_variable,
64
65
  set_environ_config, set_system_paths)
65
66
 
@@ -136,8 +137,8 @@ def run(args):
136
137
  # Handle include_paths parameter
137
138
  if len(get_param('include_paths')) > 0:
138
139
  include_paths = ",".join(expand_paths(get_param('include_paths'))).replace('\n', '')
139
- features_path = os.environ.get('FEATURES_PATH')
140
- if features_path == '' or features_path is None or not os.path.exists(features_path):
140
+ features_path = os.environ.get('FEATURES_PATH', '')
141
+ if features_path == '' or not os.path.exists(features_path):
141
142
  os.environ['FEATURES_PATH'] = include_paths
142
143
  else:
143
144
  os.environ['FEATURES_PATH'] = features_path + ',' + include_paths
@@ -210,7 +211,7 @@ def launch_behavex():
210
211
  execution_codes = []
211
212
  results = None
212
213
  config = conf_mgr.get_config()
213
- features_path = os.environ.get('FEATURES_PATH')
214
+ features_path = os.environ.get('FEATURES_PATH', '')
214
215
  parallel_scheme = get_param('parallel_scheme')
215
216
  if get_param('dry_run'):
216
217
  parallel_processes = 1
@@ -221,7 +222,7 @@ def launch_behavex():
221
222
  show_progress_bar = get_param('show_progress_bar')
222
223
  multiprocess = (
223
224
  True
224
- if get_param('parallel_processes') > 1 and not get_param('dry_run')
225
+ if parallel_processes > 1 and not get_param('dry_run')
225
226
  else False
226
227
  )
227
228
  set_behave_tags()
@@ -283,11 +284,9 @@ def launch_behavex():
283
284
  lock,
284
285
  show_progress_bar)
285
286
  wrap_up_process_pools(process_pool, json_reports, scenario)
286
- time_end = global_vars.execution_end_time
287
287
 
288
288
  if get_param('dry_run'):
289
- msg = '\nDry run completed. Please, see the report in {0}' ' folder.\n\n'
290
- print(msg.format(get_env('OUTPUT')))
289
+ print_parallel('execution.dry_run.completed', get_env('OUTPUT'))
291
290
 
292
291
  remove_temporary_files(parallel_processes, json_reports)
293
292
 
@@ -397,7 +396,6 @@ def create_scenario_line_references(features):
397
396
  updated_features = {}
398
397
  for feature_path, scenarios in features.items():
399
398
  for scenario in scenarios:
400
- scenario_filename = text(scenario.filename)
401
399
  if global_vars.rerun_failures or ".feature:" in feature_path:
402
400
  if feature_path not in updated_features:
403
401
  updated_features[feature_path] = []
@@ -502,8 +500,10 @@ def launch_by_feature(features,
502
500
  for parallel_process in parallel_processes:
503
501
  try:
504
502
  parallel_process.result()
503
+ except (KeyboardInterrupt, SystemExit):
504
+ # Allow KeyboardInterrupt (Ctrl+C) and SystemExit to propagate to top level
505
+ raise
505
506
  except:
506
- from concurrent.futures.process import BrokenProcessPool
507
507
  e = sys.exc_info()[1]
508
508
  if isinstance(e, BrokenProcessPool):
509
509
  print_parallel('process.pool.broken.main', str(e))
@@ -625,8 +625,10 @@ def launch_by_scenario(features,
625
625
  for parallel_process in parallel_processes:
626
626
  try:
627
627
  parallel_process.result()
628
+ except (KeyboardInterrupt, SystemExit):
629
+ # Allow KeyboardInterrupt (Ctrl+C) and SystemExit to propagate to top level
630
+ raise
628
631
  except:
629
- from concurrent.futures.process import BrokenProcessPool
630
632
  e = sys.exc_info()[1]
631
633
  if isinstance(e, BrokenProcessPool):
632
634
  print_parallel('process.pool.broken.main', str(e))
@@ -772,21 +774,57 @@ def _launch_behave(behave_args):
772
774
 
773
775
  # Use a custom output capture class that mirrors to console and captures
774
776
  class TeePrint:
777
+ """A complete wrapper for stdout that captures output while maintaining all stdout functionality."""
778
+
775
779
  def __init__(self, original_stdout):
776
- self.original_stdout = original_stdout
777
- self.captured = io.StringIO()
780
+ # Use object.__setattr__ to avoid recursion with __setattr__
781
+ object.__setattr__(self, 'original_stdout', original_stdout)
782
+ object.__setattr__(self, 'captured', io.StringIO())
778
783
 
779
784
  def write(self, data):
785
+ """Override write to capture output while maintaining original behavior."""
780
786
  self.original_stdout.write(data)
781
787
  self.captured.write(data)
788
+ return len(data) if hasattr(data, '__len__') else None
782
789
 
783
790
  def flush(self):
791
+ """Override flush to flush both original and captured streams."""
784
792
  self.original_stdout.flush()
785
793
  self.captured.flush()
786
794
 
787
795
  def getvalue(self):
796
+ """Custom method to get captured output."""
788
797
  return self.captured.getvalue()
789
798
 
799
+ def __getattr__(self, name):
800
+ """Delegate all other attributes/methods to the original stdout."""
801
+ try:
802
+ return getattr(self.original_stdout, name)
803
+ except AttributeError:
804
+ raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")
805
+
806
+ def __setattr__(self, name, value):
807
+ """Delegate attribute setting to original stdout (except for our internal attributes)."""
808
+ if name in ('original_stdout', 'captured'):
809
+ object.__setattr__(self, name, value)
810
+ else:
811
+ setattr(self.original_stdout, name, value)
812
+
813
+ def __getattribute__(self, name):
814
+ """Handle attribute access with proper delegation."""
815
+ # Handle our own methods and attributes first
816
+ if name in ('original_stdout', 'captured', 'write', 'flush', 'getvalue',
817
+ '__class__', '__dict__', '__getattr__', '__setattr__'):
818
+ return object.__getattribute__(self, name)
819
+
820
+ # For common stdout properties, delegate to original_stdout
821
+ try:
822
+ original_stdout = object.__getattribute__(self, 'original_stdout')
823
+ return getattr(original_stdout, name)
824
+ except (AttributeError, KeyError):
825
+ # Fallback to our own implementation
826
+ return object.__getattribute__(self, name)
827
+
790
828
  # Create the tee capture object
791
829
  tee = TeePrint(sys.__stdout__)
792
830
 
@@ -867,7 +905,7 @@ def wrap_up_process_pools(process_pool,
867
905
  output = os.path.join(get_env('OUTPUT'))
868
906
  try:
869
907
  process_pool.shutdown(wait=True)
870
- except Exception as ex:
908
+ except Exception:
871
909
  process_pool.shutdown(wait=False, cancel_futures=True)
872
910
  if type(json_reports) is list:
873
911
  if scenario:
@@ -1049,8 +1087,25 @@ def _set_env_variables(args):
1049
1087
  if get_param('name'):
1050
1088
  set_env_variable('NAME', args.name)
1051
1089
  if get_param('formatter'):
1052
- formatter_outdir = get_param('formatter_outdir')
1053
- set_env_variable('LOGS', os.path.join(get_env('output'), formatter_outdir))
1090
+ formatter_outdir = get_param('formatter_outdir', '')
1091
+ formatter_spec = get_param('formatter')
1092
+
1093
+ # If no formatter output directory is specified, use the default
1094
+ if not formatter_outdir:
1095
+ formatter_outdir = DEFAULT_FORMATTER_DIR
1096
+
1097
+ # Special handling for custom formatters with default settings
1098
+ if formatter_outdir == DEFAULT_FORMATTER_DIR and formatter_spec:
1099
+ # Try to dynamically get the formatter's preferred output directory
1100
+ custom_formatter_dir = FormatterManager.get_formatter_output_dir(formatter_spec)
1101
+ if custom_formatter_dir:
1102
+ # Use the formatter's preferred directory to maintain consistency
1103
+ set_env_variable('LOGS', os.path.join(get_env('output'), custom_formatter_dir))
1104
+ else:
1105
+ # Fallback to default formatter directory
1106
+ set_env_variable('LOGS', os.path.join(get_env('output'), formatter_outdir))
1107
+ else:
1108
+ set_env_variable('LOGS', os.path.join(get_env('output'), formatter_outdir))
1054
1109
  else:
1055
1110
  set_env_variable('LOGS', os.path.join(get_env('output'), 'outputs', 'logs'))
1056
1111
  for arg in BEHAVEX_ARGS[4:]:
@@ -1267,8 +1322,9 @@ def dump_json_results():
1267
1322
  process_name = multiprocessing.current_process().name.split('-')[-1]
1268
1323
  path_info = os.path.join(gettempdir(), 'result{}.tmp'.format(process_name))
1269
1324
 
1270
- def _load_json():
1325
+ def _load_json() -> Dict[str, Any]:
1271
1326
  """this function load from file"""
1327
+ json_output_converted = {}
1272
1328
  with open(path_info, 'r') as info_file:
1273
1329
  json_output_file = info_file.read()
1274
1330
  json_output_converted = json.loads(json_output_file)