robotframework 7.1rc2__zip → 7.2b1__zip

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 (286) hide show
  1. {robotframework-7.1rc2 → robotframework-7.2b1}/BUILD.rst +7 -7
  2. {robotframework-7.1rc2 → robotframework-7.2b1}/PKG-INFO +2 -2
  3. {robotframework-7.1rc2 → robotframework-7.2b1}/setup.py +1 -1
  4. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/api/__init__.py +33 -9
  5. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/api/deco.py +8 -6
  6. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/api/interfaces.py +66 -15
  7. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/api/parsing.py +72 -68
  8. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/conf/languages.py +4 -3
  9. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/conf/settings.py +11 -10
  10. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/errors.py +3 -3
  11. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/htmlfilewriter.py +3 -0
  12. robotframework-7.2b1/src/robot/htmldata/libdoc/libdoc.html +408 -0
  13. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/log.html +1 -1
  14. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/report.html +1 -1
  15. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/testdata.js +1 -1
  16. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/template.py +0 -2
  17. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdoc.py +19 -11
  18. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/htmlwriter.py +7 -4
  19. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/model.py +7 -5
  20. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/writer.py +2 -2
  21. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/BuiltIn.py +121 -32
  22. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/Collections.py +20 -7
  23. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/__init__.py +3 -3
  24. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/body.py +48 -57
  25. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/control.py +35 -4
  26. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/message.py +13 -8
  27. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/modelobject.py +26 -0
  28. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/statistics.py +13 -9
  29. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/stats.py +12 -14
  30. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/suitestatistics.py +8 -9
  31. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/tags.py +10 -0
  32. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/tagstatistics.py +7 -8
  33. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/testcase.py +1 -0
  34. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/testsuite.py +1 -0
  35. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/totalstatistics.py +14 -21
  36. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/visitor.py +29 -3
  37. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/__init__.py +3 -2
  38. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/console/dotted.py +6 -3
  39. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/console/highlighting.py +18 -10
  40. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/debugfile.py +3 -3
  41. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/filelogger.py +7 -3
  42. robotframework-7.2b1/src/robot/output/jsonlogger.py +338 -0
  43. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/librarylogger.py +12 -11
  44. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/listeners.py +49 -47
  45. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/logger.py +33 -20
  46. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/loggerapi.py +6 -0
  47. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/loggerhelper.py +29 -57
  48. robotframework-7.2b1/src/robot/output/loglevel.py +54 -0
  49. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/output.py +21 -17
  50. robotframework-7.2b1/src/robot/output/outputfile.py +174 -0
  51. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/pyloggingconf.py +2 -0
  52. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/xmllogger.py +40 -169
  53. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/lexer/blocklexers.py +25 -12
  54. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/lexer/statementlexers.py +16 -7
  55. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/lexer/tokens.py +2 -1
  56. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/model/__init__.py +1 -1
  57. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/model/blocks.py +34 -2
  58. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/model/statements.py +32 -2
  59. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/parser/blockparsers.py +7 -2
  60. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/rebot.py +10 -2
  61. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/reporting/jsmodelbuilders.py +1 -1
  62. robotframework-7.2b1/src/robot/reporting/outputwriter.py +30 -0
  63. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/__init__.py +1 -1
  64. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/executionerrors.py +9 -9
  65. robotframework-7.2b1/src/robot/result/executionresult.py +331 -0
  66. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/messagefilter.py +14 -10
  67. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/model.py +90 -18
  68. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/resultbuilder.py +2 -5
  69. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/xmlelementhandlers.py +20 -9
  70. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/run.py +18 -6
  71. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/__init__.py +1 -1
  72. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/embedded.py +7 -6
  73. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/typeconverters.py +14 -2
  74. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/typeinfo.py +23 -4
  75. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/bodyrunner.py +41 -0
  76. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/builder/settings.py +2 -1
  77. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/builder/transformers.py +46 -20
  78. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/context.py +22 -11
  79. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/librarykeyword.py +1 -1
  80. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/librarykeywordrunner.py +29 -23
  81. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/model.py +31 -4
  82. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/outputcapture.py +2 -1
  83. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/signalhandler.py +7 -3
  84. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/status.py +49 -38
  85. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/statusreporter.py +1 -1
  86. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/suiterunner.py +22 -15
  87. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/userkeywordrunner.py +4 -0
  88. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/importer.py +39 -23
  89. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/unic.py +7 -8
  90. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/evaluation.py +21 -2
  91. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/replacer.py +20 -24
  92. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/scopes.py +19 -9
  93. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/variables.py +4 -1
  94. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/version.py +1 -1
  95. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robotframework.egg-info/PKG-INFO +2 -2
  96. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robotframework.egg-info/SOURCES.txt +3 -4
  97. robotframework-7.1rc2/src/robot/htmldata/libdoc/doc_formatting.css +0 -74
  98. robotframework-7.1rc2/src/robot/htmldata/libdoc/libdoc.css +0 -747
  99. robotframework-7.1rc2/src/robot/htmldata/libdoc/libdoc.html +0 -702
  100. robotframework-7.1rc2/src/robot/htmldata/libdoc/print.css +0 -11
  101. robotframework-7.1rc2/src/robot/htmldata/libdoc/pygments.css +0 -162
  102. robotframework-7.1rc2/src/robot/reporting/outputwriter.py +0 -48
  103. robotframework-7.1rc2/src/robot/result/executionresult.py +0 -201
  104. {robotframework-7.1rc2 → robotframework-7.2b1}/AUTHORS.rst +0 -0
  105. {robotframework-7.1rc2 → robotframework-7.2b1}/CONTRIBUTING.rst +0 -0
  106. {robotframework-7.1rc2 → robotframework-7.2b1}/COPYRIGHT.txt +0 -0
  107. {robotframework-7.1rc2 → robotframework-7.2b1}/INSTALL.rst +0 -0
  108. {robotframework-7.1rc2 → robotframework-7.2b1}/LICENSE.txt +0 -0
  109. {robotframework-7.1rc2 → robotframework-7.2b1}/MANIFEST.in +0 -0
  110. {robotframework-7.1rc2 → robotframework-7.2b1}/README.rst +0 -0
  111. {robotframework-7.1rc2 → robotframework-7.2b1}/setup.cfg +0 -0
  112. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/__init__.py +0 -0
  113. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/__main__.py +0 -0
  114. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/api/exceptions.py +0 -0
  115. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/api/logger.py +0 -0
  116. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/api/py.typed +0 -0
  117. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/conf/__init__.py +0 -0
  118. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/conf/gatherfailed.py +0 -0
  119. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/__init__.py +0 -0
  120. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/common/__init__.py +0 -0
  121. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/common/js_disabled.css +0 -0
  122. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/common/storage.js +0 -0
  123. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/jsonwriter.py +0 -0
  124. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/lib/__init__.py +0 -0
  125. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/lib/jquery.highlight.min.js +0 -0
  126. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/lib/jquery.min.js +0 -0
  127. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/lib/jquery.tablesorter.min.js +0 -0
  128. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/lib/jquery.tmpl.min.js +0 -0
  129. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/lib/jsxcompressor.min.js +0 -0
  130. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/libdoc/__init__.py +0 -0
  131. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/__init__.py +0 -0
  132. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/common.css +0 -0
  133. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/doc_formatting.css +0 -0
  134. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/fileloading.js +0 -0
  135. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/log.css +0 -0
  136. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/log.js +0 -0
  137. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/model.js +0 -0
  138. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/print.css +0 -0
  139. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/report.css +0 -0
  140. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/util.js +0 -0
  141. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/rebot/view.js +0 -0
  142. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/testdoc/__init__.py +0 -0
  143. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/testdoc/testdoc.css +0 -0
  144. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/htmldata/testdoc/testdoc.html +0 -0
  145. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/__init__.py +0 -0
  146. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/builder.py +0 -0
  147. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/consoleviewer.py +0 -0
  148. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/datatypes.py +0 -0
  149. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/htmlutils.py +0 -0
  150. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/jsonbuilder.py +0 -0
  151. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/jsonwriter.py +0 -0
  152. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/output.py +0 -0
  153. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/robotbuilder.py +0 -0
  154. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/standardtypes.py +0 -0
  155. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/xmlbuilder.py +0 -0
  156. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libdocpkg/xmlwriter.py +0 -0
  157. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/DateTime.py +0 -0
  158. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/Dialogs.py +0 -0
  159. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/Easter.py +0 -0
  160. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/OperatingSystem.py +0 -0
  161. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/Process.py +0 -0
  162. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/Remote.py +0 -0
  163. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/Screenshot.py +0 -0
  164. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/String.py +0 -0
  165. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/Telnet.py +0 -0
  166. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/XML.py +0 -0
  167. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/__init__.py +0 -0
  168. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/libraries/dialogs_py.py +0 -0
  169. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/configurer.py +0 -0
  170. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/filter.py +0 -0
  171. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/fixture.py +0 -0
  172. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/itemlist.py +0 -0
  173. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/keyword.py +0 -0
  174. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/metadata.py +0 -0
  175. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/modifier.py +0 -0
  176. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/namepatterns.py +0 -0
  177. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/model/tagsetter.py +0 -0
  178. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/console/__init__.py +0 -0
  179. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/console/quiet.py +0 -0
  180. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/console/verbose.py +0 -0
  181. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/output/stdoutlogsplitter.py +0 -0
  182. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/__init__.py +0 -0
  183. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/lexer/__init__.py +0 -0
  184. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/lexer/context.py +0 -0
  185. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/lexer/lexer.py +0 -0
  186. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/lexer/settings.py +0 -0
  187. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/lexer/tokenizer.py +0 -0
  188. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/model/visitor.py +0 -0
  189. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/parser/__init__.py +0 -0
  190. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/parser/fileparser.py +0 -0
  191. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/parser/parser.py +0 -0
  192. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/parsing/suitestructure.py +0 -0
  193. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/pythonpathsetter.py +0 -0
  194. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/reporting/__init__.py +0 -0
  195. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/reporting/expandkeywordmatcher.py +0 -0
  196. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/reporting/jsbuildingcontext.py +0 -0
  197. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/reporting/jsexecutionresult.py +0 -0
  198. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/reporting/jswriter.py +0 -0
  199. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/reporting/logreportwriters.py +0 -0
  200. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/reporting/resultwriter.py +0 -0
  201. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/reporting/stringcache.py +0 -0
  202. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/reporting/xunitwriter.py +0 -0
  203. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/configurer.py +0 -0
  204. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/flattenkeywordmatcher.py +0 -0
  205. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/keywordremover.py +0 -0
  206. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/merger.py +0 -0
  207. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/modeldeprecation.py +0 -0
  208. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/suiteteardownfailed.py +0 -0
  209. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/result/visitor.py +0 -0
  210. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/__init__.py +0 -0
  211. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/argumentconverter.py +0 -0
  212. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/argumentmapper.py +0 -0
  213. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/argumentparser.py +0 -0
  214. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/argumentresolver.py +0 -0
  215. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/argumentspec.py +0 -0
  216. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/argumentvalidator.py +0 -0
  217. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/customconverters.py +0 -0
  218. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/typeinfoparser.py +0 -0
  219. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/arguments/typevalidator.py +0 -0
  220. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/builder/__init__.py +0 -0
  221. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/builder/builders.py +0 -0
  222. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/builder/parsers.py +0 -0
  223. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/dynamicmethods.py +0 -0
  224. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/importer.py +0 -0
  225. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/invalidkeyword.py +0 -0
  226. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/keywordfinder.py +0 -0
  227. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/keywordimplementation.py +0 -0
  228. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/libraryscopes.py +0 -0
  229. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/namespace.py +0 -0
  230. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/randomizer.py +0 -0
  231. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/resourcemodel.py +0 -0
  232. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/runkwregister.py +0 -0
  233. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/testlibraries.py +0 -0
  234. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/timeouts/__init__.py +0 -0
  235. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/timeouts/nosupport.py +0 -0
  236. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/timeouts/posix.py +0 -0
  237. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/running/timeouts/windows.py +0 -0
  238. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/testdoc.py +0 -0
  239. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/__init__.py +0 -0
  240. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/application.py +0 -0
  241. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/argumentparser.py +0 -0
  242. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/asserts.py +0 -0
  243. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/charwidth.py +0 -0
  244. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/compress.py +0 -0
  245. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/connectioncache.py +0 -0
  246. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/dotdict.py +0 -0
  247. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/encoding.py +0 -0
  248. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/encodingsniffer.py +0 -0
  249. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/error.py +0 -0
  250. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/escaping.py +0 -0
  251. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/etreewrapper.py +0 -0
  252. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/filereader.py +0 -0
  253. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/frange.py +0 -0
  254. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/htmlformatters.py +0 -0
  255. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/markuputils.py +0 -0
  256. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/markupwriters.py +0 -0
  257. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/match.py +0 -0
  258. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/misc.py +0 -0
  259. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/normalizing.py +0 -0
  260. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/notset.py +0 -0
  261. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/platform.py +0 -0
  262. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/recommendations.py +0 -0
  263. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/restreader.py +0 -0
  264. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/robotenv.py +0 -0
  265. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/robotinspect.py +0 -0
  266. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/robotio.py +0 -0
  267. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/robotpath.py +0 -0
  268. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/robottime.py +0 -0
  269. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/robottypes.py +0 -0
  270. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/setter.py +0 -0
  271. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/sortable.py +0 -0
  272. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/text.py +0 -0
  273. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/utils/typehints.py +0 -0
  274. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/__init__.py +0 -0
  275. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/assigner.py +0 -0
  276. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/filesetter.py +0 -0
  277. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/finders.py +0 -0
  278. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/notfound.py +0 -0
  279. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/resolvable.py +0 -0
  280. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/search.py +0 -0
  281. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/store.py +0 -0
  282. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robot/variables/tablesetter.py +0 -0
  283. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robotframework.egg-info/dependency_links.txt +0 -0
  284. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robotframework.egg-info/entry_points.txt +0 -0
  285. {robotframework-7.1rc2 → robotframework-7.2b1}/src/robotframework.egg-info/top_level.txt +0 -0
  286. {robotframework-7.1rc2 → robotframework-7.2b1}/tasks.py +0 -0
