robotframework 6.1rc1__zip → 7.0__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 (307) hide show
  1. {robotframework-6.1rc1 → robotframework-7.0}/INSTALL.rst +25 -25
  2. {robotframework-6.1rc1 → robotframework-7.0}/PKG-INFO +6 -8
  3. {robotframework-6.1rc1 → robotframework-7.0}/README.rst +3 -2
  4. {robotframework-6.1rc1 → robotframework-7.0}/setup.py +3 -6
  5. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/api/__init__.py +4 -1
  6. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/api/deco.py +60 -14
  7. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/api/exceptions.py +6 -6
  8. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/api/interfaces.py +403 -68
  9. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/api/logger.py +27 -20
  10. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/api/parsing.py +4 -2
  11. robotframework-7.0/src/robot/api/py.typed +0 -0
  12. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/conf/gatherfailed.py +2 -2
  13. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/conf/languages.py +61 -25
  14. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/conf/settings.py +21 -14
  15. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/common/storage.js +11 -6
  16. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/htmlfilewriter.py +1 -1
  17. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/libdoc/libdoc.css +10 -2
  18. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/libdoc/libdoc.html +10 -0
  19. robotframework-7.0/src/robot/htmldata/rebot/common.css +362 -0
  20. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/doc_formatting.css +13 -7
  21. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/log.css +51 -10
  22. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/log.html +14 -23
  23. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/model.js +2 -4
  24. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/report.css +83 -19
  25. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/report.html +22 -7
  26. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/testdata.js +10 -3
  27. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/view.js +84 -0
  28. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/template.py +3 -3
  29. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/builder.py +0 -8
  30. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/datatypes.py +9 -10
  31. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/htmlutils.py +4 -7
  32. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/jsonbuilder.py +38 -20
  33. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/model.py +21 -28
  34. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/robotbuilder.py +50 -31
  35. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/standardtypes.py +9 -1
  36. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/xmlbuilder.py +35 -20
  37. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/xmlwriter.py +18 -38
  38. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/BuiltIn.py +255 -205
  39. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/Collections.py +384 -240
  40. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/DateTime.py +73 -60
  41. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/OperatingSystem.py +6 -5
  42. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/Process.py +26 -27
  43. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/Remote.py +40 -36
  44. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/String.py +66 -76
  45. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/XML.py +66 -59
  46. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/__init__.py +2 -2
  47. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/dialogs_py.py +14 -2
  48. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/__init__.py +5 -5
  49. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/body.py +44 -13
  50. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/configurer.py +19 -15
  51. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/control.py +230 -88
  52. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/filter.py +31 -25
  53. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/itemlist.py +9 -8
  54. robotframework-7.0/src/robot/model/keyword.py +77 -0
  55. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/message.py +19 -10
  56. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/modelobject.py +27 -15
  57. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/modifier.py +1 -1
  58. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/namepatterns.py +3 -25
  59. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/stats.py +9 -15
  60. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/tags.py +1 -1
  61. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/testcase.py +15 -27
  62. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/testsuite.py +44 -28
  63. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/visitor.py +35 -8
  64. robotframework-7.0/src/robot/output/console/dotted.py +90 -0
  65. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/console/quiet.py +3 -2
  66. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/console/verbose.py +61 -61
  67. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/debugfile.py +39 -24
  68. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/filelogger.py +18 -15
  69. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/librarylogger.py +3 -2
  70. robotframework-7.0/src/robot/output/listeners.py +753 -0
  71. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/logger.py +216 -80
  72. robotframework-7.0/src/robot/output/loggerapi.py +215 -0
  73. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/loggerhelper.py +23 -56
  74. robotframework-7.0/src/robot/output/output.py +185 -0
  75. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/stdoutlogsplitter.py +8 -5
  76. robotframework-7.0/src/robot/output/xmllogger.py +459 -0
  77. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/lexer/blocklexers.py +17 -13
  78. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/lexer/context.py +12 -2
  79. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/lexer/lexer.py +1 -1
  80. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/lexer/settings.py +6 -3
  81. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/lexer/statementlexers.py +39 -10
  82. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/lexer/tokens.py +27 -29
  83. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/model/blocks.py +19 -6
  84. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/model/statements.py +193 -94
  85. robotframework-7.0/src/robot/parsing/model/visitor.py +100 -0
  86. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/rebot.py +2 -0
  87. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/expandkeywordmatcher.py +1 -1
  88. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/jsbuildingcontext.py +8 -6
  89. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/jsmodelbuilders.py +88 -61
  90. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/logreportwriters.py +16 -10
  91. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/outputwriter.py +18 -2
  92. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/resultwriter.py +4 -4
  93. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/stringcache.py +3 -3
  94. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/xunitwriter.py +15 -21
  95. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/__init__.py +3 -2
  96. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/configurer.py +12 -9
  97. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/executionresult.py +56 -8
  98. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/flattenkeywordmatcher.py +48 -9
  99. robotframework-7.0/src/robot/result/keywordremover.py +207 -0
  100. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/merger.py +1 -1
  101. robotframework-7.0/src/robot/result/model.py +1185 -0
  102. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/modeldeprecation.py +9 -3
  103. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/resultbuilder.py +46 -36
  104. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/xmlelementhandlers.py +83 -43
  105. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/run.py +3 -1
  106. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/__init__.py +14 -4
  107. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/arguments/__init__.py +2 -1
  108. robotframework-7.0/src/robot/running/arguments/argumentconverter.py +100 -0
  109. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/arguments/argumentmapper.py +26 -24
  110. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/arguments/argumentparser.py +85 -61
  111. robotframework-7.0/src/robot/running/arguments/argumentresolver.py +158 -0
  112. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/arguments/argumentspec.py +83 -116
  113. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/arguments/argumentvalidator.py +33 -27
  114. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/arguments/customconverters.py +15 -12
  115. robotframework-7.0/src/robot/running/arguments/embedded.py +145 -0
  116. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/arguments/typeconverters.py +268 -220
  117. robotframework-7.0/src/robot/running/arguments/typeinfo.py +317 -0
  118. robotframework-7.0/src/robot/running/arguments/typeinfoparser.py +219 -0
  119. robotframework-7.0/src/robot/running/arguments/typevalidator.py +60 -0
  120. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/bodyrunner.py +147 -129
  121. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/builder/builders.py +17 -26
  122. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/builder/parsers.py +17 -11
  123. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/builder/settings.py +9 -17
  124. robotframework-7.0/src/robot/running/builder/transformers.py +535 -0
  125. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/context.py +113 -31
  126. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/dynamicmethods.py +62 -34
  127. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/importer.py +27 -49
  128. robotframework-7.0/src/robot/running/invalidkeyword.py +67 -0
  129. robotframework-7.0/src/robot/running/keywordfinder.py +91 -0
  130. robotframework-7.0/src/robot/running/keywordimplementation.py +164 -0
  131. robotframework-7.0/src/robot/running/librarykeyword.py +392 -0
  132. robotframework-7.0/src/robot/running/librarykeywordrunner.py +250 -0
  133. robotframework-7.0/src/robot/running/libraryscopes.py +115 -0
  134. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/model.py +198 -337
  135. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/namespace.py +124 -137
  136. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/outputcapture.py +20 -12
  137. robotframework-7.0/src/robot/running/resourcemodel.py +448 -0
  138. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/statusreporter.py +16 -13
  139. robotframework-7.0/src/robot/running/suiterunner.py +265 -0
  140. robotframework-7.0/src/robot/running/testlibraries.py +500 -0
  141. robotframework-7.0/src/robot/running/userkeywordrunner.py +260 -0
  142. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/testdoc.py +18 -13
  143. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/__init__.py +53 -35
  144. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/connectioncache.py +64 -59
  145. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/dotdict.py +3 -0
  146. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/encodingsniffer.py +9 -4
  147. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/escaping.py +19 -19
  148. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/importer.py +2 -2
  149. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/markupwriters.py +23 -15
  150. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/misc.py +1 -2
  151. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/normalizing.py +9 -24
  152. robotframework-7.0/src/robot/utils/notset.py +33 -0
  153. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/platform.py +15 -5
  154. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/robotio.py +9 -9
  155. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/robotpath.py +1 -0
  156. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/robottime.py +122 -90
  157. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/robottypes.py +21 -31
  158. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/setter.py +0 -5
  159. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/text.py +7 -4
  160. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/unic.py +2 -2
  161. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/__init__.py +2 -2
  162. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/assigner.py +17 -19
  163. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/evaluation.py +20 -17
  164. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/filesetter.py +80 -80
  165. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/replacer.py +33 -36
  166. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/scopes.py +17 -12
  167. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/search.py +66 -41
  168. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/store.py +14 -16
  169. robotframework-7.0/src/robot/variables/tablesetter.py +164 -0
  170. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/variables.py +1 -1
  171. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/version.py +1 -1
  172. {robotframework-6.1rc1 → robotframework-7.0}/src/robotframework.egg-info/PKG-INFO +6 -8
  173. {robotframework-6.1rc1 → robotframework-7.0}/src/robotframework.egg-info/SOURCES.txt +10 -8
  174. {robotframework-6.1rc1 → robotframework-7.0}/tasks.py +6 -6
  175. robotframework-6.1rc1/src/robot/htmldata/rebot/common.css +0 -239
  176. robotframework-6.1rc1/src/robot/libraries/Reserved.py +0 -48
  177. robotframework-6.1rc1/src/robot/model/keyword.py +0 -173
  178. robotframework-6.1rc1/src/robot/output/console/dotted.py +0 -90
  179. robotframework-6.1rc1/src/robot/output/listenerarguments.py +0 -165
  180. robotframework-6.1rc1/src/robot/output/listenermethods.py +0 -113
  181. robotframework-6.1rc1/src/robot/output/listeners.py +0 -174
  182. robotframework-6.1rc1/src/robot/output/output.py +0 -95
  183. robotframework-6.1rc1/src/robot/output/xmllogger.py +0 -354
  184. robotframework-6.1rc1/src/robot/parsing/model/visitor.py +0 -67
  185. robotframework-6.1rc1/src/robot/result/keywordremover.py +0 -183
  186. robotframework-6.1rc1/src/robot/result/model.py +0 -957
  187. robotframework-6.1rc1/src/robot/running/arguments/argumentconverter.py +0 -79
  188. robotframework-6.1rc1/src/robot/running/arguments/argumentresolver.py +0 -131
  189. robotframework-6.1rc1/src/robot/running/arguments/embedded.py +0 -146
  190. robotframework-6.1rc1/src/robot/running/arguments/typevalidator.py +0 -56
  191. robotframework-6.1rc1/src/robot/running/builder/transformers.py +0 -642
  192. robotframework-6.1rc1/src/robot/running/handlers.py +0 -321
  193. robotframework-6.1rc1/src/robot/running/handlerstore.py +0 -66
  194. robotframework-6.1rc1/src/robot/running/librarykeywordrunner.py +0 -238
  195. robotframework-6.1rc1/src/robot/running/libraryscopes.py +0 -97
  196. robotframework-6.1rc1/src/robot/running/modelcombiner.py +0 -32
  197. robotframework-6.1rc1/src/robot/running/suiterunner.py +0 -249
  198. robotframework-6.1rc1/src/robot/running/testlibraries.py +0 -420
  199. robotframework-6.1rc1/src/robot/running/usererrorhandler.py +0 -75
  200. robotframework-6.1rc1/src/robot/running/userkeyword.py +0 -111
  201. robotframework-6.1rc1/src/robot/running/userkeywordrunner.py +0 -274
  202. robotframework-6.1rc1/src/robot/variables/tablesetter.py +0 -156
  203. {robotframework-6.1rc1 → robotframework-7.0}/AUTHORS.rst +0 -0
  204. {robotframework-6.1rc1 → robotframework-7.0}/BUILD.rst +0 -0
  205. {robotframework-6.1rc1 → robotframework-7.0}/CONTRIBUTING.rst +0 -0
  206. {robotframework-6.1rc1 → robotframework-7.0}/COPYRIGHT.txt +0 -0
  207. {robotframework-6.1rc1 → robotframework-7.0}/LICENSE.txt +0 -0
  208. {robotframework-6.1rc1 → robotframework-7.0}/MANIFEST.in +0 -0
  209. {robotframework-6.1rc1 → robotframework-7.0}/setup.cfg +0 -0
  210. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/__init__.py +0 -0
  211. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/__main__.py +0 -0
  212. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/conf/__init__.py +0 -0
  213. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/errors.py +0 -0
  214. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/__init__.py +0 -0
  215. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/common/__init__.py +0 -0
  216. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/common/js_disabled.css +0 -0
  217. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/jsonwriter.py +0 -0
  218. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/lib/__init__.py +0 -0
  219. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/lib/jquery.highlight.min.js +0 -0
  220. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/lib/jquery.min.js +0 -0
  221. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/lib/jquery.tablesorter.min.js +0 -0
  222. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/lib/jquery.tmpl.min.js +0 -0
  223. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/lib/jsxcompressor.min.js +0 -0
  224. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/libdoc/__init__.py +0 -0
  225. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/libdoc/doc_formatting.css +0 -0
  226. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/libdoc/print.css +0 -0
  227. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/libdoc/pygments.css +0 -0
  228. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/__init__.py +0 -0
  229. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/fileloading.js +0 -0
  230. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/log.js +0 -0
  231. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/print.css +0 -0
  232. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/util.js +0 -0
  233. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/testdoc/__init__.py +0 -0
  234. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/testdoc/testdoc.css +0 -0
  235. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/testdoc/testdoc.html +0 -0
  236. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdoc.py +0 -0
  237. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/__init__.py +0 -0
  238. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/consoleviewer.py +0 -0
  239. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/htmlwriter.py +0 -0
  240. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/jsonwriter.py +0 -0
  241. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/output.py +0 -0
  242. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/writer.py +0 -0
  243. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/Dialogs.py +0 -0
  244. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/Easter.py +0 -0
  245. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/Screenshot.py +0 -0
  246. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/Telnet.py +0 -0
  247. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/fixture.py +0 -0
  248. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/metadata.py +0 -0
  249. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/statistics.py +0 -0
  250. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/suitestatistics.py +0 -0
  251. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/tagsetter.py +0 -0
  252. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/tagstatistics.py +0 -0
  253. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/totalstatistics.py +0 -0
  254. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/__init__.py +0 -0
  255. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/console/__init__.py +0 -0
  256. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/console/highlighting.py +0 -0
  257. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/pyloggingconf.py +0 -0
  258. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/__init__.py +0 -0
  259. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/lexer/__init__.py +0 -0
  260. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/lexer/tokenizer.py +0 -0
  261. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/model/__init__.py +0 -0
  262. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/parser/__init__.py +0 -0
  263. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/parser/blockparsers.py +0 -0
  264. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/parser/fileparser.py +0 -0
  265. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/parser/parser.py +0 -0
  266. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/suitestructure.py +0 -0
  267. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/pythonpathsetter.py +0 -0
  268. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/__init__.py +0 -0
  269. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/jsexecutionresult.py +0 -0
  270. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/jswriter.py +0 -0
  271. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/executionerrors.py +0 -0
  272. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/messagefilter.py +0 -0
  273. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/suiteteardownfailed.py +0 -0
  274. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/visitor.py +0 -0
  275. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/builder/__init__.py +0 -0
  276. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/randomizer.py +0 -0
  277. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/runkwregister.py +0 -0
  278. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/signalhandler.py +0 -0
  279. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/status.py +0 -0
  280. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/timeouts/__init__.py +0 -0
  281. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/timeouts/posix.py +0 -0
  282. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/timeouts/windows.py +0 -0
  283. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/application.py +0 -0
  284. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/argumentparser.py +0 -0
  285. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/asserts.py +0 -0
  286. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/charwidth.py +0 -0
  287. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/compress.py +0 -0
  288. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/encoding.py +0 -0
  289. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/error.py +0 -0
  290. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/etreewrapper.py +0 -0
  291. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/filereader.py +0 -0
  292. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/frange.py +0 -0
  293. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/htmlformatters.py +0 -0
  294. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/markuputils.py +0 -0
  295. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/match.py +0 -0
  296. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/recommendations.py +0 -0
  297. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/restreader.py +0 -0
  298. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/robotenv.py +0 -0
  299. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/robotinspect.py +0 -0
  300. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/sortable.py +0 -0
  301. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/typehints.py +0 -0
  302. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/finders.py +0 -0
  303. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/notfound.py +0 -0
  304. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/resolvable.py +0 -0
  305. {robotframework-6.1rc1 → robotframework-7.0}/src/robotframework.egg-info/dependency_links.txt +0 -0
  306. {robotframework-6.1rc1 → robotframework-7.0}/src/robotframework.egg-info/entry_points.txt +0 -0
  307. {robotframework-6.1rc1 → robotframework-7.0}/src/robotframework.egg-info/top_level.txt +0 -0
