robotframework 6.1.1__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.1.1 → robotframework-7.0}/INSTALL.rst +25 -25
- {robotframework-6.1.1 → robotframework-7.0}/PKG-INFO +6 -8
- {robotframework-6.1.1 → robotframework-7.0}/README.rst +3 -2
- {robotframework-6.1.1 → robotframework-7.0}/setup.py +3 -6
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/api/__init__.py +4 -1
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/api/deco.py +60 -14
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/api/exceptions.py +6 -6
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/api/interfaces.py +402 -67
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/api/logger.py +27 -20
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/api/parsing.py +4 -2
- robotframework-7.0/src/robot/api/py.typed +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/conf/gatherfailed.py +2 -2
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/conf/languages.py +0 -9
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/conf/settings.py +21 -14
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/common/storage.js +11 -6
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/htmlfilewriter.py +1 -1
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/libdoc/libdoc.css +10 -2
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/libdoc/libdoc.html +10 -0
- robotframework-7.0/src/robot/htmldata/rebot/common.css +362 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/rebot/doc_formatting.css +13 -7
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/rebot/log.css +51 -10
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/rebot/log.html +14 -23
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/rebot/model.js +2 -4
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/rebot/report.css +83 -19
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/rebot/report.html +22 -7
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/rebot/testdata.js +10 -3
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/rebot/view.js +84 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/template.py +3 -3
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libdocpkg/builder.py +0 -8
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libdocpkg/datatypes.py +9 -10
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libdocpkg/htmlutils.py +4 -7
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libdocpkg/jsonbuilder.py +38 -20
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libdocpkg/model.py +21 -28
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libdocpkg/robotbuilder.py +50 -31
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libdocpkg/standardtypes.py +9 -1
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libdocpkg/xmlbuilder.py +35 -20
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libdocpkg/xmlwriter.py +18 -38
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libraries/BuiltIn.py +255 -205
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libraries/Collections.py +384 -240
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libraries/DateTime.py +73 -60
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libraries/OperatingSystem.py +6 -5
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libraries/Process.py +26 -27
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libraries/Remote.py +40 -36
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libraries/String.py +66 -76
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libraries/XML.py +66 -59
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libraries/__init__.py +2 -2
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/model/__init__.py +5 -5
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/model/body.py +44 -13
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/model/configurer.py +15 -11
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/model/control.py +226 -84
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/model/filter.py +31 -25
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/model/itemlist.py +9 -8
- robotframework-7.0/src/robot/model/keyword.py +77 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/model/message.py +19 -10
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/model/modelobject.py +25 -7
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/model/namepatterns.py +3 -25
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/model/stats.py +9 -15
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/model/tags.py +1 -1
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/model/testcase.py +15 -27
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/model/testsuite.py +17 -26
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/model/visitor.py +35 -8
- robotframework-7.0/src/robot/output/console/dotted.py +90 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/output/console/quiet.py +3 -2
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/output/console/verbose.py +61 -61
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/output/debugfile.py +39 -24
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/output/filelogger.py +18 -15
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/output/librarylogger.py +3 -2
- robotframework-7.0/src/robot/output/listeners.py +753 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/output/logger.py +216 -80
- robotframework-7.0/src/robot/output/loggerapi.py +215 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/output/loggerhelper.py +23 -56
- robotframework-7.0/src/robot/output/output.py +185 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/output/stdoutlogsplitter.py +8 -5
- robotframework-7.0/src/robot/output/xmllogger.py +459 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/parsing/lexer/blocklexers.py +17 -13
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/parsing/lexer/context.py +12 -2
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/parsing/lexer/lexer.py +1 -1
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/parsing/lexer/settings.py +6 -3
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/parsing/lexer/statementlexers.py +39 -10
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/parsing/lexer/tokens.py +27 -29
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/parsing/model/blocks.py +19 -6
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/parsing/model/statements.py +193 -94
- robotframework-7.0/src/robot/parsing/model/visitor.py +100 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/rebot.py +2 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/reporting/expandkeywordmatcher.py +1 -1
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/reporting/jsbuildingcontext.py +8 -6
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/reporting/jsmodelbuilders.py +88 -61
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/reporting/logreportwriters.py +16 -10
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/reporting/outputwriter.py +18 -2
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/reporting/resultwriter.py +4 -4
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/reporting/stringcache.py +3 -3
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/reporting/xunitwriter.py +15 -21
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/result/__init__.py +3 -2
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/result/configurer.py +12 -9
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/result/executionresult.py +56 -8
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/result/flattenkeywordmatcher.py +48 -9
- robotframework-7.0/src/robot/result/keywordremover.py +207 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/result/merger.py +1 -1
- robotframework-7.0/src/robot/result/model.py +1185 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/result/modeldeprecation.py +9 -3
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/result/resultbuilder.py +46 -36
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/result/xmlelementhandlers.py +83 -43
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/run.py +2 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/running/__init__.py +14 -4
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/running/arguments/__init__.py +2 -1
- robotframework-7.0/src/robot/running/arguments/argumentconverter.py +100 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/running/arguments/argumentmapper.py +26 -24
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/running/arguments/argumentparser.py +82 -60
- robotframework-7.0/src/robot/running/arguments/argumentresolver.py +158 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/running/arguments/argumentspec.py +83 -116
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/running/arguments/argumentvalidator.py +33 -27
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/running/arguments/customconverters.py +15 -12
- robotframework-7.0/src/robot/running/arguments/embedded.py +145 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/running/arguments/typeconverters.py +233 -213
- 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.1.1 → robotframework-7.0}/src/robot/running/bodyrunner.py +147 -129
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/running/builder/builders.py +2 -1
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/running/builder/parsers.py +10 -11
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/running/builder/settings.py +9 -17
- robotframework-7.0/src/robot/running/builder/transformers.py +535 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/running/context.py +113 -31
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/running/dynamicmethods.py +62 -34
- {robotframework-6.1.1 → 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.1.1 → robotframework-7.0}/src/robot/running/model.py +195 -377
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/running/namespace.py +124 -137
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/running/outputcapture.py +20 -12
- robotframework-7.0/src/robot/running/resourcemodel.py +448 -0
- {robotframework-6.1.1 → 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.1.1 → robotframework-7.0}/src/robot/testdoc.py +18 -13
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/__init__.py +53 -35
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/connectioncache.py +64 -59
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/dotdict.py +3 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/encodingsniffer.py +1 -1
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/escaping.py +19 -19
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/importer.py +2 -2
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/markupwriters.py +23 -15
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/misc.py +1 -2
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/normalizing.py +9 -24
- robotframework-7.0/src/robot/utils/notset.py +33 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/platform.py +15 -5
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/robotio.py +9 -9
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/robotpath.py +1 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/robottime.py +122 -90
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/robottypes.py +21 -31
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/setter.py +0 -5
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/text.py +7 -4
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/unic.py +2 -2
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/variables/__init__.py +2 -2
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/variables/assigner.py +17 -19
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/variables/evaluation.py +20 -17
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/variables/filesetter.py +80 -80
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/variables/replacer.py +33 -36
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/variables/scopes.py +17 -12
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/variables/search.py +66 -41
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/variables/store.py +14 -16
- robotframework-7.0/src/robot/variables/tablesetter.py +164 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/variables/variables.py +1 -1
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/version.py +1 -1
- {robotframework-6.1.1 → robotframework-7.0}/src/robotframework.egg-info/PKG-INFO +6 -8
- {robotframework-6.1.1 → robotframework-7.0}/src/robotframework.egg-info/SOURCES.txt +10 -8
- {robotframework-6.1.1 → robotframework-7.0}/tasks.py +6 -6
- robotframework-6.1.1/src/robot/htmldata/rebot/common.css +0 -239
- robotframework-6.1.1/src/robot/libraries/Reserved.py +0 -48
- robotframework-6.1.1/src/robot/model/keyword.py +0 -173
- robotframework-6.1.1/src/robot/output/console/dotted.py +0 -90
- robotframework-6.1.1/src/robot/output/listenerarguments.py +0 -165
- robotframework-6.1.1/src/robot/output/listenermethods.py +0 -113
- robotframework-6.1.1/src/robot/output/listeners.py +0 -174
- robotframework-6.1.1/src/robot/output/output.py +0 -95
- robotframework-6.1.1/src/robot/output/xmllogger.py +0 -354
- robotframework-6.1.1/src/robot/parsing/model/visitor.py +0 -67
- robotframework-6.1.1/src/robot/result/keywordremover.py +0 -183
- robotframework-6.1.1/src/robot/result/model.py +0 -957
- robotframework-6.1.1/src/robot/running/arguments/argumentconverter.py +0 -79
- robotframework-6.1.1/src/robot/running/arguments/argumentresolver.py +0 -131
- robotframework-6.1.1/src/robot/running/arguments/embedded.py +0 -146
- robotframework-6.1.1/src/robot/running/arguments/typevalidator.py +0 -56
- robotframework-6.1.1/src/robot/running/builder/transformers.py +0 -642
- robotframework-6.1.1/src/robot/running/handlers.py +0 -321
- robotframework-6.1.1/src/robot/running/handlerstore.py +0 -66
- robotframework-6.1.1/src/robot/running/librarykeywordrunner.py +0 -238
- robotframework-6.1.1/src/robot/running/libraryscopes.py +0 -97
- robotframework-6.1.1/src/robot/running/modelcombiner.py +0 -32
- robotframework-6.1.1/src/robot/running/suiterunner.py +0 -248
- robotframework-6.1.1/src/robot/running/testlibraries.py +0 -423
- robotframework-6.1.1/src/robot/running/usererrorhandler.py +0 -75
- robotframework-6.1.1/src/robot/running/userkeyword.py +0 -111
- robotframework-6.1.1/src/robot/running/userkeywordrunner.py +0 -274
- robotframework-6.1.1/src/robot/variables/tablesetter.py +0 -156
- {robotframework-6.1.1 → robotframework-7.0}/AUTHORS.rst +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/BUILD.rst +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/CONTRIBUTING.rst +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/COPYRIGHT.txt +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/LICENSE.txt +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/MANIFEST.in +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/setup.cfg +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/__init__.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/__main__.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/conf/__init__.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/errors.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/__init__.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/common/__init__.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/common/js_disabled.css +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/jsonwriter.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/lib/__init__.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/lib/jquery.highlight.min.js +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/lib/jquery.min.js +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/lib/jquery.tablesorter.min.js +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/lib/jquery.tmpl.min.js +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/lib/jsxcompressor.min.js +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/libdoc/__init__.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/libdoc/doc_formatting.css +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/libdoc/print.css +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/libdoc/pygments.css +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/rebot/__init__.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/rebot/fileloading.js +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/rebot/log.js +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/rebot/print.css +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/rebot/util.js +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/testdoc/__init__.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/testdoc/testdoc.css +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/htmldata/testdoc/testdoc.html +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libdoc.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libdocpkg/__init__.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libdocpkg/consoleviewer.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libdocpkg/htmlwriter.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libdocpkg/jsonwriter.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libdocpkg/output.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libdocpkg/writer.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libraries/Dialogs.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libraries/Easter.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libraries/Screenshot.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libraries/Telnet.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/libraries/dialogs_py.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/model/fixture.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/model/metadata.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/model/modifier.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/model/statistics.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/model/suitestatistics.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/model/tagsetter.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/model/tagstatistics.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/model/totalstatistics.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/output/__init__.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/output/console/__init__.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/output/console/highlighting.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/output/pyloggingconf.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/parsing/__init__.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/parsing/lexer/__init__.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/parsing/lexer/tokenizer.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/parsing/model/__init__.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/parsing/parser/__init__.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/parsing/parser/blockparsers.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/parsing/parser/fileparser.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/parsing/parser/parser.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/parsing/suitestructure.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/pythonpathsetter.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/reporting/__init__.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/reporting/jsexecutionresult.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/reporting/jswriter.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/result/executionerrors.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/result/messagefilter.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/result/suiteteardownfailed.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/result/visitor.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/running/builder/__init__.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/running/randomizer.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/running/runkwregister.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/running/signalhandler.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/running/status.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/running/timeouts/__init__.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/running/timeouts/posix.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/running/timeouts/windows.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/application.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/argumentparser.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/asserts.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/charwidth.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/compress.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/encoding.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/error.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/etreewrapper.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/filereader.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/frange.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/htmlformatters.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/markuputils.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/match.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/recommendations.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/restreader.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/robotenv.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/robotinspect.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/sortable.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/utils/typehints.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/variables/finders.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/variables/notfound.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robot/variables/resolvable.py +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robotframework.egg-info/dependency_links.txt +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robotframework.egg-info/entry_points.txt +0 -0
- {robotframework-6.1.1 → robotframework-7.0}/src/robotframework.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,317 @@
|
|
|
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
|
+
from collections.abc import Mapping, Sequence, Set
|
|
17
|
+
from datetime import date, datetime, timedelta
|
|
18
|
+
from decimal import Decimal
|
|
19
|
+
from enum import Enum
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Any, ForwardRef, get_type_hints, Literal, Union
|
|
22
|
+
|
|
23
|
+
from robot.conf import Languages, LanguagesLike
|
|
24
|
+
from robot.errors import DataError
|
|
25
|
+
from robot.utils import (has_args, is_union, NOT_SET, plural_or_not as s, setter,
|
|
26
|
+
SetterAwareType, type_name, type_repr, typeddict_types)
|
|
27
|
+
|
|
28
|
+
from ..context import EXECUTION_CONTEXTS
|
|
29
|
+
from .customconverters import CustomArgumentConverters
|
|
30
|
+
from .typeconverters import TypeConverter
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
TYPE_NAMES = {
|
|
34
|
+
'...': Ellipsis,
|
|
35
|
+
'ellipsis': Ellipsis,
|
|
36
|
+
'any': Any,
|
|
37
|
+
'str': str,
|
|
38
|
+
'string': str,
|
|
39
|
+
'unicode': str,
|
|
40
|
+
'bool': bool,
|
|
41
|
+
'boolean': bool,
|
|
42
|
+
'int': int,
|
|
43
|
+
'integer': int,
|
|
44
|
+
'long': int,
|
|
45
|
+
'float': float,
|
|
46
|
+
'double': float,
|
|
47
|
+
'decimal': Decimal,
|
|
48
|
+
'bytes': bytes,
|
|
49
|
+
'bytearray': bytearray,
|
|
50
|
+
'datetime': datetime,
|
|
51
|
+
'date': date,
|
|
52
|
+
'timedelta': timedelta,
|
|
53
|
+
'path': Path,
|
|
54
|
+
'none': type(None),
|
|
55
|
+
'list': list,
|
|
56
|
+
'sequence': list,
|
|
57
|
+
'tuple': tuple,
|
|
58
|
+
'dictionary': dict,
|
|
59
|
+
'dict': dict,
|
|
60
|
+
'mapping': dict,
|
|
61
|
+
'map': dict,
|
|
62
|
+
'set': set,
|
|
63
|
+
'frozenset': frozenset,
|
|
64
|
+
'union': Union,
|
|
65
|
+
'literal': Literal
|
|
66
|
+
}
|
|
67
|
+
LITERAL_TYPES = (int, str, bytes, bool, Enum, type(None))
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class TypeInfo(metaclass=SetterAwareType):
|
|
71
|
+
"""Represents an argument type.
|
|
72
|
+
|
|
73
|
+
Normally created using the :meth:`from_type_hint` classmethod.
|
|
74
|
+
With unions and parametrized types, :attr:`nested` contains nested types.
|
|
75
|
+
|
|
76
|
+
Values can be converted according to this type info by using the
|
|
77
|
+
:meth:`convert` method.
|
|
78
|
+
|
|
79
|
+
Part of the public API starting from Robot Framework 7.0. In such usage
|
|
80
|
+
should be imported via the :mod:`robot.api` package.
|
|
81
|
+
"""
|
|
82
|
+
is_typed_dict = False
|
|
83
|
+
__slots__ = ('name', 'type')
|
|
84
|
+
|
|
85
|
+
def __init__(self, name: 'str|None' = None,
|
|
86
|
+
type: Any = NOT_SET,
|
|
87
|
+
nested: 'Sequence[TypeInfo]|None' = None):
|
|
88
|
+
if type is NOT_SET:
|
|
89
|
+
type = TYPE_NAMES.get(name.lower()) if name else None
|
|
90
|
+
self.name = name
|
|
91
|
+
self.type = type
|
|
92
|
+
self.nested = nested
|
|
93
|
+
|
|
94
|
+
@setter
|
|
95
|
+
def nested(self, nested: 'Sequence[TypeInfo]') -> 'tuple[TypeInfo, ...]|None':
|
|
96
|
+
"""Nested types as a tuple of ``TypeInfo`` objects.
|
|
97
|
+
|
|
98
|
+
Used with parameterized types and unions.
|
|
99
|
+
"""
|
|
100
|
+
typ = self.type
|
|
101
|
+
if self.is_union:
|
|
102
|
+
self._validate_union(nested)
|
|
103
|
+
elif nested is None:
|
|
104
|
+
return None
|
|
105
|
+
elif typ is None:
|
|
106
|
+
return tuple(nested)
|
|
107
|
+
elif typ is Literal:
|
|
108
|
+
self._validate_literal(nested)
|
|
109
|
+
elif not isinstance(typ, type):
|
|
110
|
+
self._report_nested_error(nested)
|
|
111
|
+
elif issubclass(typ, tuple):
|
|
112
|
+
if nested[-1].type is Ellipsis:
|
|
113
|
+
self._validate_nested_count(nested, 2, 'Homogenous tuple', offset=-1)
|
|
114
|
+
elif issubclass(typ, Sequence) and not issubclass(typ, (str, bytes, bytearray)):
|
|
115
|
+
self._validate_nested_count(nested, 1)
|
|
116
|
+
elif issubclass(typ, Set):
|
|
117
|
+
self._validate_nested_count(nested, 1)
|
|
118
|
+
elif issubclass(typ, Mapping):
|
|
119
|
+
self._validate_nested_count(nested, 2)
|
|
120
|
+
elif typ in TYPE_NAMES.values():
|
|
121
|
+
self._report_nested_error(nested)
|
|
122
|
+
return tuple(nested)
|
|
123
|
+
|
|
124
|
+
def _validate_union(self, nested):
|
|
125
|
+
if not nested:
|
|
126
|
+
raise DataError('Union cannot be empty.')
|
|
127
|
+
|
|
128
|
+
def _validate_literal(self, nested):
|
|
129
|
+
if not nested:
|
|
130
|
+
raise DataError('Literal cannot be empty.')
|
|
131
|
+
for info in nested:
|
|
132
|
+
if not isinstance(info.type, LITERAL_TYPES):
|
|
133
|
+
raise DataError(f'Literal supports only integers, strings, bytes, '
|
|
134
|
+
f'Booleans, enums and None, value {info.name} is '
|
|
135
|
+
f'{type_name(info.type)}.')
|
|
136
|
+
|
|
137
|
+
def _validate_nested_count(self, nested, expected, kind=None, offset=0):
|
|
138
|
+
if len(nested) != expected:
|
|
139
|
+
self._report_nested_error(nested, expected, kind, offset)
|
|
140
|
+
|
|
141
|
+
def _report_nested_error(self, nested, expected=0, kind=None, offset=0):
|
|
142
|
+
expected += offset
|
|
143
|
+
actual = len(nested) + offset
|
|
144
|
+
args = ', '.join(str(n) for n in nested)
|
|
145
|
+
kind = kind or f"'{self.name}{'[]' if expected > 0 else ''}'"
|
|
146
|
+
if expected == 0:
|
|
147
|
+
raise DataError(f"{kind} does not accept parameters, "
|
|
148
|
+
f"'{self.name}[{args}]' has {actual}.")
|
|
149
|
+
raise DataError(f"{kind} requires exactly {expected} parameter{s(expected)}, "
|
|
150
|
+
f"'{self.name}[{args}]' has {actual}.")
|
|
151
|
+
|
|
152
|
+
@property
|
|
153
|
+
def is_union(self):
|
|
154
|
+
return self.name == 'Union'
|
|
155
|
+
|
|
156
|
+
@classmethod
|
|
157
|
+
def from_type_hint(cls, hint: Any) -> 'TypeInfo':
|
|
158
|
+
"""Construct a ``TypeInfo`` based on a type hint.
|
|
159
|
+
|
|
160
|
+
The type hint can be in various different formats:
|
|
161
|
+
|
|
162
|
+
- an actual type such as ``int``
|
|
163
|
+
- a parameterized type such as ``list[int]``
|
|
164
|
+
- a union such as ``int | float``
|
|
165
|
+
- a string such as ``'int'``, ``'list[int]'`` or ``'int | float'``
|
|
166
|
+
- a ``TypedDict`` (represented as a :class:`TypedDictInfo`)
|
|
167
|
+
- a sequence of supported type hints to create a union from such as
|
|
168
|
+
``[int, float]`` or ``('int', 'list[int]')``
|
|
169
|
+
|
|
170
|
+
In special cases, for example with dictionaries or sequences, using the
|
|
171
|
+
more specialized methods like :meth:`from_dict` or :meth:`from_sequence`
|
|
172
|
+
may be more appropriate than using this generic method.
|
|
173
|
+
"""
|
|
174
|
+
if hint is NOT_SET:
|
|
175
|
+
return cls()
|
|
176
|
+
if isinstance(hint, ForwardRef):
|
|
177
|
+
hint = hint.__forward_arg__
|
|
178
|
+
if isinstance(hint, typeddict_types):
|
|
179
|
+
return TypedDictInfo(hint.__name__, hint)
|
|
180
|
+
if is_union(hint):
|
|
181
|
+
nested = [cls.from_type_hint(a) for a in hint.__args__]
|
|
182
|
+
return cls('Union', nested=nested)
|
|
183
|
+
if hasattr(hint, '__origin__'):
|
|
184
|
+
if hint.__origin__ is Literal:
|
|
185
|
+
nested = [cls(repr(a) if not isinstance(a, Enum) else a.name, a)
|
|
186
|
+
for a in hint.__args__]
|
|
187
|
+
elif has_args(hint):
|
|
188
|
+
nested = [cls.from_type_hint(a) for a in hint.__args__]
|
|
189
|
+
else:
|
|
190
|
+
nested = None
|
|
191
|
+
return cls(type_repr(hint, nested=False), hint.__origin__, nested)
|
|
192
|
+
if isinstance(hint, str):
|
|
193
|
+
return cls.from_string(hint)
|
|
194
|
+
if isinstance(hint, (tuple, list)):
|
|
195
|
+
return cls.from_sequence(hint)
|
|
196
|
+
if isinstance(hint, type):
|
|
197
|
+
return cls(type_repr(hint), hint)
|
|
198
|
+
if hint is None:
|
|
199
|
+
return cls('None', type(None))
|
|
200
|
+
if hint is Union: # Plain `Union` without params.
|
|
201
|
+
return cls('Union')
|
|
202
|
+
if hint is Any:
|
|
203
|
+
return cls('Any', hint)
|
|
204
|
+
if hint is Ellipsis:
|
|
205
|
+
return cls('...', hint)
|
|
206
|
+
return cls(str(hint))
|
|
207
|
+
|
|
208
|
+
@classmethod
|
|
209
|
+
def from_type(cls, hint: type) -> 'TypeInfo':
|
|
210
|
+
"""Construct a ``TypeInfo`` based on an actual type.
|
|
211
|
+
|
|
212
|
+
Use :meth:`from_type_hint` if the type hint can also be something else
|
|
213
|
+
than a concrete type such as a string.
|
|
214
|
+
"""
|
|
215
|
+
return cls(type_repr(hint), hint)
|
|
216
|
+
|
|
217
|
+
@classmethod
|
|
218
|
+
def from_string(cls, hint: str) -> 'TypeInfo':
|
|
219
|
+
"""Construct a ``TypeInfo`` based on a string.
|
|
220
|
+
|
|
221
|
+
In addition to just types names or their aliases like ``int`` or ``integer``,
|
|
222
|
+
supports also parameterized types like ``list[int]`` as well as unions like
|
|
223
|
+
``int | float``.
|
|
224
|
+
|
|
225
|
+
Use :meth:`from_type_hint` if the type hint can also be something else
|
|
226
|
+
than a string such as an actual type.
|
|
227
|
+
"""
|
|
228
|
+
# Needs to be imported here due to cyclic dependency.
|
|
229
|
+
from .typeinfoparser import TypeInfoParser
|
|
230
|
+
try:
|
|
231
|
+
return TypeInfoParser(hint).parse()
|
|
232
|
+
except ValueError as err:
|
|
233
|
+
raise DataError(str(err))
|
|
234
|
+
|
|
235
|
+
@classmethod
|
|
236
|
+
def from_sequence(cls, sequence: 'tuple|list') -> 'TypeInfo':
|
|
237
|
+
"""Construct a ``TypeInfo`` based on a sequence of types.
|
|
238
|
+
|
|
239
|
+
Types can be actual types, strings, or anything else accepted by
|
|
240
|
+
:meth:`from_type_hint`. If the sequence contains just one type,
|
|
241
|
+
a ``TypeInfo`` created based on it is returned. If there are more
|
|
242
|
+
types, the returned ``TypeInfo`` represents a union. Using an empty
|
|
243
|
+
sequence is an error.
|
|
244
|
+
|
|
245
|
+
Use :meth:`from_type_hint` if other types than sequences need to
|
|
246
|
+
supported.
|
|
247
|
+
"""
|
|
248
|
+
infos = []
|
|
249
|
+
for typ in sequence:
|
|
250
|
+
info = cls.from_type_hint(typ)
|
|
251
|
+
if info.is_union:
|
|
252
|
+
infos.extend(info.nested)
|
|
253
|
+
else:
|
|
254
|
+
infos.append(info)
|
|
255
|
+
if len(infos) == 1:
|
|
256
|
+
return infos[0]
|
|
257
|
+
return cls('Union', nested=infos)
|
|
258
|
+
|
|
259
|
+
def convert(self, value: Any,
|
|
260
|
+
name: 'str|None' = None,
|
|
261
|
+
custom_converters: 'CustomArgumentConverters|dict|None' = None,
|
|
262
|
+
languages: 'LanguagesLike' = None,
|
|
263
|
+
kind: str = 'Argument'):
|
|
264
|
+
"""Convert ``value`` based on type information this ``TypeInfo`` contains.
|
|
265
|
+
|
|
266
|
+
:param value: Value to convert.
|
|
267
|
+
:param name: Name of the argument or other thing to convert.
|
|
268
|
+
Used only for error reporting.
|
|
269
|
+
:param custom_converters: Custom argument converters.
|
|
270
|
+
:param languages: Language configuration. During execution, uses the
|
|
271
|
+
current language configuration by default.
|
|
272
|
+
:param kind: Type of the thing to be converted.
|
|
273
|
+
Used only for error reporting.
|
|
274
|
+
:raises: ``TypeError`` if there is no converter for this type or
|
|
275
|
+
``ValueError`` is conversion fails.
|
|
276
|
+
:return: Converted value.
|
|
277
|
+
"""
|
|
278
|
+
if isinstance(custom_converters, dict):
|
|
279
|
+
custom_converters = CustomArgumentConverters.from_dict(custom_converters)
|
|
280
|
+
if not languages and EXECUTION_CONTEXTS.current:
|
|
281
|
+
languages = EXECUTION_CONTEXTS.current.languages
|
|
282
|
+
elif not isinstance(languages, Languages):
|
|
283
|
+
languages = Languages(languages)
|
|
284
|
+
converter = TypeConverter.converter_for(self, custom_converters, languages)
|
|
285
|
+
if not converter:
|
|
286
|
+
raise TypeError(f"No converter found for '{self}'.")
|
|
287
|
+
return converter.convert(value, name, kind)
|
|
288
|
+
|
|
289
|
+
def __str__(self):
|
|
290
|
+
if self.is_union:
|
|
291
|
+
return ' | '.join(str(n) for n in self.nested)
|
|
292
|
+
name = self.name or ''
|
|
293
|
+
if self.nested is None:
|
|
294
|
+
return name
|
|
295
|
+
nested = ', '.join(str(n) for n in self.nested)
|
|
296
|
+
return f'{name}[{nested}]'
|
|
297
|
+
|
|
298
|
+
def __bool__(self):
|
|
299
|
+
return self.name is not None
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
class TypedDictInfo(TypeInfo):
|
|
303
|
+
"""Represents ``TypedDict`` used as an argument."""
|
|
304
|
+
|
|
305
|
+
is_typed_dict = True
|
|
306
|
+
__slots__ = ('annotations', 'required')
|
|
307
|
+
|
|
308
|
+
def __init__(self, name: str, type: type):
|
|
309
|
+
super().__init__(name, type)
|
|
310
|
+
try:
|
|
311
|
+
type_hints = get_type_hints(type)
|
|
312
|
+
except Exception:
|
|
313
|
+
type_hints = type.__annotations__
|
|
314
|
+
self.annotations = {name: TypeInfo.from_type_hint(hint)
|
|
315
|
+
for name, hint in type_hints.items()}
|
|
316
|
+
# __required_keys__ is new in Python 3.9.
|
|
317
|
+
self.required = getattr(type, '__required_keys__', frozenset())
|
|
@@ -0,0 +1,219 @@
|
|
|
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
|
+
from ast import literal_eval
|
|
17
|
+
from enum import auto, Enum
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
from typing import Literal
|
|
20
|
+
|
|
21
|
+
from .typeinfo import LITERAL_TYPES, TypeInfo
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class TokenType(Enum):
|
|
25
|
+
NAME = auto()
|
|
26
|
+
LEFT_SQUARE = auto()
|
|
27
|
+
RIGHT_SQUARE = auto()
|
|
28
|
+
PIPE = auto()
|
|
29
|
+
COMMA = auto()
|
|
30
|
+
|
|
31
|
+
def __repr__(self):
|
|
32
|
+
return str(self)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class Token:
|
|
37
|
+
type: TokenType
|
|
38
|
+
value: str
|
|
39
|
+
position: int = -1
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class TypeInfoTokenizer:
|
|
43
|
+
markers = {
|
|
44
|
+
'[': TokenType.LEFT_SQUARE,
|
|
45
|
+
']': TokenType.RIGHT_SQUARE,
|
|
46
|
+
'|': TokenType.PIPE,
|
|
47
|
+
',': TokenType.COMMA,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
def __init__(self, source: str):
|
|
51
|
+
self.source = source
|
|
52
|
+
self.tokens: 'list[Token]' = []
|
|
53
|
+
self.start = 0
|
|
54
|
+
self.current = 0
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def at_end(self) -> bool:
|
|
58
|
+
return self.current >= len(self.source)
|
|
59
|
+
|
|
60
|
+
def tokenize(self) -> 'list[Token]':
|
|
61
|
+
while not self.at_end:
|
|
62
|
+
self.start = self.current
|
|
63
|
+
char = self.advance()
|
|
64
|
+
if char in self.markers:
|
|
65
|
+
self.add_token(self.markers[char])
|
|
66
|
+
elif char.strip():
|
|
67
|
+
self.name()
|
|
68
|
+
return self.tokens
|
|
69
|
+
|
|
70
|
+
def advance(self) -> str:
|
|
71
|
+
char = self.source[self.current]
|
|
72
|
+
self.current += 1
|
|
73
|
+
return char
|
|
74
|
+
|
|
75
|
+
def peek(self) -> 'str|None':
|
|
76
|
+
try:
|
|
77
|
+
return self.source[self.current]
|
|
78
|
+
except IndexError:
|
|
79
|
+
return None
|
|
80
|
+
|
|
81
|
+
def name(self):
|
|
82
|
+
end_at = set(self.markers) | {None}
|
|
83
|
+
closing_quote = None
|
|
84
|
+
char = self.source[self.current-1]
|
|
85
|
+
if char in ('"', "'"):
|
|
86
|
+
end_at = {None}
|
|
87
|
+
closing_quote = char
|
|
88
|
+
elif char == 'b' and self.peek() in ('"', "'"):
|
|
89
|
+
end_at = {None}
|
|
90
|
+
closing_quote = self.advance()
|
|
91
|
+
while True:
|
|
92
|
+
char = self.peek()
|
|
93
|
+
if char in end_at:
|
|
94
|
+
break
|
|
95
|
+
self.current += 1
|
|
96
|
+
if char == closing_quote:
|
|
97
|
+
break
|
|
98
|
+
self.add_token(TokenType.NAME)
|
|
99
|
+
|
|
100
|
+
def add_token(self, type: TokenType):
|
|
101
|
+
value = self.source[self.start:self.current].strip()
|
|
102
|
+
self.tokens.append(Token(type, value, self.start))
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class TypeInfoParser:
|
|
106
|
+
|
|
107
|
+
def __init__(self, source: str):
|
|
108
|
+
self.source = source
|
|
109
|
+
self.tokens: 'list[Token]' = []
|
|
110
|
+
self.current = 0
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def at_end(self) -> bool:
|
|
114
|
+
return self.peek() is None
|
|
115
|
+
|
|
116
|
+
def parse(self) -> TypeInfo:
|
|
117
|
+
self.tokens = TypeInfoTokenizer(self.source).tokenize()
|
|
118
|
+
info = self.type()
|
|
119
|
+
if not self.at_end:
|
|
120
|
+
self.error(f"Extra content after '{info}'.")
|
|
121
|
+
return info
|
|
122
|
+
|
|
123
|
+
def type(self) -> TypeInfo:
|
|
124
|
+
if not self.check(TokenType.NAME):
|
|
125
|
+
self.error('Type name missing.')
|
|
126
|
+
info = TypeInfo(self.advance().value)
|
|
127
|
+
if self.match(TokenType.LEFT_SQUARE):
|
|
128
|
+
info.nested = self.params(literal=info.type is Literal)
|
|
129
|
+
if self.match(TokenType.PIPE):
|
|
130
|
+
nested = [info] + self.union()
|
|
131
|
+
info = TypeInfo('Union', nested=nested)
|
|
132
|
+
return info
|
|
133
|
+
|
|
134
|
+
def params(self, literal: bool = False) -> 'list[TypeInfo]':
|
|
135
|
+
params = []
|
|
136
|
+
prev = None
|
|
137
|
+
while True:
|
|
138
|
+
token = self.peek()
|
|
139
|
+
if not token:
|
|
140
|
+
self.error("Closing ']' missing.")
|
|
141
|
+
if token.type is TokenType.RIGHT_SQUARE:
|
|
142
|
+
self.advance()
|
|
143
|
+
break
|
|
144
|
+
if token.type is TokenType.COMMA:
|
|
145
|
+
if not prev or prev.type is TokenType.COMMA:
|
|
146
|
+
self.error("Type missing before ','.")
|
|
147
|
+
self.advance()
|
|
148
|
+
prev = token
|
|
149
|
+
continue
|
|
150
|
+
if token.type is TokenType.NAME:
|
|
151
|
+
param = self.type()
|
|
152
|
+
elif token.type is TokenType.LEFT_SQUARE:
|
|
153
|
+
self.advance()
|
|
154
|
+
param = TypeInfo()
|
|
155
|
+
param.nested = self.params()
|
|
156
|
+
if literal:
|
|
157
|
+
param = self._literal_param(param)
|
|
158
|
+
params.append(param)
|
|
159
|
+
prev = token
|
|
160
|
+
if literal and not params:
|
|
161
|
+
self.error('Literal cannot be empty.')
|
|
162
|
+
return params
|
|
163
|
+
|
|
164
|
+
def _literal_param(self, param: TypeInfo) -> TypeInfo:
|
|
165
|
+
try:
|
|
166
|
+
if param.name is None:
|
|
167
|
+
raise ValueError
|
|
168
|
+
try:
|
|
169
|
+
value = literal_eval(param.name)
|
|
170
|
+
except ValueError:
|
|
171
|
+
if param.name.isidentifier():
|
|
172
|
+
return TypeInfo(param.name, None)
|
|
173
|
+
raise
|
|
174
|
+
if not isinstance(value, LITERAL_TYPES):
|
|
175
|
+
raise ValueError
|
|
176
|
+
except (ValueError, SyntaxError):
|
|
177
|
+
self.error(f"Invalid literal value {str(param)!r}.")
|
|
178
|
+
else:
|
|
179
|
+
return TypeInfo(repr(value), value)
|
|
180
|
+
|
|
181
|
+
def union(self) -> 'list[TypeInfo]':
|
|
182
|
+
types = []
|
|
183
|
+
while not types or self.match(TokenType.PIPE):
|
|
184
|
+
info = self.type()
|
|
185
|
+
if info.is_union:
|
|
186
|
+
types.extend(info.nested)
|
|
187
|
+
else:
|
|
188
|
+
types.append(info)
|
|
189
|
+
return types
|
|
190
|
+
|
|
191
|
+
def match(self, *types: TokenType) -> bool:
|
|
192
|
+
for typ in types:
|
|
193
|
+
if self.check(typ):
|
|
194
|
+
self.advance()
|
|
195
|
+
return True
|
|
196
|
+
return False
|
|
197
|
+
|
|
198
|
+
def check(self, expected: TokenType) -> bool:
|
|
199
|
+
peeked = self.peek()
|
|
200
|
+
return peeked and peeked.type == expected
|
|
201
|
+
|
|
202
|
+
def advance(self) -> 'Token|None':
|
|
203
|
+
token = self.peek()
|
|
204
|
+
if token:
|
|
205
|
+
self.current += 1
|
|
206
|
+
return token
|
|
207
|
+
|
|
208
|
+
def peek(self) -> 'Token|None':
|
|
209
|
+
try:
|
|
210
|
+
return self.tokens[self.current]
|
|
211
|
+
except IndexError:
|
|
212
|
+
return None
|
|
213
|
+
|
|
214
|
+
def error(self, message: str, token: 'Token|None' = None):
|
|
215
|
+
if not token:
|
|
216
|
+
token = self.peek()
|
|
217
|
+
position = f'index {token.position}' if token else 'end'
|
|
218
|
+
raise ValueError(f"Parsing type {self.source!r} failed: "
|
|
219
|
+
f"Error at {position}: {message}")
|
|
@@ -0,0 +1,60 @@
|
|
|
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
|
+
from collections.abc import Mapping, Sequence
|
|
17
|
+
from typing import TYPE_CHECKING
|
|
18
|
+
|
|
19
|
+
from robot.errors import DataError
|
|
20
|
+
from robot.utils import (is_dict_like, is_list_like, plural_or_not as s,
|
|
21
|
+
seq2str, type_name)
|
|
22
|
+
|
|
23
|
+
from .typeinfo import TypeInfo
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from .argumentspec import ArgumentSpec
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class TypeValidator:
|
|
30
|
+
|
|
31
|
+
def __init__(self, spec: 'ArgumentSpec'):
|
|
32
|
+
self.spec = spec
|
|
33
|
+
|
|
34
|
+
def validate(self, types: 'Mapping|Sequence|None') -> 'dict[str, TypeInfo]|None':
|
|
35
|
+
if types is None:
|
|
36
|
+
return None
|
|
37
|
+
if not types:
|
|
38
|
+
return {}
|
|
39
|
+
if is_dict_like(types):
|
|
40
|
+
self._validate_type_dict(types)
|
|
41
|
+
elif is_list_like(types):
|
|
42
|
+
types = self._type_list_to_dict(types)
|
|
43
|
+
else:
|
|
44
|
+
raise DataError(f'Type information must be given as a dictionary or '
|
|
45
|
+
f'a list, got {type_name(types)}.')
|
|
46
|
+
return {k: TypeInfo.from_type_hint(types[k]) for k in types}
|
|
47
|
+
|
|
48
|
+
def _validate_type_dict(self, types: Mapping):
|
|
49
|
+
names = set(self.spec.argument_names)
|
|
50
|
+
extra = [t for t in types if t not in names]
|
|
51
|
+
if extra:
|
|
52
|
+
raise DataError(f'Type information given to non-existing '
|
|
53
|
+
f'argument{s(extra)} {seq2str(sorted(extra))}.')
|
|
54
|
+
|
|
55
|
+
def _type_list_to_dict(self, types: Sequence) -> dict:
|
|
56
|
+
names = self.spec.argument_names
|
|
57
|
+
if len(types) > len(names):
|
|
58
|
+
raise DataError(f'Type information given to {len(types)} argument{s(types)} '
|
|
59
|
+
f'but keyword has only {len(names)} argument{s(names)}.')
|
|
60
|
+
return {name: value for name, value in zip(names, types) if value}
|