@@ -19,7 +19,7 @@ from robot.parsing import File, ModelVisitor, Token
19
19
  from robot.utils import NormalizedDict
20
20
  from robot.variables import VariableMatches
21
21
 
22
- from ..model import For, If, IfBranch, TestSuite, TestCase, Try, TryBranch, While
22
+ from ..model import For, Group, If, IfBranch, TestSuite, TestCase, Try, TryBranch, While
23
23
  from ..resourcemodel import ResourceFile, UserKeyword
24
24
  from .settings import FileSettings
25
25
 
@@ -62,6 +62,15 @@ class SettingsBuilder(ModelVisitor):
62
62
  self.settings.default_tags = node.values
63
63
 
64
64
  def visit_TestTags(self, node):
65
+ for tag in node.values:
66
+ if tag.startswith('-'):
67
+ LOGGER.warn(
68
+ f"Error in file '{self.suite.source}' on line {node.lineno}: "
69
+ f"Setting tags starting with a hyphen like '{tag}' using the "
70
+ f"'Test Tags' setting is deprecated. In Robot Framework 8.0 this "
71
+ f"syntax will be used for removing tags. Escape the tag like "
72
+ f"'\\{tag}' to use the literal value and to avoid this warning."
73
+ )
65
74
  self.settings.test_tags = node.values
