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.
- {robotframework-6.1rc1 → robotframework-7.0}/INSTALL.rst +25 -25
- {robotframework-6.1rc1 → robotframework-7.0}/PKG-INFO +6 -8
- {robotframework-6.1rc1 → robotframework-7.0}/README.rst +3 -2
- {robotframework-6.1rc1 → robotframework-7.0}/setup.py +3 -6
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/api/__init__.py +4 -1
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/api/deco.py +60 -14
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/api/exceptions.py +6 -6
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/api/interfaces.py +403 -68
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/api/logger.py +27 -20
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/api/parsing.py +4 -2
- robotframework-7.0/src/robot/api/py.typed +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/conf/gatherfailed.py +2 -2
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/conf/languages.py +61 -25
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/conf/settings.py +21 -14
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/common/storage.js +11 -6
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/htmlfilewriter.py +1 -1
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/libdoc/libdoc.css +10 -2
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/libdoc/libdoc.html +10 -0
- robotframework-7.0/src/robot/htmldata/rebot/common.css +362 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/doc_formatting.css +13 -7
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/log.css +51 -10
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/log.html +14 -23
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/model.js +2 -4
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/report.css +83 -19
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/report.html +22 -7
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/testdata.js +10 -3
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/view.js +84 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/template.py +3 -3
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/builder.py +0 -8
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/datatypes.py +9 -10
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/htmlutils.py +4 -7
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/jsonbuilder.py +38 -20
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/model.py +21 -28
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/robotbuilder.py +50 -31
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/standardtypes.py +9 -1
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/xmlbuilder.py +35 -20
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/xmlwriter.py +18 -38
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/BuiltIn.py +255 -205
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/Collections.py +384 -240
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/DateTime.py +73 -60
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/OperatingSystem.py +6 -5
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/Process.py +26 -27
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/Remote.py +40 -36
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/String.py +66 -76
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/XML.py +66 -59
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/__init__.py +2 -2
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/dialogs_py.py +14 -2
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/__init__.py +5 -5
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/body.py +44 -13
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/configurer.py +19 -15
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/control.py +230 -88
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/filter.py +31 -25
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/itemlist.py +9 -8
- robotframework-7.0/src/robot/model/keyword.py +77 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/message.py +19 -10
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/modelobject.py +27 -15
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/modifier.py +1 -1
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/namepatterns.py +3 -25
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/stats.py +9 -15
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/tags.py +1 -1
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/testcase.py +15 -27
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/testsuite.py +44 -28
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/visitor.py +35 -8
- robotframework-7.0/src/robot/output/console/dotted.py +90 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/console/quiet.py +3 -2
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/console/verbose.py +61 -61
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/debugfile.py +39 -24
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/filelogger.py +18 -15
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/librarylogger.py +3 -2
- robotframework-7.0/src/robot/output/listeners.py +753 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/logger.py +216 -80
- robotframework-7.0/src/robot/output/loggerapi.py +215 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/loggerhelper.py +23 -56
- robotframework-7.0/src/robot/output/output.py +185 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/stdoutlogsplitter.py +8 -5
- robotframework-7.0/src/robot/output/xmllogger.py +459 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/lexer/blocklexers.py +17 -13
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/lexer/context.py +12 -2
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/lexer/lexer.py +1 -1
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/lexer/settings.py +6 -3
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/lexer/statementlexers.py +39 -10
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/lexer/tokens.py +27 -29
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/model/blocks.py +19 -6
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/model/statements.py +193 -94
- robotframework-7.0/src/robot/parsing/model/visitor.py +100 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/rebot.py +2 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/expandkeywordmatcher.py +1 -1
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/jsbuildingcontext.py +8 -6
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/jsmodelbuilders.py +88 -61
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/logreportwriters.py +16 -10
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/outputwriter.py +18 -2
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/resultwriter.py +4 -4
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/stringcache.py +3 -3
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/xunitwriter.py +15 -21
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/__init__.py +3 -2
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/configurer.py +12 -9
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/executionresult.py +56 -8
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/flattenkeywordmatcher.py +48 -9
- robotframework-7.0/src/robot/result/keywordremover.py +207 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/merger.py +1 -1
- robotframework-7.0/src/robot/result/model.py +1185 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/modeldeprecation.py +9 -3
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/resultbuilder.py +46 -36
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/xmlelementhandlers.py +83 -43
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/run.py +3 -1
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/__init__.py +14 -4
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/arguments/__init__.py +2 -1
- robotframework-7.0/src/robot/running/arguments/argumentconverter.py +100 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/arguments/argumentmapper.py +26 -24
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/arguments/argumentparser.py +85 -61
- robotframework-7.0/src/robot/running/arguments/argumentresolver.py +158 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/arguments/argumentspec.py +83 -116
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/arguments/argumentvalidator.py +33 -27
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/arguments/customconverters.py +15 -12
- robotframework-7.0/src/robot/running/arguments/embedded.py +145 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/arguments/typeconverters.py +268 -220
- robotframework-7.0/src/robot/running/arguments/typeinfo.py +317 -0
- robotframework-7.0/src/robot/running/arguments/typeinfoparser.py +219 -0
- robotframework-7.0/src/robot/running/arguments/typevalidator.py +60 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/bodyrunner.py +147 -129
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/builder/builders.py +17 -26
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/builder/parsers.py +17 -11
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/builder/settings.py +9 -17
- robotframework-7.0/src/robot/running/builder/transformers.py +535 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/context.py +113 -31
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/dynamicmethods.py +62 -34
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/importer.py +27 -49
- robotframework-7.0/src/robot/running/invalidkeyword.py +67 -0
- robotframework-7.0/src/robot/running/keywordfinder.py +91 -0
- robotframework-7.0/src/robot/running/keywordimplementation.py +164 -0
- robotframework-7.0/src/robot/running/librarykeyword.py +392 -0
- robotframework-7.0/src/robot/running/librarykeywordrunner.py +250 -0
- robotframework-7.0/src/robot/running/libraryscopes.py +115 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/model.py +198 -337
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/namespace.py +124 -137
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/outputcapture.py +20 -12
- robotframework-7.0/src/robot/running/resourcemodel.py +448 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/statusreporter.py +16 -13
- robotframework-7.0/src/robot/running/suiterunner.py +265 -0
- robotframework-7.0/src/robot/running/testlibraries.py +500 -0
- robotframework-7.0/src/robot/running/userkeywordrunner.py +260 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/testdoc.py +18 -13
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/__init__.py +53 -35
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/connectioncache.py +64 -59
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/dotdict.py +3 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/encodingsniffer.py +9 -4
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/escaping.py +19 -19
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/importer.py +2 -2
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/markupwriters.py +23 -15
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/misc.py +1 -2
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/normalizing.py +9 -24
- robotframework-7.0/src/robot/utils/notset.py +33 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/platform.py +15 -5
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/robotio.py +9 -9
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/robotpath.py +1 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/robottime.py +122 -90
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/robottypes.py +21 -31
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/setter.py +0 -5
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/text.py +7 -4
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/unic.py +2 -2
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/__init__.py +2 -2
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/assigner.py +17 -19
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/evaluation.py +20 -17
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/filesetter.py +80 -80
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/replacer.py +33 -36
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/scopes.py +17 -12
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/search.py +66 -41
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/store.py +14 -16
- robotframework-7.0/src/robot/variables/tablesetter.py +164 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/variables.py +1 -1
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/version.py +1 -1
- {robotframework-6.1rc1 → robotframework-7.0}/src/robotframework.egg-info/PKG-INFO +6 -8
- {robotframework-6.1rc1 → robotframework-7.0}/src/robotframework.egg-info/SOURCES.txt +10 -8
- {robotframework-6.1rc1 → robotframework-7.0}/tasks.py +6 -6
- robotframework-6.1rc1/src/robot/htmldata/rebot/common.css +0 -239
- robotframework-6.1rc1/src/robot/libraries/Reserved.py +0 -48
- robotframework-6.1rc1/src/robot/model/keyword.py +0 -173
- robotframework-6.1rc1/src/robot/output/console/dotted.py +0 -90
- robotframework-6.1rc1/src/robot/output/listenerarguments.py +0 -165
- robotframework-6.1rc1/src/robot/output/listenermethods.py +0 -113
- robotframework-6.1rc1/src/robot/output/listeners.py +0 -174
- robotframework-6.1rc1/src/robot/output/output.py +0 -95
- robotframework-6.1rc1/src/robot/output/xmllogger.py +0 -354
- robotframework-6.1rc1/src/robot/parsing/model/visitor.py +0 -67
- robotframework-6.1rc1/src/robot/result/keywordremover.py +0 -183
- robotframework-6.1rc1/src/robot/result/model.py +0 -957
- robotframework-6.1rc1/src/robot/running/arguments/argumentconverter.py +0 -79
- robotframework-6.1rc1/src/robot/running/arguments/argumentresolver.py +0 -131
- robotframework-6.1rc1/src/robot/running/arguments/embedded.py +0 -146
- robotframework-6.1rc1/src/robot/running/arguments/typevalidator.py +0 -56
- robotframework-6.1rc1/src/robot/running/builder/transformers.py +0 -642
- robotframework-6.1rc1/src/robot/running/handlers.py +0 -321
- robotframework-6.1rc1/src/robot/running/handlerstore.py +0 -66
- robotframework-6.1rc1/src/robot/running/librarykeywordrunner.py +0 -238
- robotframework-6.1rc1/src/robot/running/libraryscopes.py +0 -97
- robotframework-6.1rc1/src/robot/running/modelcombiner.py +0 -32
- robotframework-6.1rc1/src/robot/running/suiterunner.py +0 -249
- robotframework-6.1rc1/src/robot/running/testlibraries.py +0 -420
- robotframework-6.1rc1/src/robot/running/usererrorhandler.py +0 -75
- robotframework-6.1rc1/src/robot/running/userkeyword.py +0 -111
- robotframework-6.1rc1/src/robot/running/userkeywordrunner.py +0 -274
- robotframework-6.1rc1/src/robot/variables/tablesetter.py +0 -156
- {robotframework-6.1rc1 → robotframework-7.0}/AUTHORS.rst +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/BUILD.rst +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/CONTRIBUTING.rst +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/COPYRIGHT.txt +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/LICENSE.txt +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/MANIFEST.in +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/setup.cfg +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/__init__.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/__main__.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/conf/__init__.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/errors.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/__init__.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/common/__init__.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/common/js_disabled.css +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/jsonwriter.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/lib/__init__.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/lib/jquery.highlight.min.js +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/lib/jquery.min.js +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/lib/jquery.tablesorter.min.js +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/lib/jquery.tmpl.min.js +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/lib/jsxcompressor.min.js +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/libdoc/__init__.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/libdoc/doc_formatting.css +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/libdoc/print.css +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/libdoc/pygments.css +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/__init__.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/fileloading.js +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/log.js +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/print.css +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/util.js +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/testdoc/__init__.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/testdoc/testdoc.css +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/testdoc/testdoc.html +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdoc.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/__init__.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/consoleviewer.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/htmlwriter.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/jsonwriter.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/output.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/writer.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/Dialogs.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/Easter.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/Screenshot.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/Telnet.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/fixture.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/metadata.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/statistics.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/suitestatistics.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/tagsetter.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/tagstatistics.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/totalstatistics.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/__init__.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/console/__init__.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/console/highlighting.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/pyloggingconf.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/__init__.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/lexer/__init__.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/lexer/tokenizer.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/model/__init__.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/parser/__init__.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/parser/blockparsers.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/parser/fileparser.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/parser/parser.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/suitestructure.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/pythonpathsetter.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/__init__.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/jsexecutionresult.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/jswriter.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/executionerrors.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/messagefilter.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/suiteteardownfailed.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/visitor.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/builder/__init__.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/randomizer.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/runkwregister.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/signalhandler.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/status.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/timeouts/__init__.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/timeouts/posix.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/timeouts/windows.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/application.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/argumentparser.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/asserts.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/charwidth.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/compress.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/encoding.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/error.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/etreewrapper.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/filereader.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/frange.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/htmlformatters.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/markuputils.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/match.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/recommendations.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/restreader.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/robotenv.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/robotinspect.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/sortable.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/typehints.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/finders.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/notfound.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/resolvable.py +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robotframework.egg-info/dependency_links.txt +0 -0
- {robotframework-6.1rc1 → robotframework-7.0}/src/robotframework.egg-info/entry_points.txt +0 -0
- {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
|