@@ -0,0 +1,1185 @@
1
+ # Copyright 2008-2015 Nokia Networks
2
+ # Copyright 2016- Robot Framework Foundation
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ """Module implementing result related model objects.
17
+
18
+ During test execution these objects are created internally by various runners.
19
+ At that time they can be inspected and modified by listeners__.
20
+
21
+ When results are parsed from XML output files after execution to be able to
22
+ create logs and reports, these objects are created by the
23
+ :func:`~.resultbuilder.ExecutionResult` factory method.
24
+ At that point they can be inspected and modified by `pre-Rebot modifiers`__.
25
+
26
+ The :func:`~.resultbuilder.ExecutionResult` factory method can also be used
27
+ by custom scripts and tools. In such usage it is often easiest to inspect and
28
+ modify these objects using the :mod:`visitor interface <robot.model.visitor>`.
29
+
30
+ If classes defined here are needed, for example, as type hints, they can
31
+ be imported via the :mod:`robot.running` module.
32
+
33
+ __ http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#listener-interface
34
+ __ http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#programmatic-modification-of-results
35
+ """
36
+
37
+ from datetime import datetime, timedelta
38
+ from io import StringIO
39
+ from itertools import chain
40
+ from pathlib import Path
41
+ from typing import Literal, Mapping, overload, Sequence, Union, TextIO, TypeVar
42
+
43
+ from robot import model
44
+ from robot.model import (BodyItem, create_fixture, DataDict, Tags, TestSuites,
45
+ TotalStatistics, TotalStatisticsBuilder)
46
+ from robot.utils import is_dict_like, is_list_like, setter
47
+
48
+ from .configurer import SuiteConfigurer
49
+ from .messagefilter import MessageFilter
50
+ from .modeldeprecation import DeprecatedAttributesMixin
51
+ from .keywordremover import KeywordRemover
52
+ from .suiteteardownfailed import SuiteTeardownFailed, SuiteTeardownFailureHandler
53
+
54
+
55
+ IT = TypeVar('IT', bound='IfBranch|TryBranch')
56
+ FW = TypeVar('FW', bound='ForIteration|WhileIteration')
57
+ BodyItemParent = Union['TestSuite', 'TestCase', 'Keyword', 'For', 'ForIteration', 'If',
58
+ 'IfBranch', 'Try', 'TryBranch', 'While', 'WhileIteration', None]
59
+
60
+
61
+ class Body(model.BaseBody['Keyword', 'For', 'While', 'If', 'Try', 'Var', 'Return',
62
+ 'Continue', 'Break', 'Message', 'Error']):
63
+ __slots__ = ()
64
+
65
+
66
+ class Branches(model.BaseBranches['Keyword', 'For', 'While', 'If', 'Try', 'Var', 'Return',
67
+ 'Continue', 'Break', 'Message', 'Error', IT]):
68
+ __slots__ = ()
69
+
70
+
71
+ class Iterations(model.BaseIterations['Keyword', 'For', 'While', 'If', 'Try', 'Var', 'Return',
72
+ 'Continue', 'Break', 'Message', 'Error', FW]):
73
+ __slots__ = ()
74
+
75
+
76
+ @Body.register
77
+ @Branches.register
78
+ @Iterations.register
79
+ class Message(model.Message):
80
+ __slots__ = ()
81
+
82
+ def to_dict(self) -> DataDict:
83
+ data: DataDict = {
84
+ 'type': self.type,
85
+ 'message': self.message,
86
+ 'level': self.level,
87
+ 'html': self.html,
88
+ }
89
+ if self.timestamp:
90
+ data['timestamp'] = self.timestamp.isoformat()
91
+ return data
92
+
93
+
94
+ class StatusMixin:
95
+ PASS = 'PASS'
96
+ FAIL = 'FAIL'
97
+ SKIP = 'SKIP'
98
+ NOT_RUN = 'NOT RUN'
99
+ NOT_SET = 'NOT SET'
100
+ status: Literal['PASS', 'FAIL', 'SKIP', 'NOT RUN', 'NOT SET']
101
+ __slots__ = ()
102
+
103
+ @property
104
+ def start_time(self) -> 'datetime|None':
105
+ """Execution start time as a ``datetime`` or as a ``None`` if not set.
106
+
107
+ If start time is not set, it is calculated based :attr:`end_time`
108
+ and :attr:`elapsed_time` if possible.
109
+
110
+ Can be set either directly as a ``datetime`` or as a string in ISO 8601
111
+ format.
112
+
113
+ New in Robot Framework 6.1. Heavily enhanced in Robot Framework 7.0.
114
+ """
115
+ if self._start_time:
116
+ return self._start_time
117
+ if self._end_time:
118
+ return self._end_time - self.elapsed_time
119
+ return None
120
+
121
+ @start_time.setter
122
+ def start_time(self, start_time: 'datetime|str|None'):
123
+ if isinstance(start_time, str):
124
+ start_time = datetime.fromisoformat(start_time)
125
+ self._start_time = start_time
126
+
127
+ @property
128
+ def end_time(self) -> 'datetime|None':
129
+ """Execution end time as a ``datetime`` or as a ``None`` if not set.
130
+
131
+ If end time is not set, it is calculated based :attr:`start_time`
132
+ and :attr:`elapsed_time` if possible.
133
+
134
+ Can be set either directly as a ``datetime`` or as a string in ISO 8601
135
+ format.
136
+
137
+ New in Robot Framework 6.1. Heavily enhanced in Robot Framework 7.0.
138
+ """
139
+ if self._end_time:
140
+ return self._end_time
141
+ if self._start_time:
142
+ return self._start_time + self.elapsed_time
143
+ return None
144
+
145
+ @end_time.setter
146
+ def end_time(self, end_time: 'datetime|str|None'):
147
+ if isinstance(end_time, str):
148
+ end_time = datetime.fromisoformat(end_time)
149
+ self._end_time = end_time
150
+
151
+ @property
152
+ def elapsed_time(self) -> timedelta:
153
+ """Total execution time as a ``timedelta``.
154
+
155
+ If not set, calculated based on :attr:`start_time` and :attr:`end_time`
156
+ if possible. If that fails, calculated based on the elapsed time of
157
+ child items.
158
+
159
+ Can be set either directly as a ``timedelta`` or as an integer or a float
160
+ representing seconds.
161
+
162
+ New in Robot Framework 6.1. Heavily enhanced in Robot Framework 7.0.
163
+ """
164
+ if self._elapsed_time is not None:
165
+ return self._elapsed_time
166
+ if self._start_time and self._end_time:
167
+ return self._end_time - self._start_time
168
+ return self._elapsed_time_from_children()
169
+
170
+ def _elapsed_time_from_children(self) -> timedelta:
171
+ elapsed = timedelta()
172
+ for child in self.body:
173
+ if hasattr(child, 'elapsed_time'):
174
+ elapsed += child.elapsed_time
175
+ if getattr(self, 'has_setup', False):
176
+ elapsed += self.setup.elapsed_time
177
+ if getattr(self, 'has_teardown', False):
178
+ elapsed += self.teardown.elapsed_time
179
+ return elapsed
180
+
181
+ @elapsed_time.setter
182
+ def elapsed_time(self, elapsed_time: 'timedelta|int|float|None'):
183
+ if isinstance(elapsed_time, (int, float)):
184
+ elapsed_time = timedelta(seconds=elapsed_time)
185
+ self._elapsed_time = elapsed_time
186
+
187
+ @property
188
+ def starttime(self) -> 'str|None':
189
+ """Execution start time as a string or as a ``None`` if not set.
190
+
191
+ The string format is ``%Y%m%d %H:%M:%S.%f``.
192
+
193
+ Considered deprecated starting from Robot Framework 7.0.
194
+ :attr:`start_time` should be used instead.
195
+ """
196
+ return self._datetime_to_timestr(self.start_time)
197
+
198
+ @starttime.setter
199
+ def starttime(self, starttime: 'str|None'):
200
+ self.start_time = self._timestr_to_datetime(starttime)
201
+
202
+ @property
203
+ def endtime(self) -> 'str|None':
204
+ """Execution end time as a string or as a ``None`` if not set.
205
+
206
+ The string format is ``%Y%m%d %H:%M:%S.%f``.
207
+
208
+ Considered deprecated starting from Robot Framework 7.0.
209
+ :attr:`end_time` should be used instead.
210
+ """
211
+ return self._datetime_to_timestr(self.end_time)
212
+
213
+ @endtime.setter
214
+ def endtime(self, endtime: 'str|None'):
215
+ self.end_time = self._timestr_to_datetime(endtime)
216
+
217
+ @property
218
+ def elapsedtime(self) -> int:
219
+ """Total execution time in milliseconds.
220
+
221
+ Considered deprecated starting from Robot Framework 7.0.
222
+ :attr:`elapsed_time` should be used instead.
223
+ """
224
+ return round(self.elapsed_time.total_seconds() * 1000)
225
+
226
+ def _timestr_to_datetime(self, ts: 'str|None') -> 'datetime|None':
227
+ if not ts:
228
+ return None
229
+ ts = ts.ljust(24, '0')
230
+ return datetime(int(ts[:4]), int(ts[4:6]), int(ts[6:8]),
231
+ int(ts[9:11]), int(ts[12:14]), int(ts[15:17]), int(ts[18:24]))
232
+
233
+ def _datetime_to_timestr(self, dt: 'datetime|None') -> 'str|None':
234
+ if not dt:
235
+ return None
236
+ return dt.isoformat(' ', timespec='milliseconds').replace('-', '')
237
+
238
+ @property
239
+ def passed(self) -> bool:
240
+ """``True`` when :attr:`status` is 'PASS', ``False`` otherwise."""
241
+ return self.status == self.PASS
242
+
243
+ @passed.setter
244
+ def passed(self, passed: bool):
245
+ self.status = self.PASS if passed else self.FAIL
246
+
247
+ @property
248
+ def failed(self) -> bool:
249
+ """``True`` when :attr:`status` is 'FAIL', ``False`` otherwise."""
250
+ return self.status == self.FAIL
251
+
252
+ @failed.setter
253
+ def failed(self, failed: bool):
254
+ self.status = self.FAIL if failed else self.PASS
255
+
256
+ @property
257
+ def skipped(self) -> bool:
258
+ """``True`` when :attr:`status` is 'SKIP', ``False`` otherwise.
259
+
260
+ Setting to ``False`` value is ambiguous and raises an exception.
261
+ """
262
+ return self.status == self.SKIP
263
+
264
+ @skipped.setter
265
+ def skipped(self, skipped: Literal[True]):
266
+ if not skipped:
267
+ raise ValueError(f"`skipped` value must be truthy, got '{skipped}'.")
268
+ self.status = self.SKIP
269
+
270
+ @property
271
+ def not_run(self) -> bool:
272
+ """``True`` when :attr:`status` is 'NOT RUN', ``False`` otherwise.
273
+
274
+ Setting to ``False`` value is ambiguous and raises an exception.
275
+ """
276
+ return self.status == self.NOT_RUN
277
+
278
+ @not_run.setter
279
+ def not_run(self, not_run: Literal[True]):
280
+ if not not_run:
281
+ raise ValueError(f"`not_run` value must be truthy, got '{not_run}'.")
282
+ self.status = self.NOT_RUN
283
+
284
+ def to_dict(self):
285
+ data = {'status': self.status,
286
+ 'elapsed_time': self.elapsed_time.total_seconds()}
287
+ if self.start_time:
288
+ data['start_time'] = self.start_time.isoformat()
289
+ if self.message:
290
+ data['message'] = self.message
291
+ return data
292
+
293
+
294
+ class ForIteration(model.ForIteration, StatusMixin, DeprecatedAttributesMixin):
295
+ body_class = Body
296
+ __slots__ = ['assign', 'message', 'status', '_start_time', '_end_time',
297
+ '_elapsed_time']
298
+
299
+ def __init__(self, assign: 'Mapping[str, str]|None' = None,
300
+ status: str = 'FAIL',
301
+ message: str = '',
302
+ start_time: 'datetime|str|None' = None,
303
+ end_time: 'datetime|str|None' = None,
304
+ elapsed_time: 'timedelta|int|float|None' = None,
305
+ parent: BodyItemParent = None):
306
+ super().__init__(assign, parent)
307
+ self.status = status
308
+ self.message = message
309
+ self.start_time = start_time
310
+ self.end_time = end_time
311
+ self.elapsed_time = elapsed_time
312
+
313
+ def to_dict(self) -> DataDict:
314
+ return {**super().to_dict(), **StatusMixin.to_dict(self)}
315
+
316
+
317
+ @Body.register
318
+ class For(model.For, StatusMixin, DeprecatedAttributesMixin):
319
+ iteration_class = ForIteration
320
+ iterations_class = Iterations[iteration_class]
321
+ __slots__ = ['status', 'message', '_start_time', '_end_time', '_elapsed_time']
322
+
323
+ def __init__(self, assign: Sequence[str] = (),
324
+ flavor: Literal['IN', 'IN RANGE', 'IN ENUMERATE', 'IN ZIP'] = 'IN',
325
+ values: Sequence[str] = (),
326
+ start: 'str|None' = None,
327
+ mode: 'str|None' = None,
328
+ fill: 'str|None' = None,
329
+ status: str = 'FAIL',
330
+ message: str = '',
331
+ start_time: 'datetime|str|None' = None,
332
+ end_time: 'datetime|str|None' = None,
333
+ elapsed_time: 'timedelta|int|float|None' = None,
334
+ parent: BodyItemParent = None):
335
+ super().__init__(assign, flavor, values, start, mode, fill, parent)
336
+ self.status = status
337
+ self.message = message
338
+ self.start_time = start_time
339
+ self.end_time = end_time
340
+ self.elapsed_time = elapsed_time
341
+
342
+ @setter
343
+ def body(self, iterations: 'Sequence[ForIteration|DataDict]') -> iterations_class:
344
+ return self.iterations_class(self.iteration_class, self, iterations)
345
+
346
+ @property
347
+ def _log_name(self):
348
+ return str(self)[7:] # Drop 'FOR ' prefix.
349
+
350
+ def to_dict(self) -> DataDict:
351
+ return {**super().to_dict(), **StatusMixin.to_dict(self)}
352
+
353
+
354
+ class WhileIteration(model.WhileIteration, StatusMixin, DeprecatedAttributesMixin):
355
+ body_class = Body
356
+ __slots__ = ['status', 'message', '_start_time', '_end_time', '_elapsed_time']
357
+
358
+ def __init__(self, status: str = 'FAIL',
359
+ message: str = '',
360
+ start_time: 'datetime|str|None' = None,
361
+ end_time: 'datetime|str|None' = None,
362
+ elapsed_time: 'timedelta|int|float|None' = None,
363
+ parent: BodyItemParent = None):
364
+ super().__init__(parent)
365
+ self.status = status
366
+ self.message = message
367
+ self.start_time = start_time
368
+ self.end_time = end_time
369
+ self.elapsed_time = elapsed_time
370
+
371
+ def to_dict(self) -> DataDict:
372
+ return {**super().to_dict(), **StatusMixin.to_dict(self)}
373
+
374
+
375
+ @Body.register
376
+ class While(model.While, StatusMixin, DeprecatedAttributesMixin):
377
+ iteration_class = WhileIteration
378
+ iterations_class = Iterations[iteration_class]
379
+ __slots__ = ['status', 'message', '_start_time', '_end_time', '_elapsed_time']
380
+
381
+ def __init__(self, condition: 'str|None' = None,
382
+ limit: 'str|None' = None,
383
+ on_limit: 'str|None' = None,
384
+ on_limit_message: 'str|None' = None,
385
+ status: str = 'FAIL',
386
+ message: str = '',
387
+ start_time: 'datetime|str|None' = None,
388
+ end_time: 'datetime|str|None' = None,
389
+ elapsed_time: 'timedelta|int|float|None' = None,
390
+ parent: BodyItemParent = None):
391
+ super().__init__(condition, limit, on_limit, on_limit_message, parent)
392
+ self.status = status
393
+ self.message = message
394
+ self.start_time = start_time
395
+ self.end_time = end_time
396
+ self.elapsed_time = elapsed_time
397
+
398
+ @setter
399
+ def body(self, iterations: 'Sequence[WhileIteration|DataDict]') -> iterations_class:
400
+ return self.iterations_class(self.iteration_class, self, iterations)
401
+
402
+ @property
403
+ def _log_name(self):
404
+ return str(self)[9:] # Drop 'WHILE ' prefix.
405
+
406
+ def to_dict(self) -> DataDict:
407
+ return {**super().to_dict(), **StatusMixin.to_dict(self)}
408
+
409
+
410
+ class IfBranch(model.IfBranch, StatusMixin, DeprecatedAttributesMixin):
411
+ body_class = Body
412
+ __slots__ = ['status', 'message', '_start_time', '_end_time', '_elapsed_time']
413
+
414
+ def __init__(self, type: str = BodyItem.IF,
415
+ condition: 'str|None' = None,
416
+ status: str = 'FAIL',
417
+ message: str = '',
418
+ start_time: 'datetime|str|None' = None,
419
+ end_time: 'datetime|str|None' = None,
420
+ elapsed_time: 'timedelta|int|float|None' = None,
421
+ parent: BodyItemParent = None):
422
+ super().__init__(type, condition, parent)
423
+ self.status = status
424
+ self.message = message
425
+ self.start_time = start_time
426
+ self.end_time = end_time
427
+ self.elapsed_time = elapsed_time
428
+
429
+ @property
430
+ def _log_name(self):
431
+ return self.condition or ''
432
+
433
+ def to_dict(self) -> DataDict:
434
+ return {**super().to_dict(), **StatusMixin.to_dict(self)}
435
+
436
+
437
+ @Body.register
438
+ class If(model.If, StatusMixin, DeprecatedAttributesMixin):
439
+ branch_class = IfBranch
440
+ branches_class = Branches[branch_class]
441
+ __slots__ = ['status', 'message', '_start_time', '_end_time', '_elapsed_time']
442
+
443
+ def __init__(self, status: str = 'FAIL',
444
+ message: str = '',
445
+ start_time: 'datetime|str|None' = None,
446
+ end_time: 'datetime|str|None' = None,
447
+ elapsed_time: 'timedelta|int|float|None' = None,
448
+ parent: BodyItemParent = None):
449
+ super().__init__(parent)
450
+ self.status = status
451
+ self.message = message
452
+ self.start_time = start_time
453
+ self.end_time = end_time
454
+ self.elapsed_time = elapsed_time
455
+
456
+ def to_dict(self) -> DataDict:
457
+ return {**super().to_dict(), **StatusMixin.to_dict(self)}
458
+
459
+
460
+ class TryBranch(model.TryBranch, StatusMixin, DeprecatedAttributesMixin):
461
+ body_class = Body
462
+ __slots__ = ['status', 'message', '_start_time', '_end_time', '_elapsed_time']
463
+
464
+ def __init__(self, type: str = BodyItem.TRY,
465
+ patterns: Sequence[str] = (),
466
+ pattern_type: 'str|None' = None,
467
+ assign: 'str|None' = None,
468
+ status: str = 'FAIL',
469
+ message: str = '',
470
+ start_time: 'datetime|str|None' = None,
471
+ end_time: 'datetime|str|None' = None,
472
+ elapsed_time: 'timedelta|int|float|None' = None,
473
+ parent: BodyItemParent = None):
474
+ super().__init__(type, patterns, pattern_type, assign, parent)
475
+ self.status = status
476
+ self.message = message
477
+ self.start_time = start_time
478
+ self.end_time = end_time
479
+ self.elapsed_time = elapsed_time
480
+
481
+ @property
482
+ def _log_name(self):
483
+ return str(self)[len(self.type)+4:] # Drop '<type> ' prefix.
484
+
485
+ def to_dict(self) -> DataDict:
486
+ return {**super().to_dict(), **StatusMixin.to_dict(self)}
487
+
488
+
489
+ @Body.register
490
+ class Try(model.Try, StatusMixin, DeprecatedAttributesMixin):
491
+ branch_class = TryBranch
492
+ branches_class = Branches[branch_class]
493
+ __slots__ = ['status', 'message', '_start_time', '_end_time', '_elapsed_time']
494
+
495
+ def __init__(self, status: str = 'FAIL',
496
+ message: str = '',
497
+ start_time: 'datetime|str|None' = None,
498
+ end_time: 'datetime|str|None' = None,
499
+ elapsed_time: 'timedelta|int|float|None' = None,
500
+ parent: BodyItemParent = None):
501
+ super().__init__(parent)
502
+ self.status = status
503
+ self.message = message
504
+ self.start_time = start_time
505
+ self.end_time = end_time
506
+ self.elapsed_time = elapsed_time
507
+
508
+ def to_dict(self) -> DataDict:
509
+ return {**super().to_dict(), **StatusMixin.to_dict(self)}
510
+
511
+
512
+ @Body.register
513
+ class Var(model.Var, StatusMixin, DeprecatedAttributesMixin):
514
+ __slots__ = ['status', 'message', '_start_time', '_end_time', '_elapsed_time']
515
+ body_class = Body
516
+
517
+ def __init__(self, name: str = '',
518
+ value: 'str|Sequence[str]' = (),
519
+ scope: 'str|None' = None,
520
+ separator: 'str|None' = None,
521
+ status: str = 'FAIL',
522
+ message: str = '',
523
+ start_time: 'datetime|str|None' = None,
524
+ end_time: 'datetime|str|None' = None,
525
+ elapsed_time: 'timedelta|int|float|None' = None,
526
+ parent: BodyItemParent = None):
527
+ super().__init__(name, value, scope, separator, parent)
528
+ self.status = status
529
+ self.message = message
530
+ self.start_time = start_time
531
+ self.end_time = end_time
532
+ self.elapsed_time = elapsed_time
533
+ self.body = ()
534
+
535
+ @setter
536
+ def body(self, body: 'Sequence[BodyItem|DataDict]') -> Body:
537
+ """Child keywords and messages as a :class:`~.Body` object.
538
+
539
+ Typically empty. Only contains something if running VAR has failed
540
+ due to a syntax error or listeners have logged messages or executed
541
+ keywords.
542
+ """
543
+ return self.body_class(self, body)
544
+
545
+ @property
546
+ def _log_name(self):
547
+ return str(self)[7:] # Drop 'VAR ' prefix.
548
+
549
+ def to_dict(self) -> DataDict:
550
+ data = {**super().to_dict(), **StatusMixin.to_dict(self)}
551
+ if self.body:
552
+ data['body'] = self.body.to_dicts()
553
+ return data
554
+
555
+
556
+ @Body.register
557
+ class Return(model.Return, StatusMixin, DeprecatedAttributesMixin):
558
+ __slots__ = ['status', 'message', '_start_time', '_end_time', '_elapsed_time']
559
+ body_class = Body
560
+
561
+ def __init__(self, values: Sequence[str] = (),
562
+ status: str = 'FAIL',
563
+ message: str = '',
564
+ start_time: 'datetime|str|None' = None,
565
+ end_time: 'datetime|str|None' = None,
566
+ elapsed_time: 'timedelta|int|float|None' = None,
567
+ parent: BodyItemParent = None):
568
+ super().__init__(values, parent)
569
+ self.status = status
570
+ self.message = message
571
+ self.start_time = start_time
572
+ self.end_time = end_time
573
+ self.elapsed_time = elapsed_time
574
+ self.body = ()
575
+
576
+ @setter
577
+ def body(self, body: 'Sequence[BodyItem|DataDict]') -> Body:
578
+ """Child keywords and messages as a :class:`~.Body` object.
579
+
580
+ Typically empty. Only contains something if running RETURN has failed
581
+ due to a syntax error or listeners have logged messages or executed
582
+ keywords.
583
+ """
584
+ return self.body_class(self, body)
585
+
586
+ def to_dict(self) -> DataDict:
587
+ data = {**super().to_dict(), **StatusMixin.to_dict(self)}
588
+ if self.body:
589
+ data['body'] = self.body.to_dicts()
590
+ return data
591
+
592
+
593
+ @Body.register
594
+ class Continue(model.Continue, StatusMixin, DeprecatedAttributesMixin):
595
+ __slots__ = ['status', 'message', '_start_time', '_end_time', '_elapsed_time']
596
+ body_class = Body
597
+
598
+ def __init__(self, status: str = 'FAIL',
599
+ message: str = '',
600
+ start_time: 'datetime|str|None' = None,
601
+ end_time: 'datetime|str|None' = None,
602
+ elapsed_time: 'timedelta|int|float|None' = None,
603
+ parent: BodyItemParent = None):
604
+ super().__init__(parent)
605
+ self.status = status
606
+ self.message = message
607
+ self.start_time = start_time
608
+ self.end_time = end_time
609
+ self.elapsed_time = elapsed_time
610
+ self.body = ()
611
+
612
+ @setter
613
+ def body(self, body: 'Sequence[BodyItem|DataDict]') -> Body:
614
+ """Child keywords and messages as a :class:`~.Body` object.
615
+
616
+ Typically empty. Only contains something if running CONTINUE has failed
617
+ due to a syntax error or listeners have logged messages or executed
618
+ keywords.
619
+ """
620
+ return self.body_class(self, body)
621
+
622
+ def to_dict(self) -> DataDict:
623
+ data = {**super().to_dict(), **StatusMixin.to_dict(self)}
624
+ if self.body:
625
+ data['body'] = self.body.to_dicts()
626
+ return data
627
+
628
+
629
+ @Body.register
630
+ class Break(model.Break, StatusMixin, DeprecatedAttributesMixin):
631
+ __slots__ = ['status', 'message', '_start_time', '_end_time', '_elapsed_time']
632
+ body_class = Body
633
+
634
+ def __init__(self, status: str = 'FAIL',
635
+ message: str = '',
636
+ start_time: 'datetime|str|None' = None,
637
+ end_time: 'datetime|str|None' = None,
638
+ elapsed_time: 'timedelta|int|float|None' = None,
639
+ parent: BodyItemParent = None):
640
+ super().__init__(parent)
641
+ self.status = status
642
+ self.message = message
643
+ self.start_time = start_time
644
+ self.end_time = end_time
645
+ self.elapsed_time = elapsed_time
646
+ self.body = ()
647
+
648
+ @setter
649
+ def body(self, body: 'Sequence[BodyItem|DataDict]') -> Body:
650
+ """Child keywords and messages as a :class:`~.Body` object.
651
+
652
+ Typically empty. Only contains something if running BREAK has failed
653
+ due to a syntax error or listeners have logged messages or executed
654
+ keywords.
655
+ """
656
+ return self.body_class(self, body)
657
+
658
+ def to_dict(self) -> DataDict:
659
+ data = {**super().to_dict(), **StatusMixin.to_dict(self)}
660
+ if self.body:
661
+ data['body'] = self.body.to_dicts()
662
+ return data
663
+
664
+
665
+ @Body.register
666
+ class Error(model.Error, StatusMixin, DeprecatedAttributesMixin):
667
+ __slots__ = ['status', 'message', '_start_time', '_end_time', '_elapsed_time']
668
+ body_class = Body
669
+
670
+ def __init__(self, values: Sequence[str] = (),
671
+ status: str = 'FAIL',
672
+ message: str = '',
673
+ start_time: 'datetime|str|None' = None,
674
+ end_time: 'datetime|str|None' = None,
675
+ elapsed_time: 'timedelta|int|float|None' = None,
676
+ parent: BodyItemParent = None):
677
+ super().__init__(values, parent)
678
+ self.status = status
679
+ self.message = message
680
+ self.start_time = start_time
681
+ self.end_time = end_time
682
+ self.elapsed_time = elapsed_time
683
+ self.body = ()
684
+
685
+ @setter
686
+ def body(self, body: 'Sequence[BodyItem|DataDict]') -> Body:
687
+ """Messages as a :class:`~.Body` object.
688
+
689
+ Typically contains the message that caused the error.
690
+ """
691
+ return self.body_class(self, body)
692
+
693
+ def to_dict(self) -> DataDict:
694
+ data = {**super().to_dict(), **StatusMixin.to_dict(self)}
695
+ if self.body:
696
+ data['body'] = self.body.to_dicts()
697
+ return data
698
+
699
+
700
+ @Body.register
701
+ @Branches.register
702
+ @Iterations.register
703
+ class Keyword(model.Keyword, StatusMixin):
704
+ """Represents an executed library or user keyword."""
705
+ body_class = Body
706
+ __slots__ = ['owner', 'source_name', 'doc', 'timeout', 'status', 'message',
707
+ '_start_time', '_end_time', '_elapsed_time', '_setup', '_teardown']
708
+
709
+ def __init__(self, name: 'str|None' = '',
710
+ owner: 'str|None' = None,
711
+ source_name: 'str|None' = None,
712
+ doc: str = '',
713
+ args: model.Arguments = (),
714
+ assign: Sequence[str] = (),
715
+ tags: Sequence[str] = (),
716
+ timeout: 'str|None' = None,
717
+ type: str = BodyItem.KEYWORD,
718
+ status: str = 'FAIL',
719
+ message: str = '',
720
+ start_time: 'datetime|str|None' = None,
721
+ end_time: 'datetime|str|None' = None,
722
+ elapsed_time: 'timedelta|int|float|None' = None,
723
+ parent: BodyItemParent = None):
724
+ super().__init__(name, args, assign, type, parent)
725
+ #: Name of the library or resource containing this keyword.
726
+ self.owner = owner
727
+ #: Original name of keyword with embedded arguments.
728
+ self.source_name = source_name
729
+ self.doc = doc
730
+ self.tags = tags
731
+ self.timeout = timeout
732
+ self.status = status
733
+ self.message = message
734
+ self.start_time = start_time
735
+ self.end_time = end_time
736
+ self.elapsed_time = elapsed_time
737
+ self._setup = None
738
+ self._teardown = None
739
+ self.body = ()
740
+
741
+ @setter
742
+ def args(self, args: model.Arguments) -> 'tuple[str, ...]':
743
+ """Keyword arguments.
744
+
745
+ Arguments originating from normal data are given as a list of strings.
746
+ Programmatically it is possible to use also other types and named arguments
747
+ can be specified using name-value tuples. Additionally, it is possible
748
+ o give arguments directly as a list of positional arguments and a dictionary
749
+ of named arguments. In all these cases arguments are stored as strings.
750
+ """
751
+ if len(args) == 2 and is_list_like(args[0]) and is_dict_like(args[1]):
752
+ positional = [str(a) for a in args[0]]
753
+ named = [f'{n}={v}' for n, v in args[1].items()]
754
+ return tuple(positional + named)
755
+ return tuple([a if isinstance(a, str) else self._arg_to_str(a) for a in args])
756
+
757
+ def _arg_to_str(self, arg):
758
+ if isinstance(arg, tuple):
759
+ if len(arg) == 2:
760
+ return f'{arg[0]}={arg[1]}'
761
+ if len(arg) == 1:
762
+ return str(arg[0])
763
+ return str(arg)
764
+
765
+ @setter
766
+ def body(self, body: 'Sequence[BodyItem|DataDict]') -> Body:
767
+ """Possible keyword body as a :class:`~.Body` object.
768
+
769
+ Body can consist of child keywords, messages, and control structures
770
+ such as IF/ELSE. Library keywords typically have an empty body.
771
+ """
772
+ return self.body_class(self, body)
773
+
774
+ @property
775
+ def messages(self) -> 'list[Message]':
776
+ """Keyword's messages.
777
+
778
+ Starting from Robot Framework 4.0 this is a list generated from messages
779
+ in :attr:`body`.
780
+ """
781
+ return self.body.filter(messages=True) # type: ignore
782
+
783
+ @property
784
+ def full_name(self) -> 'str|None':
785
+ """Keyword name in format ``owner.name``.
786
+
787
+ Just ``name`` if :attr:`owner` is not set. In practice this is the
788
+ case only with user keywords in the suite file.
789
+
790
+ Cannot be set directly. Set :attr:`name` and :attr:`owner` separately
791
+ instead.
792
+
793
+ Notice that prior to Robot Framework 7.0, the ``name`` attribute contained
794
+ the full name and keyword and owner names were in ``kwname`` and ``libname``,
795
+ respectively.
796
+ """
797
+ return f'{self.owner}.{self.name}' if self.owner else self.name
798
+
799
+ # TODO: Deprecate 'kwname', 'libname' and 'sourcename' loudly in RF 8.
800
+ @property
801
+ def kwname(self) -> 'str|None':
802
+ """Deprecated since Robot Framework 7.0. Use :attr:`name` instead."""
803
+ return self.name
804
+
805
+ @kwname.setter
806
+ def kwname(self, name: 'str|None'):
807
+ self.name = name
808
+
809
+ @property
810
+ def libname(self) -> 'str|None':
811
+ """Deprecated since Robot Framework 7.0. Use :attr:`owner` instead."""
812
+ return self.owner
813
+
814
+ @libname.setter
815
+ def libname(self, name: 'str|None'):
816
+ self.owner = name
817
+
818
+ @property
819
+ def sourcename(self) -> str:
820
+ """Deprecated since Robot Framework 7.0. Use :attr:`source_name` instead."""
821
+ return self.source_name
822
+
823
+ @sourcename.setter
824
+ def sourcename(self, name: str):
825
+ self.source_name = name
826
+
827
+ @property
828
+ def setup(self) -> 'Keyword':
829
+ """Keyword setup as a :class:`Keyword` object.
830
+
831
+ See :attr:`teardown` for more information. New in Robot Framework 7.0.
832
+ """
833
+ if self._setup is None:
834
+ self.setup = None
835
+ return self._setup
836
+
837
+ @setup.setter
838
+ def setup(self, setup: 'Keyword|DataDict|None'):
839
+ self._setup = create_fixture(self.__class__, setup, self, self.SETUP)
840
+
841
+ @property
842
+ def has_setup(self) -> bool:
843
+ """Check does a keyword have a setup without creating a setup object.
844
+
845
+ See :attr:`has_teardown` for more information. New in Robot Framework 7.0.
846
+ """
847
+ return bool(self._setup)
848
+
849
+ @property
850
+ def teardown(self) -> 'Keyword':
851
+ """Keyword teardown as a :class:`Keyword` object.
852
+
853
+ Teardown can be modified by setting attributes directly::
854
+
855
+ keyword.teardown.name = 'Example'
856
+ keyword.teardown.args = ('First', 'Second')
857
+
858
+ Alternatively the :meth:`config` method can be used to set multiple
859
+ attributes in one call::
860
+
861
+ keyword.teardown.config(name='Example', args=('First', 'Second'))
862
+
863
+ The easiest way to reset the whole teardown is setting it to ``None``.
864
+ It will automatically recreate the underlying ``Keyword`` object::
865
+
866
+ keyword.teardown = None
867
+
868
+ This attribute is a ``Keyword`` object also when a keyword has no teardown
869
+ but in that case its truth value is ``False``. If there is a need to just
870
+ check does a keyword have a teardown, using the :attr:`has_teardown`
871
+ attribute avoids creating the ``Keyword`` object and is thus more memory
872
+ efficient.
873
+
874
+ New in Robot Framework 4.0. Earlier teardown was accessed like
875
+ ``keyword.keywords.teardown``. :attr:`has_teardown` is new in Robot
876
+ Framework 4.1.2.
877
+ """
878
+ if self._teardown is None:
879
+ self.teardown = None
880
+ return self._teardown
881
+
882
+ @teardown.setter
883
+ def teardown(self, teardown: 'Keyword|DataDict|None'):
884
+ self._teardown = create_fixture(self.__class__, teardown, self, self.TEARDOWN)
885
+
886
+ @property
887
+ def has_teardown(self) -> bool:
888
+ """Check does a keyword have a teardown without creating a teardown object.
889
+
890
+ A difference between using ``if kw.has_teardown:`` and ``if kw.teardown:``
891
+ is that accessing the :attr:`teardown` attribute creates a :class:`Keyword`
892
+ object representing a teardown even when the keyword actually does not
893
+ have one. This typically does not matter, but with bigger suite structures
894
+ having lots of keywords it can have a considerable effect on memory usage.
895
+
896
+ New in Robot Framework 4.1.2.
897
+ """
898
+ return bool(self._teardown)
899
+
900
+ @setter
901
+ def tags(self, tags: Sequence[str]) -> model.Tags:
902
+ """Keyword tags as a :class:`~.model.tags.Tags` object."""
903
+ return Tags(tags)
904
+
905
+ def to_dict(self) -> DataDict:
906
+ data = {**super().to_dict(), **StatusMixin.to_dict(self)}
907
+ if self.owner:
908
+ data['owner'] = self.owner
909
+ if self.source_name:
910
+ data['source_name'] = self.source_name
911
+ if self.doc:
912
+ data['doc'] = self.doc
913
+ if self.tags:
914
+ data['tags'] = list(self.tags)
915
+ if self.timeout:
916
+ data['timeout'] = self.timeout
917
+ if self.body:
918
+ data['body'] = self.body.to_dicts()
919
+ if self.has_setup:
920
+ data['setup'] = self.setup.to_dict()
921
+ if self.has_teardown:
922
+ data['teardown'] = self.teardown.to_dict()
923
+ return data
924
+
925
+
926
+ class TestCase(model.TestCase[Keyword], StatusMixin):
927
+ """Represents results of a single test case.
928
+
929
+ See the base class for documentation of attributes not documented here.
930
+ """
931
+ __slots__ = ['status', 'message', '_start_time', '_end_time', '_elapsed_time']
932
+ body_class = Body
933
+ fixture_class = Keyword
934
+
935
+ def __init__(self, name: str = '',
936
+ doc: str = '',
937
+ tags: Sequence[str] = (),
938
+ timeout: 'str|None' = None,
939
+ lineno: 'int|None' = None,
940
+ status: str = 'FAIL',
941
+ message: str = '',
942
+ start_time: 'datetime|str|None' = None,
943
+ end_time: 'datetime|str|None' = None,
944
+ elapsed_time: 'timedelta|int|float|None' = None,
945
+ parent: 'TestSuite|None' = None):
946
+ super().__init__(name, doc, tags, timeout, lineno, parent)
947
+ self.status = status
948
+ self.message = message
949
+ self.start_time = start_time
950
+ self.end_time = end_time
951
+ self.elapsed_time = elapsed_time
952
+
953
+ @property
954
+ def not_run(self) -> bool:
955
+ return False
956
+
957
+ @setter
958
+ def body(self, body: 'Sequence[BodyItem|DataDict]') -> Body:
959
+ """Test body as a :class:`~robot.result.Body` object."""
960
+ return self.body_class(self, body)
961
+
962
+ def to_dict(self) -> DataDict:
963
+ return {**super().to_dict(), **StatusMixin.to_dict(self)}
964
+
965
+
966
+ class TestSuite(model.TestSuite[Keyword, TestCase], StatusMixin):
967
+ """Represents results of a single test suite.
968
+
969
+ See the base class for documentation of attributes not documented here.
970
+ """
971
+ __slots__ = ['message', '_start_time', '_end_time', '_elapsed_time']
972
+ test_class = TestCase
973
+ fixture_class = Keyword
974
+
975
+ def __init__(self, name: str = '',
976
+ doc: str = '',
977
+ metadata: 'Mapping[str, str]|None' = None,
978
+ source: 'Path|str|None' = None,
979
+ rpa: bool = False,
980
+ message: str = '',
981
+ start_time: 'datetime|str|None' = None,
982
+ end_time: 'datetime|str|None' = None,
983
+ elapsed_time: 'timedelta|int|float|None' = None,
984
+ parent: 'TestSuite|None' = None):
985
+ super().__init__(name, doc, metadata, source, rpa, parent)
986
+ #: Possible suite setup or teardown error message.
987
+ self.message = message
988
+ self.start_time = start_time
989
+ self.end_time = end_time
990
+ self.elapsed_time = elapsed_time
991
+
992
+ def _elapsed_time_from_children(self) -> timedelta:
993
+ elapsed = timedelta()
994
+ if self.has_setup:
995
+ elapsed += self.setup.elapsed_time
996
+ if self.has_teardown:
997
+ elapsed += self.teardown.elapsed_time
998
+ for child in chain(self.suites, self.tests):
999
+ elapsed += child.elapsed_time
1000
+ return elapsed
1001
+
1002
+ @property
1003
+ def passed(self) -> bool:
1004
+ """``True`` if no test has failed but some have passed, ``False`` otherwise."""
1005
+ return self.status == self.PASS
1006
+
1007
+ @property
1008
+ def failed(self) -> bool:
1009
+ """``True`` if any test has failed, ``False`` otherwise."""
1010
+ return self.status == self.FAIL
1011
+
1012
+ @property
1013
+ def skipped(self) -> bool:
1014
+ """``True`` if there are no passed or failed tests, ``False`` otherwise."""
1015
+ return self.status == self.SKIP
1016
+
1017
+ @property
1018
+ def not_run(self) -> bool:
1019
+ return False
1020
+
1021
+ @property
1022
+ def status(self) -> Literal['PASS', 'SKIP', 'FAIL']:
1023
+ """'PASS', 'FAIL' or 'SKIP' depending on test statuses.
1024
+
1025
+ - If any test has failed, status is 'FAIL'.
1026
+ - If no test has failed but at least some test has passed, status is 'PASS'.
1027
+ - If there are no failed or passed tests, status is 'SKIP'. This covers both
1028
+ the case when all tests have been skipped and when there are no tests.
1029
+ """
1030
+ stats = self.statistics # Local variable avoids recreating stats.
1031
+ if stats.failed:
1032
+ return self.FAIL
1033
+ if stats.passed:
1034
+ return self.PASS
1035
+ return self.SKIP
1036
+
1037
+ @property
1038
+ def statistics(self) -> TotalStatistics:
1039
+ """Suite statistics as a :class:`~robot.model.totalstatistics.TotalStatistics` object.
1040
+
1041
+ Recreated every time this property is accessed, so saving the results
1042
+ to a variable and inspecting it is often a good idea::
1043
+
1044
+ stats = suite.statistics
1045
+ print(stats.failed)
1046
+ print(stats.total)
1047
+ print(stats.message)
1048
+ """
1049
+ return TotalStatisticsBuilder(self, bool(self.rpa)).stats
1050
+
1051
+ @property
1052
+ def full_message(self) -> str:
1053
+ """Combination of :attr:`message` and :attr:`stat_message`."""
1054
+ if not self.message:
1055
+ return self.stat_message
1056
+ return f'{self.message}\n\n{self.stat_message}'
1057
+
1058
+ @property
1059
+ def stat_message(self) -> str:
1060
+ """String representation of the :attr:`statistics`."""
1061
+ return self.statistics.message
1062
+
1063
+ @setter
1064
+ def suites(self, suites: 'Sequence[TestSuite|DataDict]') -> TestSuites['TestSuite']:
1065
+ return TestSuites['TestSuite'](self.__class__, self, suites)
1066
+
1067
+ def remove_keywords(self, how: str):
1068
+ """Remove keywords based on the given condition.
1069
+
1070
+ :param how: Which approach to use when removing keywords. Either
1071
+ ``ALL``, ``PASSED``, ``FOR``, ``WUKS``, or ``NAME:<pattern>``.
1072
+
1073
+ For more information about the possible values see the documentation
1074
+ of the ``--removekeywords`` command line option.
1075
+ """
1076
+ self.visit(KeywordRemover.from_config(how))
1077
+
1078
+ def filter_messages(self, log_level: str = 'TRACE'):
1079
+ """Remove log messages below the specified ``log_level``."""
1080
+ self.visit(MessageFilter(log_level))
1081
+
1082
+ def configure(self, **options):
1083
+ """A shortcut to configure a suite using one method call.
1084
+
1085
+ Can only be used with the root test suite.
1086
+
1087
+ :param options: Passed to
1088
+ :class:`~robot.result.configurer.SuiteConfigurer` that will then
1089
+ set suite attributes, call :meth:`filter`, etc. as needed.
1090
+
1091
+ Example::
1092
+
1093
+ suite.configure(remove_keywords='PASSED',
1094
+ doc='Smoke test results.')
1095
+
1096
+ Not to be confused with :meth:`config` method that suites, tests,
1097
+ and keywords have to make it possible to set multiple attributes in
1098
+ one call.
1099
+ """
1100
+ super().configure() # Parent validates is call allowed.
1101
+ self.visit(SuiteConfigurer(**options))
1102
+
1103
+ def handle_suite_teardown_failures(self):
1104
+ """Internal usage only."""
1105
+ self.visit(SuiteTeardownFailureHandler())
1106
+
1107
+ def suite_teardown_failed(self, message: str):
1108
+ """Internal usage only."""
1109
+ self.visit(SuiteTeardownFailed(message))
1110
+
1111
+ def suite_teardown_skipped(self, message: str):
1112
+ """Internal usage only."""
1113
+ self.visit(SuiteTeardownFailed(message, skipped=True))
1114
+
1115
+ def to_dict(self) -> DataDict:
1116
+ return {**super().to_dict(), **StatusMixin.to_dict(self)}
1117
+
1118
+ @overload
1119
+ def to_xml(self, file: None = None) -> str:
1120
+ ...
1121
+
1122
+ @overload
1123
+ def to_xml(self, file: 'TextIO|Path|str') -> None:
1124
+ ...
1125
+
1126
+ def to_xml(self, file: 'None|TextIO|Path|str' = None) -> 'str|None':
1127
+ """Serialize suite into XML.
1128
+
1129
+ The format is the same that is used with normal output.xml files, but
1130
+ the ``<robot>`` root node is omitted and the result contains only
1131
+ the ``<suite>`` structure.
1132
+
1133
+ The ``file`` parameter controls what to do with the resulting XML data.
1134
+ It can be:
1135
+
1136
+ - ``None`` (default) to return the data as a string,
1137
+ - an open file object where to write the data to, or
1138
+ - a path (``pathlib.Path`` or string) to a file where to write
1139
+ the data using UTF-8 encoding.
1140
+
1141
+ A serialized suite can be recreated by using the :meth:`from_xml` method.
1142
+
1143
+ New in Robot Framework 7.0.
1144
+ """
1145
+ from robot.reporting.outputwriter import OutputWriter
1146
+
1147
+ output, close = self._get_output(file)
1148
+ try:
1149
+ self.visit(OutputWriter(output, suite_only=True))
1150
+ finally:
1151
+ if close:
1152
+ output.close()
1153
+ return output.getvalue() if file is None else None
1154
+
1155
+ def _get_output(self, output) -> 'tuple[TextIO|StringIO, bool]':
1156
+ close = False
1157
+ if output is None:
1158
+ output = StringIO()
1159
+ elif isinstance(output, (Path, str)):
1160
+ output = open(output, 'w')
1161
+ close = True
1162
+ return output, close
1163
+
1164
+ @classmethod
1165
+ def from_xml(cls, source: 'str|TextIO|Path') -> 'TestSuite':
1166
+ """Create suite based on results in XML.
1167
+
1168
+ The data is given as the ``source`` parameter. It can be:
1169
+
1170
+ - a string containing the data directly,
1171
+ - an open file object where to read the data from, or
1172
+ - a path (``pathlib.Path`` or string) to a UTF-8 encoded file to read.
1173
+
1174
+ Supports both normal output.xml files and files containing only the
1175
+ ``<suite>`` structure created, for example, with the :meth:`to_xml`
1176
+ method. When using normal output.xml files, possible execution errors
1177
+ listed in ``<errors>`` are silently ignored. If that is a problem,
1178
+ :class:`~robot.result.resultbuilder.ExecutionResult` should be used
1179
+ instead.
1180
+
1181
+ New in Robot Framework 7.0.
1182
+ """
1183
+ from .resultbuilder import ExecutionResult
1184
+
1185
+ return ExecutionResult(source).suite