66
75
 
67
76
  def visit_KeywordTags(self, node):
@@ -167,7 +176,7 @@ class ResourceBuilder(ModelVisitor):
167
176
 
168
177
  class BodyBuilder(ModelVisitor):
169
178
 
170
- def __init__(self, model: 'TestCase|UserKeyword|For|If|Try|While|None' = None):
179
+ def __init__(self, model: 'TestCase|UserKeyword|For|If|Try|While|Group|None' = None):
171
180
  self.model = model
172
181
 
173
182
  def visit_For(self, node):
@@ -176,6 +185,9 @@ class BodyBuilder(ModelVisitor):
176
185
  def visit_While(self, node):
177
186
  WhileBuilder(self.model).build(node)
178
187
 
188
+ def visit_Group(self, node):
189
+ GroupBuilder(self.model).build(node)
190
+
179
191
  def visit_If(self, node):
180
192
  IfBuilder(self.model).build(node)
181
193
 
@@ -240,7 +252,7 @@ class TestCaseBuilder(BodyBuilder):
240
252
 
241
253
  def _set_template(self, parent, template):
242
254
  for item in parent.body:
243
- if item.type == item.FOR:
255
+ if item.type in (item.FOR, item.GROUP):
244
256
  self._set_template(item, template)
245
257
  elif item.type == item.IF_ELSE_ROOT:
246
258
  for branch in item.body:
@@ -365,7 +377,7 @@ class KeywordBuilder(BodyBuilder):
365
377
  class ForBuilder(BodyBuilder):
366
378
  model: For
367
379
 
368
- def __init__(self, parent: 'TestCase|UserKeyword|For|If|Try|While'):
380
+ def __init__(self, parent: 'TestCase|UserKeyword|For|If|Try|While|Group'):
369
381
  super().__init__(parent.body.create_for())
370
382
 
371
383
  def build(self, node):
@@ -387,7 +399,7 @@ class ForBuilder(BodyBuilder):
387
399
  class IfBuilder(BodyBuilder):
388
400
  model: 'IfBranch|None'
389
401
 
390
- def __init__(self, parent: 'TestCase|UserKeyword|For|If|Try|While'):
402
+ def __init__(self, parent: 'TestCase|UserKeyword|For|If|Try|While|Group'):
391
403
  super().__init__()
392
404
  self.root = parent.body.create_if()
393
405
 
@@ -427,14 +439,13 @@ class IfBuilder(BodyBuilder):
427
439
  class TryBuilder(BodyBuilder):
428
440
  model: 'TryBranch|None'
429
441
 
430
- def __init__(self, parent: 'TestCase|UserKeyword|For|If|Try|While'):
442
+ def __init__(self, parent: 'TestCase|UserKeyword|For|If|Try|While|Group'):
431
443
  super().__init__()
432
444
  self.root = parent.body.create_try()
433
- self.template_error = None
434
445
 
435
446
  def build(self, node):
436
- self.root.config(lineno=node.lineno)
437
- errors = self._get_errors(node)
447
+ self.root.config(lineno=node.lineno,
448
+ error=format_error(self._get_errors(node)))
438
449
  while node:
439
450
  self.model = self.root.body.create_branch(node.type, node.patterns,
440
451
  node.pattern_type, node.assign,
@@ -442,10 +453,6 @@ class TryBuilder(BodyBuilder):
442
453
  for step in node.body:
443
454
  self.visit(step)
444
455
  node = node.next
445
- if self.template_error:
446
- errors += (self.template_error,)
447
- if errors:
448
- self.root.error = format_error(errors)
449
456
  return self.root
450
457
 
451
458
  def _get_errors(self, node):
@@ -456,21 +463,40 @@ class TryBuilder(BodyBuilder):
456
463
  errors += node.end.errors
457
464
  return errors
458
465
 
459
- def visit_TemplateArguments(self, node):
460
- self.template_error = 'Templates cannot be used with TRY.'
461
-
462
466
 
463
467
  class WhileBuilder(BodyBuilder):
464
468
  model: While
465
469
 
466
- def __init__(self, parent: 'TestCase|UserKeyword|For|If|Try|While'):
470
+ def __init__(self, parent: 'TestCase|UserKeyword|For|If|Try|While|Group'):
467
471
  super().__init__(parent.body.create_while())
468
472
 
473
+ def build(self, node):
474
+ self.model.config(condition=node.condition,
475
+ limit=node.limit,
476
+ on_limit=node.on_limit,
477
+ on_limit_message=node.on_limit_message,
478
+ lineno=node.lineno,
479
+ error=format_error(self._get_errors(node)))
480
+ for step in node.body:
481
+ self.visit(step)
482
+ return self.model
483
+
484
+ def _get_errors(self, node):
485
+ errors = node.header.errors + node.errors
486
+ if node.end:
487
+ errors += node.end.errors
488
+ return errors
489
+
490
+
491
+ class GroupBuilder(BodyBuilder):
492
+ model: Group
493
+
494
+ def __init__(self, parent: 'TestCase|UserKeyword|For|If|Try|While|Group'):
495
+ super().__init__(parent.body.create_group())
496
+
469
497
  def build(self, node):
470
498
  error = format_error(self._get_errors(node))
471
- self.model.config(condition=node.condition, limit=node.limit,
472
- on_limit=node.on_limit, on_limit_message=node.on_limit_message,
473
- lineno=node.lineno, error=error)
499
+ self.model.config(name=node.name, lineno=node.lineno, error=error)
474
500
  for step in node.body:
475
501
  self.visit(step)
476
502
  return self.model
@@ -13,8 +13,9 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
- import inspect
17
16
  import asyncio
17
+ import inspect
18
+ import sys
18
19
  from contextlib import contextmanager
19
20
 
20
21
  from robot.errors import DataError, ExecutionFailed
@@ -95,7 +96,6 @@ EXECUTION_CONTEXTS = ExecutionContexts()
95
96
 
96
97
 
97
98
  class _ExecutionContext:
98
- _started_keywords_threshold = 100
99
99
 
100
100
  def __init__(self, suite, namespace, output, dry_run=False, asynchronous=None):
101
101
  self.suite = suite
@@ -180,8 +180,11 @@ class _ExecutionContext:
180
180
  return self.namespace.variables
181
181
 
182
182
  def continue_on_failure(self, default=False):
183
- parents = ([self.test] if self.test else []) + self.user_keywords
184
- for index, parent in enumerate(reversed(parents)):
183
+ parents = [result for _, result, implementation in reversed(self.steps)
184
+ if implementation and implementation.type == 'USER KEYWORD']
185
+ if self.test:
186
+ parents.append(self.test)
187
+ for index, parent in enumerate(parents):
185
188
  robot = parent.tags.robot
186
189
  if index == 0 and robot('stop-on-failure'):
187
190
  return False
@@ -195,10 +198,10 @@ class _ExecutionContext:
195
198
 
196
199
  @property
197
200
  def allow_loop_control(self):
198
- for _, step in reversed(self.steps):
199
- if step.type == 'ITERATION':
201
+ for _, result, _ in reversed(self.steps):
202
+ if result.type == 'ITERATION':
200
203
  return True
201
- if step.type == 'KEYWORD' and step.owner != 'BuiltIn':
204
+ if result.type == 'KEYWORD' and result.owner != 'BuiltIn':
202
205
  return False
203
206
  return False
204
207
 
@@ -249,10 +252,8 @@ class _ExecutionContext:
249
252
  self.timeout_occurred = False
250
253
 
251
254
  def start_body_item(self, data, result, implementation=None):
252
- self.steps.append((data, result))
253
- if len(self.steps) > self._started_keywords_threshold:
254
- raise DataError('Maximum limit of started keywords and control '
255
- 'structures exceeded.')
255
+ self._prevent_execution_close_to_recursion_limit()
256
+ self.steps.append((data, result, implementation))
256
257
  output = self.output
257
258
  args = (data, result)
258
259
  if implementation:
@@ -274,6 +275,7 @@ class _ExecutionContext:
274
275
  method = {
275
276
  result.FOR: output.start_for,
276
277
  result.WHILE: output.start_while,
278
+ result.GROUP: output.start_group,
277
279
  result.IF_ELSE_ROOT: output.start_if,
278
280
  result.IF: output.start_if_branch,
279
281
  result.ELSE: output.start_if_branch,
@@ -290,6 +292,14 @@ class _ExecutionContext:
290
292
  }[result.type]
291
293
  method(*args)
292
294
 
295
+ def _prevent_execution_close_to_recursion_limit(self):
296
+ try:
297
+ sys._getframe(sys.getrecursionlimit() - 100)
298
+ except (ValueError, AttributeError):
299
+ pass
300
+ else:
301
+ raise DataError('Recursive execution stopped.')
302
+
293
303
  def end_body_item(self, data, result, implementation=None):
294
304
  output = self.output
295
305
  args = (data, result)
@@ -312,6 +322,7 @@ class _ExecutionContext:
312
322
  method = {
313
323
  result.FOR: output.end_for,
314
324
  result.WHILE: output.end_while,
325
+ result.GROUP: output.end_group,
315
326
  result.IF_ELSE_ROOT: output.end_if,
316
327
  result.IF: output.end_if_branch,
317
328
  result.ELSE: output.end_if_branch,
@@ -82,7 +82,7 @@ class LibraryKeyword(KeywordImplementation):
82
82
  return EmbeddedArgumentsRunner(self, name)
83
83
  if self._resolve_args_until is not None:
84
84
  dry_run = RUN_KW_REGISTER.get_dry_run(self.owner.real_name, self.name)
85
- return RunKeywordRunner(self, execute_in_dry_run=dry_run)
85
+ return RunKeywordRunner(self, dry_run_children=dry_run)
86
86
  return LibraryKeywordRunner(self, languages=languages)
87
87
 
88
88
  def resolve_arguments(self, args: 'Sequence[str|Any]',
@@ -131,23 +131,27 @@ class LibraryKeywordRunner:
131
131
  kw = self.keyword.bind(data)
132
132
  assignment = VariableAssignment(data.assign)
133
133
  self._config_result(result, data, kw, assignment)
134
- with StatusReporter(data, result, context, run=False, implementation=kw):
134
+ with StatusReporter(data, result, context, implementation=kw,
135
+ run=self._get_initial_dry_run_status(kw)):
135
136
  assignment.validate_assignment()
136
137
  if self._executed_in_dry_run(kw):
137
138
  self._run(data, kw, context)
138
139
  else:
139
140
  self._resolve_arguments(data, kw)
140
- self._end_dry_run(data, kw, result, context)
141
+ self._dry_run(data, kw, result, context)
141
142
 
142
- def _end_dry_run(self, data: KeywordData, kw: 'LibraryKeyword',
143
- result: KeywordResult, context):
144
- pass
143
+ def _get_initial_dry_run_status(self, kw):
144
+ return self._executed_in_dry_run(kw)
145
145
 
146
146
  def _executed_in_dry_run(self, kw: 'LibraryKeyword'):
147
147
  return (kw.owner.name == 'BuiltIn'
148
148
  and kw.name in ('Import Library', 'Set Library Search Order',
149
149
  'Set Tags', 'Remove Tags', 'Import Resource'))
150
150
 
151
+ def _dry_run(self, data: KeywordData, kw: 'LibraryKeyword', result: KeywordResult,
152
+ context):
153
+ pass
154
+
151
155
 
152
156
  class EmbeddedArgumentsRunner(LibraryKeywordRunner):
153
157
 
@@ -167,9 +171,9 @@ class EmbeddedArgumentsRunner(LibraryKeywordRunner):
167
171
 
168
172
  class RunKeywordRunner(LibraryKeywordRunner):
169
173
 
170
- def __init__(self, keyword: 'LibraryKeyword', execute_in_dry_run=False):
174
+ def __init__(self, keyword: 'LibraryKeyword', dry_run_children=False):
171
175
  super().__init__(keyword)
172
- self.execute_in_dry_run = execute_in_dry_run
176
+ self._dry_run_children = dry_run_children
173
177
 
174
178
  def _get_timeout(self, context):
175
179
  # These keywords are not affected by timeouts. Keywords they execute are.
@@ -183,25 +187,31 @@ class RunKeywordRunner(LibraryKeywordRunner):
183
187
  finally:
184
188
  STOP_SIGNAL_MONITOR.stop_running_keyword()
185
189
 
186
- def _end_dry_run(self, data: KeywordData, kw: 'LibraryKeyword',
187
- result: KeywordResult, context):
190
+ def _get_initial_dry_run_status(self, kw):
191
+ return self._dry_run_children or super()._get_initial_dry_run_status(kw)
192
+
193
+ def _dry_run(self, data: KeywordData, kw: 'LibraryKeyword', result: KeywordResult,
194
+ context):
188
195
  wrapper = UserKeyword(name=kw.name,
189
- doc="Wraps keywords executed by '{kw.name}' in dry-run.",
196
+ doc=f"Wraps keywords executed by '{kw.name}' in dry-run.",
190
197
  parent=kw.parent)
191
- wrapper.body = [k for k in self._get_dry_run_keywords(kw, data.args)
192
- if not contains_variable(k.name)]
198
+ for child in self._get_dry_run_children(kw, data.args):
199
+ if not contains_variable(child.name):
200
+ child.lineno = data.lineno
201
+ wrapper.body.append(child)
193
202
  BodyRunner(context).run(wrapper, result)
194
203
 
195
- def _get_dry_run_keywords(self, kw: 'LibraryKeyword', args):
196
- if not self.execute_in_dry_run:
204
+ def _get_dry_run_children(self, kw: 'LibraryKeyword', args):
205
+ if not self._dry_run_children:
197
206
  return []
198
207
  if kw.name == 'Run Keyword If':
199
- return self._get_dry_run_keywords_for_run_keyword_if(args)
208
+ return self._get_dry_run_children_for_run_keyword_if(args)
200
209
  if kw.name == 'Run Keywords':
201
- return self._get_dry_run_keywords_for_run_keyword(args)
202
- return self._get_dry_run_keywords_based_on_name(kw, args)
210
+ return self._get_dry_run_children_for_run_keyword(args)
211
+ index = kw.args.positional.index('name')
212
+ return [KeywordData(name=args[index], args=args[index+1:])]
203
213
 
204
- def _get_dry_run_keywords_for_run_keyword_if(self, given_args):
214
+ def _get_dry_run_children_for_run_keyword_if(self, given_args):
205
215
  for kw_call in self._get_run_kw_if_calls(given_args):
206
216
  if kw_call:
207
217
  yield KeywordData(name=kw_call[0], args=kw_call[1:])
@@ -235,7 +245,7 @@ class RunKeywordRunner(LibraryKeywordRunner):
235
245
  return True
236
246
  return any(is_list_variable(item) for item in kw_call)
237
247
 
238
- def _get_dry_run_keywords_for_run_keyword(self, given_args):
248
+ def _get_dry_run_children_for_run_keyword(self, given_args):
239
249
  for kw_call in self._get_run_kws_calls(given_args):
240
250
  yield KeywordData(name=kw_call[0], args=kw_call[1:])
241
251
 
@@ -250,7 +260,3 @@ class RunKeywordRunner(LibraryKeywordRunner):
250
260
  yield kw_call
251
261
  if given_args:
252
262
  yield given_args
253
-
254
- def _get_dry_run_keywords_based_on_name(self, kw: 'LibraryKeyword', given_args):
255
- index = kw.args.positional.index('name')
256
- return [KeywordData(name=given_args[index], args=given_args[index+1:])]
@@ -46,7 +46,7 @@ from robot.output import LOGGER, Output, pyloggingconf
46
46
  from robot.utils import format_assign_message, setter
47
47
  from robot.variables import VariableResolver
48
48
 
49
- from .bodyrunner import ForRunner, IfRunner, KeywordRunner, TryRunner, WhileRunner
49
+ from .bodyrunner import ForRunner, GroupRunner, IfRunner, KeywordRunner, TryRunner, WhileRunner
50
50
  from .randomizer import Randomizer
51
51
  from .statusreporter import StatusReporter
52
52
 
@@ -58,15 +58,15 @@ if TYPE_CHECKING:
58
58
 
59
59
  IT = TypeVar('IT', bound='IfBranch|TryBranch')
60
60
  BodyItemParent = Union['TestSuite', 'TestCase', 'UserKeyword', 'For', 'If', 'IfBranch',
61
- 'Try', 'TryBranch', 'While', None]
61
+ 'Try', 'TryBranch', 'While', 'Group', None]
62
62
 
63
63
 
64
- class Body(model.BaseBody['Keyword', 'For', 'While', 'If', 'Try', 'Var', 'Return',
64
+ class Body(model.BaseBody['Keyword', 'For', 'While', 'Group', 'If', 'Try', 'Var', 'Return',
65
65
  'Continue', 'Break', 'model.Message', 'Error']):
66
66
  __slots__ = ()
67
67
 
68
68
 
69
- class Branches(model.BaseBranches['Keyword', 'For', 'While', 'If', 'Try', 'Var', 'Return',
69
+ class Branches(model.BaseBranches['Keyword', 'For', 'While', 'Group', 'If', 'Try', 'Var', 'Return',
70
70
  'Continue', 'Break', 'model.Message', 'Error', IT]):
71
71
  __slots__ = ()
72
72
 
@@ -259,6 +259,33 @@ class While(model.While, WithSource):
259
259
  iteration = WhileIteration(self, self.lineno, self.error)
260
260
  iteration.body = [item.to_dict() for item in self.body]
261
261
  return iteration
262
+ self.error = error
263
+
264
+
265
+ @Body.register
266
+ class Group(model.Group, WithSource):
267
+ __slots__ = ['lineno', 'error']
268
+ body_class = Body
269
+
270
+ def __init__(self, name: str = '',
271
+ parent: BodyItemParent = None,
272
+ lineno: 'int|None' = None,
273
+ error: 'str|None' = None):
274
+ super().__init__(name, parent)
275
+ self.lineno = lineno
276
+ self.error = error
277
+
278
+ def to_dict(self) -> DataDict:
279
+ data = super().to_dict()
280
+ if self.lineno:
281
+ data['lineno'] = self.lineno
282
+ if self.error:
283
+ data['error'] = self.error
284
+ return data
285
+
286
+ def run(self, result, context, run=True, templated=False):
287
+ result = result.body.create_group(self.name)
288
+ return GroupRunner(context, run, templated).run(self, result)
262
289
 
263
290
 
264
291
  class IfBranch(model.IfBranch, WithSource):
@@ -52,7 +52,8 @@ class OutputCapturer:
52
52
  LOGGER.log_output(stdout)
53
53
  if stderr:
54
54
  LOGGER.log_output(stderr)
55
- sys.__stderr__.write(console_encode(stderr, stream=sys.__stderr__))
55
+ if sys.__stderr__:
56
+ sys.__stderr__.write(console_encode(stderr, stream=sys.__stderr__))
56
57
 
57
58
  def _release(self):
58
59
  stdout = self.stdout.release()
@@ -31,14 +31,18 @@ class _StopSignalMonitor:
31
31
 
32
32
  def __call__(self, signum, frame):
33
33
  self._signal_count += 1
34
- LOGGER.info('Received signal: %s.' % signum)
34
+ LOGGER.info(f'Received signal: {signum}.')
35
35
  if self._signal_count > 1:
36
- sys.__stderr__.write('Execution forcefully stopped.\n')
36
+ self._write_to_stderr('Execution forcefully stopped.')
37
37
  raise SystemExit()
38
- sys.__stderr__.write('Second signal will force exit.\n')
38
+ self._write_to_stderr('Second signal will force exit.')
39
39
  if self._running_keyword:
40
40
  self._stop_execution_gracefully()
41
41
 
42
+ def _write_to_stderr(self, message):
43
+ if sys.__stderr__:
44
+ sys.__stderr__.write(message + '\n')
45
+
42
46
  def _stop_execution_gracefully(self):
43
47
  raise ExecutionFailed('Execution terminated by signal', exit=True)
44
48
 
@@ -13,9 +13,11 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
+ from abc import ABC
17
+
16
18
  from robot.errors import PassExecution
17
19
  from robot.model import TagPatterns
18
- from robot.utils import html_escape, test_or_task
20
+ from robot.utils import html_escape, plural_or_not as s, seq2str, test_or_task
19
21
 
20
22
 
21
23
  class Failure:
@@ -29,9 +31,13 @@ class Failure:
29
31
  self.teardown_skipped = None
30
32
 
31
33
  def __bool__(self):
32
- return bool(
33
- self.setup or self.test or self.teardown or
34
- self.setup_skipped or self.test_skipped or self.teardown_skipped
34
+ return (
35
+ self.setup is not None
36
+ or self.test is not None
37
+ or self.teardown is not None
38
+ or self.setup_skipped is not None
39
+ or self.test_skipped is not None
40
+ or self.teardown_skipped is not None
35
41
  )
36
42
 
37
43
 
@@ -45,10 +51,10 @@ class Exit:
45
51
  self.error = False
46
52
  self.fatal = False
47
53
 
48
- def failure_occurred(self, fatal=False):
54
+ def failure_occurred(self, fatal=False, suite_setup=False):
49
55
  if fatal:
50
56
  self.fatal = True
51
- if self.failure_mode:
57
+ if self.failure_mode and not suite_setup:
52
58
  self.failure = True
53
59
 
54
60
  def error_occurred(self):
@@ -63,7 +69,7 @@ class Exit:
63
69
  return bool(self.failure or self.error or self.fatal)
64
70
 
65
71
 
66
- class _ExecutionStatus:
72
+ class ExecutionStatus(ABC):
67
73
 
68
74
  def __init__(self, parent, exit=None):
69
75
  self.parent = parent
@@ -71,7 +77,6 @@ class _ExecutionStatus:
71
77
  self.failure = Failure()
72
78
  self.skipped = False
73
79
  self._teardown_allowed = False
74
- self._rpa = False
75
80
 
76
81
  @property
77
82
  def failed(self):
@@ -92,7 +97,8 @@ class _ExecutionStatus:
92
97
  self.skipped = True
93
98
  else:
94
99
  self.failure.setup = msg
95
- self.exit.failure_occurred(error.exit)
100
+ self.exit.failure_occurred(error.exit,
101
+ suite_setup=isinstance(self, SuiteStatus))
96
102
  self._teardown_allowed = True
97
103
 
98
104
  def teardown_executed(self, error=None):
@@ -147,7 +153,7 @@ class _ExecutionStatus:
147
153
  return ParentMessage(self.parent).message
148
154
 
149
155
 
150
- class SuiteStatus(_ExecutionStatus):
156
+ class SuiteStatus(ExecutionStatus):
151
157
 
152
158
  def __init__(self, parent=None, exit_on_failure=False, exit_on_error=False,
153
159
  skip_teardown_on_exit=False):
@@ -161,19 +167,19 @@ class SuiteStatus(_ExecutionStatus):
161
167
  return SuiteMessage(self).message
162
168
 
163
169
 
164
- class TestStatus(_ExecutionStatus):
170
+ class TestStatus(ExecutionStatus):
165
171
 
166
- def __init__(self, parent, test, skip_on_failure=None, rpa=False):
172
+ def __init__(self, parent, test, skip_on_failure=(), rpa=False):
167
173
  super().__init__(parent)
168
- self._test = test
169
- self._skip_on_failure_tags = skip_on_failure
170
- self._rpa = rpa
174
+ self.test = test
175
+ self.skip_on_failure_tags = TagPatterns(skip_on_failure)
176
+ self.rpa = rpa
171
177
 
172
178
  def test_failed(self, message=None, error=None):
173
179
  if error is not None:
174
180
  message = str(error)
175
181
  skip = error.skip
176
- fatal = error.exit
182
+ fatal = error.exit or self.test.tags.robot('exit-on-failure')
177
183
  else:
178
184
  skip = fatal = False
179
185
  if skip:
@@ -198,26 +204,32 @@ class TestStatus(_ExecutionStatus):
198
204
  return False
199
205
 
200
206
  def _skip_on_failure(self):
201
- return (self._test.tags.robot('skip-on-failure')
202
- or self._skip_on_failure_tags
203
- and TagPatterns(self._skip_on_failure_tags).match(self._test.tags))
207
+ return (self.test.tags.robot('skip-on-failure')
208
+ or self.skip_on_failure_tags.match(self.test.tags))
204
209
 
205
- def _skip_on_fail_msg(self, msg):
210
+ def _skip_on_fail_msg(self, fail_msg):
211
+ if self.test.tags.robot('skip-on-failure'):
212
+ tags = ['robot:skip-on-failure']
213
+ kind = 'tag'
214
+ else:
215
+ tags = self.skip_on_failure_tags
216
+ kind = 'tag' if tags.is_constant else 'tag pattern'
206
217
  return test_or_task(
207
- "{Test} failed but skip-on-failure mode was active and it was marked "
208
- "skipped.\n\nOriginal failure:\n%s" % msg, rpa=self._rpa
218
+ f"Failed {{test}} skipped using {seq2str(tags)} {kind}{s(tags)}.\n\n"
219
+ f"Original failure:\n{fail_msg}", rpa=self.rpa
209
220
  )
210
221
 
211
222
  def _my_message(self):
212
223
  return TestMessage(self).message
213
224
 
214
225
 
215
- class _Message:
216
- setup_message = NotImplemented
217
- setup_skipped_message = NotImplemented
218
- teardown_skipped_message = NotImplemented
219
- teardown_message = NotImplemented
220
- also_teardown_message = NotImplemented
226
+ class Message(ABC):
227
+ setup_message = ''
228
+ setup_skipped_message = ''
229
+ teardown_skipped_message = ''
230
+ teardown_message = ''
231
+ also_teardown_message = ''
232
+ also_teardown_skip_message = ''
221
233
 
222
234
  def __init__(self, status):
223
235
  self.failure = status.failure
@@ -230,11 +242,11 @@ class _Message:
230
242
 
231
243
  def _get_message_before_teardown(self):
232
244
  if self.failure.setup_skipped:
233
- return self._format_setup_or_teardown_message(
234
- self.setup_skipped_message, self.failure.setup_skipped)
245
+ return self._format_setup_or_teardown_message(self.setup_skipped_message,
246
+ self.failure.setup_skipped)
235
247
  if self.failure.setup:
236
- return self._format_setup_or_teardown_message(
237
- self.setup_message, self.failure.setup)
248
+ return self._format_setup_or_teardown_message(self.setup_message,
249
+ self.failure.setup)
238
250
  return self.failure.test_skipped or self.failure.test or ''
239
251
 
240
252
  def _format_setup_or_teardown_message(self, prefix, message):
@@ -267,7 +279,7 @@ class _Message:
267
279
  return self.also_teardown_skip_message % (teardown, message)
268
280
 
269
281
 
270
- class TestMessage(_Message):
282
+ class TestMessage(Message):
271
283
  setup_message = 'Setup failed:\n%s'
272
284
  teardown_message = 'Teardown failed:\n%s'
273
285
  setup_skipped_message = '%s'
@@ -275,12 +287,11 @@ class TestMessage(_Message):
275
287
  also_teardown_message = '%s\n\nAlso teardown failed:\n%s'
276
288
  also_teardown_skip_message = 'Skipped in teardown:\n%s\n\nEarlier message:\n%s'
277
289
  exit_on_fatal_message = 'Test execution stopped due to a fatal error.'
278
- exit_on_failure_message = \
279
- 'Failure occurred and exit-on-failure mode is in use.'
290
+ exit_on_failure_message = 'Failure occurred and exit-on-failure mode is in use.'
280
291
  exit_on_error_message = 'Error occurred and exit-on-error mode is in use.'
281
292
 
282
293
  def __init__(self, status):
283
- _Message.__init__(self, status)
294
+ super().__init__(status)
284
295
  self.exit = status.exit
285
296
 
286
297
  @property
@@ -297,7 +308,7 @@ class TestMessage(_Message):
297
308
  return ''
298
309
 
299
310
 
300
- class SuiteMessage(_Message):
311
+ class SuiteMessage(Message):
301
312
  setup_message = 'Suite setup failed:\n%s'
302
313
  setup_skipped_message = 'Skipped in suite setup:\n%s'
303
314
  teardown_skipped_message = 'Skipped in suite teardown:\n%s'
@@ -316,4 +327,4 @@ class ParentMessage(SuiteMessage):
316
327
  def __init__(self, status):
317
328
  while status.parent and status.parent.failed:
318
329
  status = status.parent
319
- SuiteMessage.__init__(self, status)
330
+ super().__init__(status)
@@ -62,7 +62,7 @@ class StatusReporter:
62
62
  result.status = failure.status
63
63
  if not isinstance(failure, (BreakLoop, ContinueLoop, ReturnFromKeyword)):
64
64
  result.message = failure.message
65
- if self.initial_test_status == 'PASS':
65
+ if self.initial_test_status == 'PASS' and result.status != 'NOT RUN':
66
66
  context.test.status = result.status
67
67
  result.elapsed_time = datetime.now() - result.start_time
68
68
  orig_status = (result.status, result.message)