robotframework 6.1rc1__zip → 7.0__zip

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (307) hide show
  1. {robotframework-6.1rc1 → robotframework-7.0}/INSTALL.rst +25 -25
  2. {robotframework-6.1rc1 → robotframework-7.0}/PKG-INFO +6 -8
  3. {robotframework-6.1rc1 → robotframework-7.0}/README.rst +3 -2
  4. {robotframework-6.1rc1 → robotframework-7.0}/setup.py +3 -6
  5. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/api/__init__.py +4 -1
  6. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/api/deco.py +60 -14
  7. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/api/exceptions.py +6 -6
  8. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/api/interfaces.py +403 -68
  9. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/api/logger.py +27 -20
  10. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/api/parsing.py +4 -2
  11. robotframework-7.0/src/robot/api/py.typed +0 -0
  12. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/conf/gatherfailed.py +2 -2
  13. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/conf/languages.py +61 -25
  14. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/conf/settings.py +21 -14
  15. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/common/storage.js +11 -6
  16. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/htmlfilewriter.py +1 -1
  17. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/libdoc/libdoc.css +10 -2
  18. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/libdoc/libdoc.html +10 -0
  19. robotframework-7.0/src/robot/htmldata/rebot/common.css +362 -0
  20. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/doc_formatting.css +13 -7
  21. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/log.css +51 -10
  22. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/log.html +14 -23
  23. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/model.js +2 -4
  24. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/report.css +83 -19
  25. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/report.html +22 -7
  26. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/testdata.js +10 -3
  27. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/view.js +84 -0
  28. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/template.py +3 -3
  29. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/builder.py +0 -8
  30. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/datatypes.py +9 -10
  31. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/htmlutils.py +4 -7
  32. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/jsonbuilder.py +38 -20
  33. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/model.py +21 -28
  34. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/robotbuilder.py +50 -31
  35. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/standardtypes.py +9 -1
  36. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/xmlbuilder.py +35 -20
  37. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/xmlwriter.py +18 -38
  38. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/BuiltIn.py +255 -205
  39. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/Collections.py +384 -240
  40. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/DateTime.py +73 -60
  41. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/OperatingSystem.py +6 -5
  42. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/Process.py +26 -27
  43. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/Remote.py +40 -36
  44. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/String.py +66 -76
  45. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/XML.py +66 -59
  46. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/__init__.py +2 -2
  47. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/dialogs_py.py +14 -2
  48. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/__init__.py +5 -5
  49. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/body.py +44 -13
  50. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/configurer.py +19 -15
  51. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/control.py +230 -88
  52. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/filter.py +31 -25
  53. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/itemlist.py +9 -8
  54. robotframework-7.0/src/robot/model/keyword.py +77 -0
  55. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/message.py +19 -10
  56. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/modelobject.py +27 -15
  57. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/modifier.py +1 -1
  58. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/namepatterns.py +3 -25
  59. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/stats.py +9 -15
  60. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/tags.py +1 -1
  61. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/testcase.py +15 -27
  62. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/testsuite.py +44 -28
  63. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/visitor.py +35 -8
  64. robotframework-7.0/src/robot/output/console/dotted.py +90 -0
  65. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/console/quiet.py +3 -2
  66. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/console/verbose.py +61 -61
  67. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/debugfile.py +39 -24
  68. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/filelogger.py +18 -15
  69. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/librarylogger.py +3 -2
  70. robotframework-7.0/src/robot/output/listeners.py +753 -0
  71. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/logger.py +216 -80
  72. robotframework-7.0/src/robot/output/loggerapi.py +215 -0
  73. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/loggerhelper.py +23 -56
  74. robotframework-7.0/src/robot/output/output.py +185 -0
  75. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/stdoutlogsplitter.py +8 -5
  76. robotframework-7.0/src/robot/output/xmllogger.py +459 -0
  77. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/lexer/blocklexers.py +17 -13
  78. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/lexer/context.py +12 -2
  79. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/lexer/lexer.py +1 -1
  80. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/lexer/settings.py +6 -3
  81. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/lexer/statementlexers.py +39 -10
  82. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/lexer/tokens.py +27 -29
  83. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/model/blocks.py +19 -6
  84. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/model/statements.py +193 -94
  85. robotframework-7.0/src/robot/parsing/model/visitor.py +100 -0
  86. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/rebot.py +2 -0
  87. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/expandkeywordmatcher.py +1 -1
  88. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/jsbuildingcontext.py +8 -6
  89. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/jsmodelbuilders.py +88 -61
  90. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/logreportwriters.py +16 -10
  91. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/outputwriter.py +18 -2
  92. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/resultwriter.py +4 -4
  93. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/stringcache.py +3 -3
  94. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/xunitwriter.py +15 -21
  95. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/__init__.py +3 -2
  96. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/configurer.py +12 -9
  97. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/executionresult.py +56 -8
  98. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/flattenkeywordmatcher.py +48 -9
  99. robotframework-7.0/src/robot/result/keywordremover.py +207 -0
  100. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/merger.py +1 -1
  101. robotframework-7.0/src/robot/result/model.py +1185 -0
  102. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/modeldeprecation.py +9 -3
  103. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/resultbuilder.py +46 -36
  104. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/xmlelementhandlers.py +83 -43
  105. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/run.py +3 -1
  106. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/__init__.py +14 -4
  107. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/arguments/__init__.py +2 -1
  108. robotframework-7.0/src/robot/running/arguments/argumentconverter.py +100 -0
  109. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/arguments/argumentmapper.py +26 -24
  110. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/arguments/argumentparser.py +85 -61
  111. robotframework-7.0/src/robot/running/arguments/argumentresolver.py +158 -0
  112. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/arguments/argumentspec.py +83 -116
  113. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/arguments/argumentvalidator.py +33 -27
  114. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/arguments/customconverters.py +15 -12
  115. robotframework-7.0/src/robot/running/arguments/embedded.py +145 -0
  116. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/arguments/typeconverters.py +268 -220
  117. robotframework-7.0/src/robot/running/arguments/typeinfo.py +317 -0
  118. robotframework-7.0/src/robot/running/arguments/typeinfoparser.py +219 -0
  119. robotframework-7.0/src/robot/running/arguments/typevalidator.py +60 -0
  120. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/bodyrunner.py +147 -129
  121. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/builder/builders.py +17 -26
  122. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/builder/parsers.py +17 -11
  123. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/builder/settings.py +9 -17
  124. robotframework-7.0/src/robot/running/builder/transformers.py +535 -0
  125. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/context.py +113 -31
  126. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/dynamicmethods.py +62 -34
  127. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/importer.py +27 -49
  128. robotframework-7.0/src/robot/running/invalidkeyword.py +67 -0
  129. robotframework-7.0/src/robot/running/keywordfinder.py +91 -0
  130. robotframework-7.0/src/robot/running/keywordimplementation.py +164 -0
  131. robotframework-7.0/src/robot/running/librarykeyword.py +392 -0
  132. robotframework-7.0/src/robot/running/librarykeywordrunner.py +250 -0
  133. robotframework-7.0/src/robot/running/libraryscopes.py +115 -0
  134. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/model.py +198 -337
  135. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/namespace.py +124 -137
  136. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/outputcapture.py +20 -12
  137. robotframework-7.0/src/robot/running/resourcemodel.py +448 -0
  138. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/statusreporter.py +16 -13
  139. robotframework-7.0/src/robot/running/suiterunner.py +265 -0
  140. robotframework-7.0/src/robot/running/testlibraries.py +500 -0
  141. robotframework-7.0/src/robot/running/userkeywordrunner.py +260 -0
  142. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/testdoc.py +18 -13
  143. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/__init__.py +53 -35
  144. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/connectioncache.py +64 -59
  145. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/dotdict.py +3 -0
  146. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/encodingsniffer.py +9 -4
  147. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/escaping.py +19 -19
  148. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/importer.py +2 -2
  149. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/markupwriters.py +23 -15
  150. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/misc.py +1 -2
  151. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/normalizing.py +9 -24
  152. robotframework-7.0/src/robot/utils/notset.py +33 -0
  153. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/platform.py +15 -5
  154. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/robotio.py +9 -9
  155. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/robotpath.py +1 -0
  156. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/robottime.py +122 -90
  157. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/robottypes.py +21 -31
  158. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/setter.py +0 -5
  159. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/text.py +7 -4
  160. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/unic.py +2 -2
  161. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/__init__.py +2 -2
  162. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/assigner.py +17 -19
  163. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/evaluation.py +20 -17
  164. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/filesetter.py +80 -80
  165. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/replacer.py +33 -36
  166. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/scopes.py +17 -12
  167. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/search.py +66 -41
  168. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/store.py +14 -16
  169. robotframework-7.0/src/robot/variables/tablesetter.py +164 -0
  170. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/variables.py +1 -1
  171. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/version.py +1 -1
  172. {robotframework-6.1rc1 → robotframework-7.0}/src/robotframework.egg-info/PKG-INFO +6 -8
  173. {robotframework-6.1rc1 → robotframework-7.0}/src/robotframework.egg-info/SOURCES.txt +10 -8
  174. {robotframework-6.1rc1 → robotframework-7.0}/tasks.py +6 -6
  175. robotframework-6.1rc1/src/robot/htmldata/rebot/common.css +0 -239
  176. robotframework-6.1rc1/src/robot/libraries/Reserved.py +0 -48
  177. robotframework-6.1rc1/src/robot/model/keyword.py +0 -173
  178. robotframework-6.1rc1/src/robot/output/console/dotted.py +0 -90
  179. robotframework-6.1rc1/src/robot/output/listenerarguments.py +0 -165
  180. robotframework-6.1rc1/src/robot/output/listenermethods.py +0 -113
  181. robotframework-6.1rc1/src/robot/output/listeners.py +0 -174
  182. robotframework-6.1rc1/src/robot/output/output.py +0 -95
  183. robotframework-6.1rc1/src/robot/output/xmllogger.py +0 -354
  184. robotframework-6.1rc1/src/robot/parsing/model/visitor.py +0 -67
  185. robotframework-6.1rc1/src/robot/result/keywordremover.py +0 -183
  186. robotframework-6.1rc1/src/robot/result/model.py +0 -957
  187. robotframework-6.1rc1/src/robot/running/arguments/argumentconverter.py +0 -79
  188. robotframework-6.1rc1/src/robot/running/arguments/argumentresolver.py +0 -131
  189. robotframework-6.1rc1/src/robot/running/arguments/embedded.py +0 -146
  190. robotframework-6.1rc1/src/robot/running/arguments/typevalidator.py +0 -56
  191. robotframework-6.1rc1/src/robot/running/builder/transformers.py +0 -642
  192. robotframework-6.1rc1/src/robot/running/handlers.py +0 -321
  193. robotframework-6.1rc1/src/robot/running/handlerstore.py +0 -66
  194. robotframework-6.1rc1/src/robot/running/librarykeywordrunner.py +0 -238
  195. robotframework-6.1rc1/src/robot/running/libraryscopes.py +0 -97
  196. robotframework-6.1rc1/src/robot/running/modelcombiner.py +0 -32
  197. robotframework-6.1rc1/src/robot/running/suiterunner.py +0 -249
  198. robotframework-6.1rc1/src/robot/running/testlibraries.py +0 -420
  199. robotframework-6.1rc1/src/robot/running/usererrorhandler.py +0 -75
  200. robotframework-6.1rc1/src/robot/running/userkeyword.py +0 -111
  201. robotframework-6.1rc1/src/robot/running/userkeywordrunner.py +0 -274
  202. robotframework-6.1rc1/src/robot/variables/tablesetter.py +0 -156
  203. {robotframework-6.1rc1 → robotframework-7.0}/AUTHORS.rst +0 -0
  204. {robotframework-6.1rc1 → robotframework-7.0}/BUILD.rst +0 -0
  205. {robotframework-6.1rc1 → robotframework-7.0}/CONTRIBUTING.rst +0 -0
  206. {robotframework-6.1rc1 → robotframework-7.0}/COPYRIGHT.txt +0 -0
  207. {robotframework-6.1rc1 → robotframework-7.0}/LICENSE.txt +0 -0
  208. {robotframework-6.1rc1 → robotframework-7.0}/MANIFEST.in +0 -0
  209. {robotframework-6.1rc1 → robotframework-7.0}/setup.cfg +0 -0
  210. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/__init__.py +0 -0
  211. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/__main__.py +0 -0
  212. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/conf/__init__.py +0 -0
  213. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/errors.py +0 -0
  214. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/__init__.py +0 -0
  215. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/common/__init__.py +0 -0
  216. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/common/js_disabled.css +0 -0
  217. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/jsonwriter.py +0 -0
  218. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/lib/__init__.py +0 -0
  219. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/lib/jquery.highlight.min.js +0 -0
  220. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/lib/jquery.min.js +0 -0
  221. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/lib/jquery.tablesorter.min.js +0 -0
  222. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/lib/jquery.tmpl.min.js +0 -0
  223. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/lib/jsxcompressor.min.js +0 -0
  224. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/libdoc/__init__.py +0 -0
  225. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/libdoc/doc_formatting.css +0 -0
  226. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/libdoc/print.css +0 -0
  227. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/libdoc/pygments.css +0 -0
  228. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/__init__.py +0 -0
  229. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/fileloading.js +0 -0
  230. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/log.js +0 -0
  231. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/print.css +0 -0
  232. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/rebot/util.js +0 -0
  233. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/testdoc/__init__.py +0 -0
  234. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/testdoc/testdoc.css +0 -0
  235. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/htmldata/testdoc/testdoc.html +0 -0
  236. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdoc.py +0 -0
  237. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/__init__.py +0 -0
  238. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/consoleviewer.py +0 -0
  239. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/htmlwriter.py +0 -0
  240. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/jsonwriter.py +0 -0
  241. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/output.py +0 -0
  242. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libdocpkg/writer.py +0 -0
  243. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/Dialogs.py +0 -0
  244. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/Easter.py +0 -0
  245. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/Screenshot.py +0 -0
  246. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/libraries/Telnet.py +0 -0
  247. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/fixture.py +0 -0
  248. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/metadata.py +0 -0
  249. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/statistics.py +0 -0
  250. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/suitestatistics.py +0 -0
  251. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/tagsetter.py +0 -0
  252. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/tagstatistics.py +0 -0
  253. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/model/totalstatistics.py +0 -0
  254. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/__init__.py +0 -0
  255. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/console/__init__.py +0 -0
  256. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/console/highlighting.py +0 -0
  257. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/output/pyloggingconf.py +0 -0
  258. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/__init__.py +0 -0
  259. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/lexer/__init__.py +0 -0
  260. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/lexer/tokenizer.py +0 -0
  261. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/model/__init__.py +0 -0
  262. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/parser/__init__.py +0 -0
  263. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/parser/blockparsers.py +0 -0
  264. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/parser/fileparser.py +0 -0
  265. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/parser/parser.py +0 -0
  266. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/parsing/suitestructure.py +0 -0
  267. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/pythonpathsetter.py +0 -0
  268. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/__init__.py +0 -0
  269. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/jsexecutionresult.py +0 -0
  270. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/reporting/jswriter.py +0 -0
  271. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/executionerrors.py +0 -0
  272. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/messagefilter.py +0 -0
  273. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/suiteteardownfailed.py +0 -0
  274. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/result/visitor.py +0 -0
  275. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/builder/__init__.py +0 -0
  276. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/randomizer.py +0 -0
  277. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/runkwregister.py +0 -0
  278. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/signalhandler.py +0 -0
  279. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/status.py +0 -0
  280. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/timeouts/__init__.py +0 -0
  281. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/timeouts/posix.py +0 -0
  282. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/running/timeouts/windows.py +0 -0
  283. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/application.py +0 -0
  284. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/argumentparser.py +0 -0
  285. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/asserts.py +0 -0
  286. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/charwidth.py +0 -0
  287. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/compress.py +0 -0
  288. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/encoding.py +0 -0
  289. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/error.py +0 -0
  290. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/etreewrapper.py +0 -0
  291. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/filereader.py +0 -0
  292. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/frange.py +0 -0
  293. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/htmlformatters.py +0 -0
  294. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/markuputils.py +0 -0
  295. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/match.py +0 -0
  296. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/recommendations.py +0 -0
  297. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/restreader.py +0 -0
  298. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/robotenv.py +0 -0
  299. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/robotinspect.py +0 -0
  300. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/sortable.py +0 -0
  301. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/utils/typehints.py +0 -0
  302. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/finders.py +0 -0
  303. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/notfound.py +0 -0
  304. {robotframework-6.1rc1 → robotframework-7.0}/src/robot/variables/resolvable.py +0 -0
  305. {robotframework-6.1rc1 → robotframework-7.0}/src/robotframework.egg-info/dependency_links.txt +0 -0
  306. {robotframework-6.1rc1 → robotframework-7.0}/src/robotframework.egg-info/entry_points.txt +0 -0
  307. {robotframework-6.1rc1 → robotframework-7.0}/src/robotframework.egg-info/top_level.txt +0 -0
@@ -15,22 +15,15 @@
15
15
 
16
16
  import copy
17
17
  from ast import literal_eval
18
+ from itertools import chain
18
19
 
19
20
  from robot.api import logger
20
- from robot.utils import (get_error_message, is_dict_like, is_list_like, is_truthy,
21
- Matcher, plural_or_not as s, seq2str, seq2str2, type_name)
21
+ from robot.utils import (get_error_message, is_dict_like, is_list_like, Matcher,
22
+ NOT_SET, plural_or_not as s, seq2str, seq2str2, type_name)
22
23
  from robot.utils.asserts import assert_equal
23
24
  from robot.version import get_version
24
25
 
25
26
 
26
- class NotSet:
27
- def __repr__(self):
28
- return ""
29
-
30
-
31
- NOT_SET = NotSet()
32
-
33
-
34
27
  class _List:
35
28
 
36
29
  def convert_to_list(self, item):
@@ -93,10 +86,7 @@ class _List:
93
86
  | ${L1} and ${L2} are not changed.
94
87
  """
95
88
  self._validate_lists(*lists)
96
- ret = []
97
- for item in lists:
98
- ret.extend(item)
99
- return ret
89
+ return list(chain.from_iterable(lists))
100
90
 
101
91
  def set_list_value(self, list_, index, value):
102
92
  """Sets the value of ``list`` specified by ``index`` to the given ``value``.
@@ -263,19 +253,19 @@ class _List:
263
253
  | ${L5} is not changed
264
254
  """
265
255
  self._validate_list(list_)
266
- if start == '':
267
- start = 0
256
+ start = self._index_to_int(start, empty_to_zero=True)
268
257
  list_ = self.get_slice_from_list(list_, start, end)
269
258
  try:
270
- return int(start) + list_.index(value)
259
+ return start + list_.index(value)
271
260
  except ValueError:
272
261
  return -1
273
262
 
274
263
  def copy_list(self, list_, deepcopy=False):
275
264
  """Returns a copy of the given list.
276
265
 
277
- If the optional ``deepcopy`` is given a true value, the returned
278
- list is a deep copy. New option in Robot Framework 3.1.2.
266
+ By default, returns a new list with same items as in the original.
267
+ Set the ``deepcopy`` argument to a true value if also items should
268
+ be copied.
279
269
 
280
270
  The given list is never altered by this keyword.
281
271
  """
@@ -301,36 +291,44 @@ class _List:
301
291
  """Sorts the given list in place.
302
292
 
303
293
  Sorting fails if items in the list are not comparable with each others.
304
- On Python 2 most objects are comparable, but on Python 3 comparing,
305
- for example, strings with numbers is not possible.
294
+ For example, sorting a list containing strings and numbers is not possible.
306
295
 
307
296
  Note that the given list is changed and nothing is returned. Use
308
- `Copy List` first, if you need to keep also the original order.
297
+ `Copy List` first, if you need to preserve the list also in the original
298
+ order.
309
299
  """
310
300
  self._validate_list(list_)
311
301
  list_.sort()
312
302
 
313
- def list_should_contain_value(self, list_, value, msg=None):
303
+ def list_should_contain_value(self, list_, value, msg=None, ignore_case=False):
314
304
  """Fails if the ``value`` is not found from ``list``.
315
305
 
316
306
  Use the ``msg`` argument to override the default error message.
307
+
308
+ The ``ignore_case`` argument can be used to make comparison case-insensitive.
309
+ See the `Ignore case` section for more details. This option is new in
310
+ Robot Framework 7.0.
317
311
  """
318
312
  self._validate_list(list_)
319
- _verify_condition(value in list_,
320
- f"{seq2str2(list_)} does not contain value '{value}'.",
321
- msg)
313
+ normalize = Normalizer(ignore_case).normalize
314
+ _verify_condition(normalize(value) in normalize(list_),
315
+ f"{seq2str2(list_)} does not contain value '{value}'.", msg)
322
316
 
323
- def list_should_not_contain_value(self, list_, value, msg=None):
317
+ def list_should_not_contain_value(self, list_, value, msg=None, ignore_case=False):
324
318
  """Fails if the ``value`` is found from ``list``.
325
319
 
326
320
  Use the ``msg`` argument to override the default error message.
321
+
322
+ The ``ignore_case`` argument can be used to make comparison case-insensitive.
323
+ See the `Ignore case` section for more details. This option is new in
324
+ Robot Framework 7.0.
327
325
  """
328
326
  self._validate_list(list_)
329
- _verify_condition(value not in list_,
330
- f"{seq2str2(list_)} contains value '{value}'.",
331
- msg)
327
+ normalize = Normalizer(ignore_case).normalize
328
+ _verify_condition(normalize(value) not in normalize(list_),
329
+ f"{seq2str2(list_)} contains value '{value}'.", msg)
332
330
 
333
- def list_should_not_contain_duplicates(self, list_, msg=None):
331
+ def list_should_not_contain_duplicates(self, list_, msg=None, ignore_case=False):
334
332
  """Fails if any element in the ``list`` is found from it more than once.
335
333
 
336
334
  The default error message lists all the elements that were found
@@ -340,11 +338,14 @@ class _List:
340
338
 
341
339
  This keyword works with all iterables that can be converted to a list.
342
340
  The original iterable is never altered.
341
+
342
+ The ``ignore_case`` argument can be used to make comparison case-insensitive.
343
+ See the `Ignore case` section for more details. This option is new in
344
+ Robot Framework 7.0.
343
345
  """
344
346
  self._validate_list(list_)
345
- if not isinstance(list_, list):
346
- list_ = list(list_)
347
347
  dupes = []
348
+ list_ = Normalizer(ignore_case).normalize(list_)
348
349
  for item in list_:
349
350
  if item not in dupes:
350
351
  count = list_.count(item)
@@ -355,7 +356,7 @@ class _List:
355
356
  raise AssertionError(msg or f'{seq2str(dupes)} found multiple times.')
356
357
 
357
358
  def lists_should_be_equal(self, list1, list2, msg=None, values=True,
358
- names=None, ignore_order=False):
359
+ names=None, ignore_order=False, ignore_case=False):
359
360
  """Fails if given lists are unequal.
360
361
 
361
362
  The keyword first verifies that the lists have equal lengths, and then
@@ -392,12 +393,17 @@ class _List:
392
393
 
393
394
  The optional ``ignore_order`` argument can be used to ignore the order
394
395
  of the elements in the lists. Using it requires items to be sortable.
395
- This is new in Robot Framework 3.2.
396
+ This option works recursively with nested lists starting from Robot
397
+ Framework 7.0.
396
398
 
397
399
  Example:
398
400
  | ${list1} = | Create List | apple | cherry | banana |
399
401
  | ${list2} = | Create List | cherry | banana | apple |
400
402
  | Lists Should Be Equal | ${list1} | ${list2} | ignore_order=True |
403
+
404
+ The ``ignore_case`` argument can be used to make comparison case-insensitive.
405
+ See the `Ignore case` section for more details. This option is new in
406
+ Robot Framework 7.0.
401
407
  """
402
408
  self._validate_lists(list1, list2)
403
409
  len1 = len(list1)
@@ -406,10 +412,9 @@ class _List:
406
412
  f'Lengths are different: {len1} != {len2}',
407
413
  msg, values)
408
414
  names = self._get_list_index_name_mapping(names, len1)
409
- if ignore_order:
410
- list1 = sorted(list1)
411
- list2 = sorted(list2)
412
- diffs = '\n'.join(self._yield_list_diffs(list1, list2, names))
415
+ normalize = Normalizer(ignore_case, ignore_order).normalize
416
+ diffs = '\n'.join(self._yield_list_diffs(normalize(list1), normalize(list2),
417
+ names))
413
418
  _verify_condition(not diffs,
414
419
  f'Lists are different:\n{diffs}',
415
420
  msg, values)
@@ -418,7 +423,7 @@ class _List:
418
423
  if not names:
419
424
  return {}
420
425
  if is_dict_like(names):
421
- return dict((int(index), names[index]) for index in names)
426
+ return {int(index): names[index] for index in names}
422
427
  return dict(zip(range(list_length), names))
423
428
 
424
429
  def _yield_list_diffs(self, list1, list2, names):
@@ -429,7 +434,8 @@ class _List:
429
434
  except AssertionError as err:
430
435
  yield str(err)
431
436
 
432
- def list_should_contain_sub_list(self, list1, list2, msg=None, values=True):
437
+ def list_should_contain_sub_list(self, list1, list2, msg=None, values=True,
438
+ ignore_case=False):
433
439
  """Fails if not all elements in ``list2`` are found in ``list1``.
434
440
 
435
441
  The order of values and the number of values are not taken into
@@ -437,8 +443,15 @@ class _List:
437
443
 
438
444
  See `Lists Should Be Equal` for more information about configuring
439
445
  the error message with ``msg`` and ``values`` arguments.
446
+
447
+ The ``ignore_case`` argument can be used to make comparison case-insensitive.
448
+ See the `Ignore case` section for more details. This option is new in
449
+ Robot Framework 7.0.
440
450
  """
441
451
  self._validate_lists(list1, list2)
452
+ normalize = Normalizer(ignore_case).normalize
453
+ list1 = normalize(list1)
454
+ list2 = normalize(list2)
442
455
  diffs = ', '.join(str(item) for item in list2 if item not in list1)
443
456
  _verify_condition(not diffs,
444
457
  f'Following values were not found from first list: {diffs}',
@@ -586,12 +599,9 @@ class _Dictionary:
586
599
  def copy_dictionary(self, dictionary, deepcopy=False):
587
600
  """Returns a copy of the given dictionary.
588
601
 
589
- The ``deepcopy`` argument controls should the returned dictionary be
590
- a [https://docs.python.org/library/copy.html|shallow or deep copy].
591
- By default returns a shallow copy, but that can be changed by giving
592
- ``deepcopy`` a true value (see `Boolean arguments`). This is a new
593
- option in Robot Framework 3.1.2. Earlier versions always returned
594
- shallow copies.
602
+ By default, returns a new dictionary with same items as in the original.
603
+ Set the ``deepcopy`` argument to a true value if also items should
604
+ be copied.
595
605
 
596
606
  The given dictionary is never altered by this keyword.
597
607
  """
@@ -603,53 +613,36 @@ class _Dictionary:
603
613
  def get_dictionary_keys(self, dictionary, sort_keys=True):
604
614
  """Returns keys of the given ``dictionary`` as a list.
605
615
 
606
- By default keys are returned in sorted order (assuming they are
616
+ By default, keys are returned in sorted order (assuming they are
607
617
  sortable), but they can be returned in the original order by giving
608
- ``sort_keys`` a false value (see `Boolean arguments`). Notice that
609
- with Python 3.5 and earlier dictionary order is undefined unless using
610
- ordered dictionaries.
618
+ ``sort_keys`` a false value.
611
619
 
612
620
  The given ``dictionary`` is never altered by this keyword.
613
621
 
614
622
  Example:
615
623
  | ${sorted} = | Get Dictionary Keys | ${D3} |
616
624
  | ${unsorted} = | Get Dictionary Keys | ${D3} | sort_keys=False |
617
- =>
618
- | ${sorted} = ['a', 'b', 'c']
619
- | ${unsorted} = ['b', 'a', 'c'] # Order depends on Python version.
620
-
621
- ``sort_keys`` is a new option in Robot Framework 3.1.2. Earlier keys
622
- were always sorted.
623
625
  """
624
626
  self._validate_dictionary(dictionary)
625
- keys = dictionary.keys()
626
627
  if sort_keys:
627
628
  try:
628
- return sorted(keys)
629
+ return sorted(dictionary)
629
630
  except TypeError:
630
631
  pass
631
- return list(keys)
632
+ return list(dictionary)
632
633
 
633
634
  def get_dictionary_values(self, dictionary, sort_keys=True):
634
635
  """Returns values of the given ``dictionary`` as a list.
635
636
 
636
637
  Uses `Get Dictionary Keys` to get keys and then returns corresponding
637
- values. By default keys are sorted and values returned in that order,
638
- but this can be changed by giving ``sort_keys`` a false value (see
639
- `Boolean arguments`). Notice that with Python 3.5 and earlier
640
- dictionary order is undefined unless using ordered dictionaries.
638
+ values. By default, keys are sorted and values returned in that order,
639
+ but this can be changed by giving ``sort_keys`` a false value.
641
640
 
642
641
  The given ``dictionary`` is never altered by this keyword.
643
642
 
644
643
  Example:
645
644
  | ${sorted} = | Get Dictionary Values | ${D3} |
646
645
  | ${unsorted} = | Get Dictionary Values | ${D3} | sort_keys=False |
647
- =>
648
- | ${sorted} = [1, 2, 3]
649
- | ${unsorted} = [2, 1, 3] # Order depends on Python version.
650
-
651
- ``sort_keys`` is a new option in Robot Framework 3.1.2. Earlier values
652
- were always sorted based on keys.
653
646
  """
654
647
  self._validate_dictionary(dictionary)
655
648
  keys = self.get_dictionary_keys(dictionary, sort_keys=sort_keys)
@@ -659,10 +652,8 @@ class _Dictionary:
659
652
  """Returns items of the given ``dictionary`` as a list.
660
653
 
661
654
  Uses `Get Dictionary Keys` to get keys and then returns corresponding
662
- items. By default keys are sorted and items returned in that order,
663
- but this can be changed by giving ``sort_keys`` a false value (see
664
- `Boolean arguments`). Notice that with Python 3.5 and earlier
665
- dictionary order is undefined unless using ordered dictionaries.
655
+ items. By default, keys are sorted and items returned in that order,
656
+ but this can be changed by giving ``sort_keys`` a false value.
666
657
 
667
658
  Items are returned as a flat list so that first item is a key,
668
659
  second item is a corresponding value, third item is the second key,
@@ -673,12 +664,6 @@ class _Dictionary:
673
664
  Example:
674
665
  | ${sorted} = | Get Dictionary Items | ${D3} |
675
666
  | ${unsorted} = | Get Dictionary Items | ${D3} | sort_keys=False |
676
- =>
677
- | ${sorted} = ['a', 1, 'b', 2, 'c', 3]
678
- | ${unsorted} = ['b', 2, 'a', 1, 'c', 3] # Order depends on Python version.
679
-
680
- ``sort_keys`` is a new option in Robot Framework 3.1.2. Earlier items
681
- were always sorted based on keys.
682
667
  """
683
668
  self._validate_dictionary(dictionary)
684
669
  keys = self.get_dictionary_keys(dictionary, sort_keys=sort_keys)
@@ -708,61 +693,95 @@ class _Dictionary:
708
693
  return default
709
694
  raise RuntimeError(f"Dictionary does not contain key '{key}'.")
710
695
 
711
- def dictionary_should_contain_key(self, dictionary, key, msg=None):
696
+ def dictionary_should_contain_key(self, dictionary, key, msg=None,
697
+ ignore_case=False):
712
698
  """Fails if ``key`` is not found from ``dictionary``.
713
699
 
714
700
  Use the ``msg`` argument to override the default error message.
701
+
702
+ The ``ignore_case`` argument can be used to make comparison case-insensitive.
703
+ See the `Ignore case` section for more details. This option is new in
704
+ Robot Framework 7.0.
715
705
  """
716
706
  self._validate_dictionary(dictionary)
717
- _verify_condition(key in dictionary,
718
- f"Dictionary does not contain key '{key}'.",
719
- msg)
720
-
721
- def dictionary_should_not_contain_key(self, dictionary, key, msg=None):
707
+ norm = Normalizer(ignore_case)
708
+ _verify_condition(
709
+ norm.normalize_key(key) in norm.normalize(dictionary),
710
+ f"Dictionary does not contain key '{key}'.", msg
711
+ )
712
+
713
+ def dictionary_should_not_contain_key(self, dictionary, key, msg=None,
714
+ ignore_case=False):
722
715
  """Fails if ``key`` is found from ``dictionary``.
723
716
 
724
717
  Use the ``msg`` argument to override the default error message.
718
+
719
+ The ``ignore_case`` argument can be used to make comparison case-insensitive.
720
+ See the `Ignore case` section for more details. This option is new in
721
+ Robot Framework 7.0.
725
722
  """
726
723
  self._validate_dictionary(dictionary)
727
- _verify_condition(key not in dictionary,
728
- f"Dictionary contains key '{key}'.",
729
- msg)
730
-
731
- def dictionary_should_contain_item(self, dictionary, key, value, msg=None):
724
+ norm = Normalizer(ignore_case)
725
+ _verify_condition(
726
+ norm.normalize_key(key) not in norm.normalize(dictionary),
727
+ f"Dictionary contains key '{key}'.", msg
728
+ )
729
+
730
+ def dictionary_should_contain_item(self, dictionary, key, value, msg=None,
731
+ ignore_case=False):
732
732
  """An item of ``key`` / ``value`` must be found in a ``dictionary``.
733
733
 
734
- Value is converted to unicode for comparison.
735
-
736
734
  Use the ``msg`` argument to override the default error message.
735
+
736
+ The ``ignore_case`` argument can be used to make comparison case-insensitive.
737
+ See the `Ignore case` section for more details. This option is new in
738
+ Robot Framework 7.0.
737
739
  """
738
740
  self._validate_dictionary(dictionary)
739
- self.dictionary_should_contain_key(dictionary, key, msg)
740
- assert_equal(dictionary[key], value,
741
- msg or f"Value of dictionary key '{key}' does not match",
742
- values=not msg)
743
-
744
- def dictionary_should_contain_value(self, dictionary, value, msg=None):
741
+ self.dictionary_should_contain_key(dictionary, key, msg, ignore_case)
742
+ norm = Normalizer(ignore_case)
743
+ assert_equal(
744
+ norm.normalize(dictionary)[norm.normalize_key(key)],
745
+ norm.normalize_value(value),
746
+ msg or f"Value of dictionary key '{key}' does not match", values=not msg
747
+ )
748
+
749
+ def dictionary_should_contain_value(self, dictionary, value, msg=None,
750
+ ignore_case=False):
745
751
  """Fails if ``value`` is not found from ``dictionary``.
746
752
 
747
753
  Use the ``msg`` argument to override the default error message.
754
+
755
+ The ``ignore_case`` argument can be used to make comparison case-insensitive.
756
+ See the `Ignore case` section for more details. This option is new in
757
+ Robot Framework 7.0.
748
758
  """
749
759
  self._validate_dictionary(dictionary)
750
- _verify_condition(value in dictionary.values(),
751
- f"Dictionary does not contain value '{value}'.",
752
- msg)
753
-
754
- def dictionary_should_not_contain_value(self, dictionary, value, msg=None):
760
+ norm = Normalizer(ignore_case)
761
+ _verify_condition(
762
+ norm.normalize_value(value) in norm.normalize(dictionary).values(),
763
+ f"Dictionary does not contain value '{value}'.", msg
764
+ )
765
+
766
+ def dictionary_should_not_contain_value(self, dictionary, value, msg=None,
767
+ ignore_case=False):
755
768
  """Fails if ``value`` is found from ``dictionary``.
756
769
 
757
770
  Use the ``msg`` argument to override the default error message.
771
+
772
+ The ``ignore_case`` argument can be used to make comparison case-insensitive.
773
+ See the `Ignore case` section for more details. This option is new in
774
+ Robot Framework 7.0.
758
775
  """
759
776
  self._validate_dictionary(dictionary)
760
- _verify_condition(value not in dictionary.values(),
761
- f"Dictionary contains value '{value}'.",
762
- msg)
777
+ norm = Normalizer(ignore_case)
778
+ _verify_condition(
779
+ norm.normalize_value(value) not in norm.normalize(dictionary).values(),
780
+ f"Dictionary contains value '{value}'.", msg
781
+ )
763
782
 
764
783
  def dictionaries_should_be_equal(self, dict1, dict2, msg=None, values=True,
765
- ignore_keys=None):
784
+ ignore_keys=None, ignore_case=False):
766
785
  """Fails if the given dictionaries are not equal.
767
786
 
768
787
  First the equality of dictionaries' keys is checked and after that all
@@ -772,7 +791,8 @@ class _Dictionary:
772
791
 
773
792
  ``ignore_keys`` can be used to provide a list of keys to ignore in the
774
793
  comparison. It can be an actual list or a Python list literal. This
775
- option is new in Robot Framework 6.1.
794
+ option is new in Robot Framework 6.1. It works recursively with nested
795
+ dictionaries starting from Robot Framework 7.0.
776
796
 
777
797
  Examples:
778
798
  | Dictionaries Should Be Equal | ${dict} | ${expected} |
@@ -781,39 +801,58 @@ class _Dictionary:
781
801
 
782
802
  See `Lists Should Be Equal` for more information about configuring
783
803
  the error message with ``msg`` and ``values`` arguments.
804
+
805
+ The ``ignore_case`` argument can be used to make comparison case-insensitive.
806
+ See the `Ignore case` section for more details. This option is new in
807
+ Robot Framework 7.0.
784
808
  """
785
- self._validate_dictionary(dict1)
786
- self._validate_dictionary(dict2, 2)
787
- if ignore_keys:
788
- if isinstance(ignore_keys, str):
789
- try:
790
- ignore_keys = literal_eval(ignore_keys)
791
- except Exception:
792
- raise ValueError("Converting 'ignore_keys' to a list failed: "
793
- + get_error_message())
794
- if not is_list_like(ignore_keys):
795
- raise ValueError(f"'ignore_keys' must be list-like, "
796
- f"got {type_name(ignore_keys)}.")
797
- dict1 = {k: v for k, v in dict1.items() if k not in ignore_keys}
798
- dict2 = {k: v for k, v in dict2.items() if k not in ignore_keys}
799
- keys = self._keys_should_be_equal(dict1, dict2, msg, values)
800
- self._key_values_should_be_equal(keys, dict1, dict2, msg, values)
809
+ self._validate_dictionary(dict1, dict2)
810
+ normalizer = Normalizer(ignore_case, ignore_keys=ignore_keys)
811
+ dict1 = normalizer.normalize(dict1)
812
+ dict2 = normalizer.normalize(dict2)
813
+ self._should_have_same_keys(dict1, dict2, msg, values)
814
+ self._should_have_same_values(dict1, dict2, msg, values)
815
+
816
+ def _should_have_same_keys(self, dict1, dict2, message, values, validate_both=True):
817
+ missing = seq2str([k for k in dict2 if k not in dict1])
818
+ error = ''
819
+ if missing:
820
+ error = f"Following keys missing from first dictionary: {missing}"
821
+ if validate_both:
822
+ missing = seq2str([k for k in dict1 if k not in dict2])
823
+ if missing:
824
+ error += f"\nFollowing keys missing from second dictionary: {missing}"
825
+ if error:
826
+ _report_error(error.strip(), message, values)
827
+
828
+ def _should_have_same_values(self, dict1, dict2, message, values):
829
+ errors = []
830
+ for key in dict2:
831
+ try:
832
+ assert_equal(dict1[key], dict2[key], msg=f'Key {key}')
833
+ except AssertionError as err:
834
+ errors.append(str(err))
835
+ if errors:
836
+ error = '\n'.join([f'Following keys have different values:', *errors])
837
+ _report_error(error, message, values)
801
838
 
802
839
  def dictionary_should_contain_sub_dictionary(self, dict1, dict2, msg=None,
803
- values=True):
840
+ values=True, ignore_case=False):
804
841
  """Fails unless all items in ``dict2`` are found from ``dict1``.
805
842
 
806
843
  See `Lists Should Be Equal` for more information about configuring
807
844
  the error message with ``msg`` and ``values`` arguments.
845
+
846
+ The ``ignore_case`` argument can be used to make comparison case-insensitive.
847
+ See the `Ignore case` section for more details. This option is new in
848
+ Robot Framework 7.0.
808
849
  """
809
- self._validate_dictionary(dict1)
810
- self._validate_dictionary(dict2, 2)
811
- keys = self.get_dictionary_keys(dict2)
812
- diffs = ', '.join(str(k) for k in keys if k not in dict1)
813
- _verify_condition(not diffs,
814
- f"Following keys missing from first dictionary: {diffs}",
815
- msg, values)
816
- self._key_values_should_be_equal(keys, dict1, dict2, msg, values)
850
+ self._validate_dictionary(dict1, dict2)
851
+ normalizer = Normalizer(ignore_case)
852
+ dict1 = normalizer.normalize(dict1)
853
+ dict2 = normalizer.normalize(dict2)
854
+ self._should_have_same_keys(dict1, dict2, msg, values, validate_both=False)
855
+ self._should_have_same_values(dict1, dict2, msg, values)
817
856
 
818
857
  def log_dictionary(self, dictionary, level='INFO'):
819
858
  """Logs the size and contents of the ``dictionary`` using given ``level``.
@@ -836,36 +875,11 @@ class _Dictionary:
836
875
  for key in self.get_dictionary_keys(dictionary):
837
876
  yield f'{key}: {dictionary[key]}'
838
877
 
839
- def _keys_should_be_equal(self, dict1, dict2, msg, values):
840
- keys1 = self.get_dictionary_keys(dict1)
841
- keys2 = self.get_dictionary_keys(dict2)
842
- miss1 = ', '.join(str(k) for k in keys2 if k not in dict1)
843
- miss2 = ', '.join(str(k) for k in keys1 if k not in dict2)
844
- error = []
845
- if miss1:
846
- error += [f'Following keys missing from first dictionary: {miss1}']
847
- if miss2:
848
- error += [f'Following keys missing from second dictionary: {miss2}']
849
- _verify_condition(not error, '\n'.join(error), msg, values)
850
- return keys1
851
-
852
- def _key_values_should_be_equal(self, keys, dict1, dict2, msg, values):
853
- diffs = '\n'.join(self._yield_dict_diffs(keys, dict1, dict2))
854
- _verify_condition(not diffs,
855
- f'Following keys have different values:\n{diffs}',
856
- msg, values)
857
-
858
- def _yield_dict_diffs(self, keys, dict1, dict2):
859
- for key in keys:
860
- try:
861
- assert_equal(dict1[key], dict2[key], msg=f'Key {key}')
862
- except AssertionError as err:
863
- yield str(err)
864
-
865
- def _validate_dictionary(self, dictionary, position=1):
866
- if not is_dict_like(dictionary):
867
- raise TypeError(f"Expected argument {position} to be a dictionary or "
868
- f"dictionary-like, got {type_name(dictionary)} instead.")
878
+ def _validate_dictionary(self, *dictionaries):
879
+ for index, dictionary in enumerate(dictionaries, start=1):
880
+ if not is_dict_like(dictionary):
881
+ raise TypeError(f"Expected argument {index} to be a dictionary, "
882
+ f"got {type_name(dictionary)} instead.")
869
883
 
870
884
 
871
885
  class Collections(_List, _Dictionary):
@@ -903,14 +917,36 @@ class Collections(_List, _Dictionary):
903
917
  = Using with list-like and dictionary-like objects =
904
918
 
905
919
  List keywords that do not alter the given list can also be used
906
- with tuples, and to some extend also with other iterables.
920
+ with tuples, and to some extent also with other iterables.
907
921
  `Convert To List` can be used to convert tuples and other iterables
908
922
  to Python ``list`` objects.
909
923
 
910
- Similarly dictionary keywords can, for most parts, be used with other
924
+ Similarly, dictionary keywords can, for most parts, be used with other
911
925
  mappings. `Convert To Dictionary` can be used if real Python ``dict``
912
926
  objects are needed.
913
927
 
928
+ = Ignore case =
929
+
930
+ Various keywords support ignoring case in comparisons by using the optional
931
+ ``ignore_case`` argument. Case-insensitivity can be enabled by using
932
+ ``ignore_case=True`` (see `Boolean arguments`) and it works recursively.
933
+ With dictionaries, it is also possible to use special values ``keys`` and
934
+ ``values`` to normalize only keys or values, respectively. These options
935
+ themselves are case-insensitive and also singular forms ``key`` and
936
+ ``value`` are supported.
937
+
938
+ If a dictionary contains keys that normalize to the same value, e.g.
939
+ ``{'a': 1, 'A': 2}``, normalizing keys causes an error.
940
+
941
+ Examples:
942
+ | `Lists Should Be Equal` | ${list1} | ${list2} | ignore_case=True |
943
+ | `Dictionaries Should Be Equal` | ${dict1} | ${dict2} | ignore_case=values |
944
+
945
+ Notice that some keywords accept also an older ``case_insensitive`` argument
946
+ in addition to ``ignore_case``. The latter is new in Robot Framework 7.0 and
947
+ should be used unless there is a need to support older versions. The old
948
+ argument is considered deprecated and will eventually be removed.
949
+
914
950
  = Boolean arguments =
915
951
 
916
952
  Some keywords accept arguments that are handled as Boolean values true or
@@ -922,20 +958,9 @@ class Collections(_List, _Dictionary):
922
958
  regardless their value, and other argument types are tested using the same
923
959
  [http://docs.python.org/library/stdtypes.html#truth|rules as in Python].
924
960
 
925
- True examples:
926
- | `Should Contain Match` | ${list} | ${pattern} | case_insensitive=True | # Strings are generally true. |
927
- | `Should Contain Match` | ${list} | ${pattern} | case_insensitive=yes | # Same as the above. |
928
- | `Should Contain Match` | ${list} | ${pattern} | case_insensitive=${TRUE} | # Python ``True`` is true. |
929
- | `Should Contain Match` | ${list} | ${pattern} | case_insensitive=${42} | # Numbers other than 0 are true. |
930
-
931
- False examples:
932
- | `Should Contain Match` | ${list} | ${pattern} | case_insensitive=False | # String ``false`` is false. |
933
- | `Should Contain Match` | ${list} | ${pattern} | case_insensitive=no | # Also string ``no`` is false. |
934
- | `Should Contain Match` | ${list} | ${pattern} | case_insensitive=${EMPTY} | # Empty string is false. |
935
- | `Should Contain Match` | ${list} | ${pattern} | case_insensitive=${FALSE} | # Python ``False`` is false. |
936
- | `Lists Should Be Equal` | ${x} | ${y} | Custom error | values=no values | # ``no values`` works with ``values`` argument |
937
-
938
- Considering ``OFF`` and ``0`` false is new in Robot Framework 3.1.
961
+ | `Should Contain Match` | ${list} | ${pattern} | ignore_case=True |
962
+ | `Should Contain Match` | ${list} | ${pattern} | ignore_case=False |
963
+ | `Lists Should Be Equal` | ${list1} | ${list2} | Custom error | no values |
939
964
 
940
965
  = Data in examples =
941
966
 
@@ -947,12 +972,15 @@ class Collections(_List, _Dictionary):
947
972
  Dictionary keywords use similar ``${Dx}`` variables. For example, ``${D1}``
948
973
  means ``{'a': 1}`` and ``${D3}`` means ``{'a': 1, 'b': 2, 'c': 3}``.
949
974
  """
975
+
950
976
  ROBOT_LIBRARY_SCOPE = 'GLOBAL'
951
977
  ROBOT_LIBRARY_VERSION = get_version()
952
978
 
953
979
  def should_contain_match(self, list, pattern, msg=None,
954
- case_insensitive=False,
955
- whitespace_insensitive=False):
980
+ case_insensitive: 'bool|None' = None,
981
+ whitespace_insensitive: 'bool|None' = None,
982
+ ignore_case: bool = False,
983
+ ignore_whitespace: bool = False):
956
984
  """Fails if ``pattern`` is not found in ``list``.
957
985
 
958
986
  By default, pattern matching is similar to matching files in a shell
@@ -964,72 +992,88 @@ class Collections(_List, _Dictionary):
964
992
  If you prepend ``regexp=`` to your pattern, your pattern will be used
965
993
  according to the Python
966
994
  [http://docs.python.org/library/re.html|re module] regular expression
967
- syntax. Important note: Backslashes are an escape character, and must
968
- be escaped with another backslash (e.g. ``regexp=\\\\d{6}`` to search for
969
- ``\\d{6}``). See `BuiltIn.Should Match Regexp` for more details.
995
+ syntax. Notice that the backslash character often used with regular
996
+ expressions is an escape character in Robot Framework data and needs
997
+ to be escaped with another backslash like ``regexp=\\\\d{6}``. See
998
+ `BuiltIn.Should Match Regexp` for more details.
970
999
 
971
- If ``case_insensitive`` is given a true value (see `Boolean arguments`),
972
- the pattern matching will ignore case.
1000
+ Matching is case-sensitive by default, but that can be changed by giving
1001
+ the ``ignore_case`` argument a true value (see `Boolean arguments`).
1002
+ This argument is new in Robot Framework 7.0, but with earlier versions
1003
+ it is possible to use ``case_insensitive`` for the same purpose.
973
1004
 
974
- If ``whitespace_insensitive`` is given a true value (see `Boolean
975
- arguments`), the pattern matching will ignore whitespace.
1005
+ It is possible to ignore all whitespace by giving the ``ignore_whitespace``
1006
+ argument a true value. This argument is new in Robot Framework 7.0 as well,
1007
+ and with earlier versions it is possible to use ``whitespace_insensitive``.
1008
+
1009
+ Notice that both ``case_insensitive`` and ``whitespace_insensitive``
1010
+ are considered deprecated. They will eventually be removed.
976
1011
 
977
1012
  Non-string values in lists are ignored when matching patterns.
978
1013
 
979
1014
  Use the ``msg`` argument to override the default error message.
980
1015
 
981
- See also ``Should Not Contain Match``.
982
-
983
1016
  Examples:
984
1017
  | Should Contain Match | ${list} | a* | | | # Match strings beginning with 'a'. |
985
1018
  | Should Contain Match | ${list} | regexp=a.* | | | # Same as the above but with regexp. |
986
1019
  | Should Contain Match | ${list} | regexp=\\\\d{6} | | | # Match strings containing six digits. |
987
- | Should Contain Match | ${list} | a* | case_insensitive=True | | # Match strings beginning with 'a' or 'A'. |
988
- | Should Contain Match | ${list} | ab* | whitespace_insensitive=yes | | # Match strings beginning with 'ab' with possible whitespace ignored. |
989
- | Should Contain Match | ${list} | ab* | whitespace_insensitive=true | case_insensitive=true | # Same as the above but also ignore case. |
1020
+ | Should Contain Match | ${list} | a* | ignore_case=True | | # Match strings beginning with 'a' or 'A'. |
1021
+ | Should Contain Match | ${list} | ab* | ignore_whitespace=yes | | # Match strings beginning with 'ab' with possible whitespace ignored. |
1022
+ | Should Contain Match | ${list} | ab* | ignore_whitespace=true | ignore_case=true | # Same as the above but also ignore case. |
990
1023
  """
991
1024
  _List._validate_list(self, list)
992
- matches = _get_matches_in_iterable(list, pattern, case_insensitive,
993
- whitespace_insensitive)
1025
+ matches = self._get_matches(list, pattern, case_insensitive,
1026
+ whitespace_insensitive, ignore_case,
1027
+ ignore_whitespace)
994
1028
  default = f"{seq2str2(list)} does not contain match for pattern '{pattern}'."
995
1029
  _verify_condition(matches, default, msg)
996
1030
 
997
1031
  def should_not_contain_match(self, list, pattern, msg=None,
998
- case_insensitive=False,
999
- whitespace_insensitive=False):
1032
+ case_insensitive: 'bool|None' = None,
1033
+ whitespace_insensitive: 'bool|None' = None,
1034
+ ignore_case: bool = False,
1035
+ ignore_whitespace: bool = False):
1000
1036
  """Fails if ``pattern`` is found in ``list``.
1001
1037
 
1002
1038
  Exact opposite of `Should Contain Match` keyword. See that keyword
1003
1039
  for information about arguments and usage in general.
1004
1040
  """
1005
1041
  _List._validate_list(self, list)
1006
- matches = _get_matches_in_iterable(list, pattern, case_insensitive,
1007
- whitespace_insensitive)
1042
+ matches = self._get_matches(list, pattern, case_insensitive,
1043
+ whitespace_insensitive, ignore_case,
1044
+ ignore_whitespace)
1008
1045
  default = f"{seq2str2(list)} contains match for pattern '{pattern}'."
1009
1046
  _verify_condition(not matches, default, msg)
1010
1047
 
1011
- def get_matches(self, list, pattern, case_insensitive=False,
1012
- whitespace_insensitive=False):
1048
+ def get_matches(self, list, pattern,
1049
+ case_insensitive: 'bool|None' = None,
1050
+ whitespace_insensitive: 'bool|None' = None,
1051
+ ignore_case: bool = False,
1052
+ ignore_whitespace: bool = False):
1013
1053
  """Returns a list of matches to ``pattern`` in ``list``.
1014
1054
 
1015
- For more information on ``pattern``, ``case_insensitive``, and
1016
- ``whitespace_insensitive``, see `Should Contain Match`.
1055
+ For more information on ``pattern``, ``case_insensitive/ignore_case``, and
1056
+ ``whitespace_insensitive/ignore_whitespace``, see `Should Contain Match`.
1017
1057
 
1018
1058
  Examples:
1019
1059
  | ${matches}= | Get Matches | ${list} | a* | # ${matches} will contain any string beginning with 'a' |
1020
1060
  | ${matches}= | Get Matches | ${list} | regexp=a.* | # ${matches} will contain any string beginning with 'a' (regexp version) |
1021
- | ${matches}= | Get Matches | ${list} | a* | case_insensitive=${True} | # ${matches} will contain any string beginning with 'a' or 'A' |
1061
+ | ${matches}= | Get Matches | ${list} | a* | ignore_case=True | # ${matches} will contain any string beginning with 'a' or 'A' |
1022
1062
  """
1023
1063
  _List._validate_list(self, list)
1024
- return _get_matches_in_iterable(list, pattern, case_insensitive,
1025
- whitespace_insensitive)
1026
-
1027
- def get_match_count(self, list, pattern, case_insensitive=False,
1028
- whitespace_insensitive=False):
1064
+ return self._get_matches(list, pattern, case_insensitive,
1065
+ whitespace_insensitive, ignore_case,
1066
+ ignore_whitespace)
1067
+
1068
+ def get_match_count(self, list, pattern,
1069
+ case_insensitive: 'bool|None' = None,
1070
+ whitespace_insensitive: 'bool|None' = None,
1071
+ ignore_case: bool = False,
1072
+ ignore_whitespace: bool = False):
1029
1073
  """Returns the count of matches to ``pattern`` in ``list``.
1030
1074
 
1031
- For more information on ``pattern``, ``case_insensitive``, and
1032
- ``whitespace_insensitive``, see `Should Contain Match`.
1075
+ For more information on ``pattern``, ``case_insensitive/ignore_case``, and
1076
+ ``whitespace_insensitive/ignore_whitespace``, see `Should Contain Match`.
1033
1077
 
1034
1078
  Examples:
1035
1079
  | ${count}= | Get Match Count | ${list} | a* | # ${count} will be the count of strings beginning with 'a' |
@@ -1038,31 +1082,131 @@ class Collections(_List, _Dictionary):
1038
1082
  """
1039
1083
  _List._validate_list(self, list)
1040
1084
  return len(self.get_matches(list, pattern, case_insensitive,
1041
- whitespace_insensitive))
1042
-
1043
-
1044
- def _verify_condition(condition, default_msg, msg, values=False):
1045
- if condition:
1046
- return
1047
- if not msg:
1048
- msg = default_msg
1049
- elif is_truthy(values) and str(values).upper() != 'NO VALUES':
1050
- msg += '\n' + default_msg
1051
- raise AssertionError(msg)
1052
-
1053
-
1054
- def _get_matches_in_iterable(iterable, pattern, case_insensitive=False,
1055
- whitespace_insensitive=False):
1056
- if not isinstance(pattern, str):
1057
- raise TypeError(f"Pattern must be string, got '{type_name(pattern)}'.")
1058
- regexp = False
1059
- if pattern.startswith('regexp='):
1060
- pattern = pattern[7:]
1061
- regexp = True
1062
- elif pattern.startswith('glob='):
1063
- pattern = pattern[5:]
1064
- matcher = Matcher(pattern,
1065
- caseless=is_truthy(case_insensitive),
1066
- spaceless=is_truthy(whitespace_insensitive),
1067
- regexp=regexp)
1068
- return [item for item in iterable if isinstance(item, str) and matcher.match(item)]
1085
+ whitespace_insensitive, ignore_case,
1086
+ ignore_whitespace))
1087
+
1088
+ def _get_matches(self, iterable, pattern, case_insensitive=None,
1089
+ whitespace_insensitive=None, ignore_case=True,
1090
+ ignore_whitespace=False):
1091
+ # `ignore_xxx` were added in RF 7.0 for consistency reasons.
1092
+ # The idea is that they eventually replace `xxx_insensitive`.
1093
+ # TODO: Emit deprecation warnings in RF 8.0.
1094
+ if case_insensitive is not None:
1095
+ ignore_case = case_insensitive
1096
+ if whitespace_insensitive is not None:
1097
+ ignore_whitespace = whitespace_insensitive
1098
+ if not isinstance(pattern, str):
1099
+ raise TypeError(f"Pattern must be string, got '{type_name(pattern)}'.")
1100
+ regexp = False
1101
+ if pattern.startswith('regexp='):
1102
+ pattern = pattern[7:]
1103
+ regexp = True
1104
+ elif pattern.startswith('glob='):
1105
+ pattern = pattern[5:]
1106
+ matcher = Matcher(pattern, caseless=ignore_case, spaceless=ignore_whitespace,
1107
+ regexp=regexp)
1108
+ return [item for item in iterable if isinstance(item, str) and matcher.match(item)]
1109
+
1110
+
1111
+ def _verify_condition(condition, default_message, message, values=False):
1112
+ if not condition:
1113
+ _report_error(default_message, message, values)
1114
+
1115
+
1116
+ def _report_error(default_message, message, values=False):
1117
+ if not message:
1118
+ message = default_message
1119
+ elif values and not (isinstance(values, str) and values.upper() == 'NO VALUES'):
1120
+ message += '\n' + default_message
1121
+ raise AssertionError(message)
1122
+
1123
+
1124
+ class Normalizer:
1125
+
1126
+ def __init__(self, ignore_case=False, ignore_order=False, ignore_keys=None):
1127
+ print(ignore_case)
1128
+ self.ignore_case = ignore_case
1129
+ if isinstance(ignore_case, str):
1130
+ self.ignore_key_case = ignore_case.upper() not in ('VALUE', 'VALUES')
1131
+ self.ignore_value_case = ignore_case.upper() not in ('KEY', 'KEYS')
1132
+ else:
1133
+ self.ignore_key_case = self.ignore_value_case = self.ignore_case
1134
+ self.ignore_order = ignore_order
1135
+ self.ignore_keys = self._parse_ignored_keys(ignore_keys)
1136
+
1137
+ def _parse_ignored_keys(self, ignore_keys):
1138
+ if not ignore_keys:
1139
+ return set()
1140
+ try:
1141
+ if isinstance(ignore_keys, str):
1142
+ ignore_keys = literal_eval(ignore_keys)
1143
+ if not is_list_like(ignore_keys):
1144
+ raise ValueError
1145
+ except Exception:
1146
+ raise ValueError(f"'ignore_keys' value '{ignore_keys}' cannot be "
1147
+ f"converted to a list.")
1148
+ return {self.normalize_key(k) for k in ignore_keys}
1149
+
1150
+ def normalize(self, value):
1151
+ if not self:
1152
+ return value
1153
+ if isinstance(value, str):
1154
+ return self.normalize_string(value)
1155
+ if is_dict_like(value):
1156
+ return self.normalize_dict(value)
1157
+ if is_list_like(value):
1158
+ return self.normalize_list(value)
1159
+ return value
1160
+
1161
+ def normalize_string(self, value):
1162
+ return value.casefold() if self.ignore_case else value
1163
+
1164
+ def normalize_list(self, value):
1165
+ cls = type(value)
1166
+ if self.ignore_order:
1167
+ value = sorted(value)
1168
+ value = [self.normalize(v) for v in value]
1169
+ return self._try_to_preserve_type(value, cls)
1170
+
1171
+ def _try_to_preserve_type(self, value, cls):
1172
+ # Try to preserve original type. Most importantly, preserve tuples to
1173
+ # allow using them as dictionary keys.
1174
+ try:
1175
+ return cls(value)
1176
+ except TypeError:
1177
+ return value
1178
+
1179
+ def normalize_dict(self, value):
1180
+ cls = type(value)
1181
+ result = {}
1182
+ for key in value:
1183
+ normalized = self.normalize_key(key)
1184
+ if normalized in self.ignore_keys:
1185
+ continue
1186
+ if normalized in result:
1187
+ raise AssertionError(
1188
+ f"Dictionary {value} contains multiple keys that are normalized "
1189
+ f"to '{normalized}'. Try normalizing only dictionary values like "
1190
+ f"'ignore_case=values'."
1191
+ )
1192
+ result[normalized] = self.normalize_value(value[key])
1193
+ return self._try_to_preserve_type(result, cls)
1194
+
1195
+ def normalize_key(self, key):
1196
+ ignore_case, self.ignore_case = self.ignore_case, self.ignore_key_case
1197
+ try:
1198
+ return self.normalize(key)
1199
+ finally:
1200
+ self.ignore_case = ignore_case
1201
+
1202
+ def normalize_value(self, value):
1203
+ ignore_case, self.ignore_case = self.ignore_case, self.ignore_value_case
1204
+ try:
1205
+ return self.normalize(value)
1206
+ finally:
1207
+ self.ignore_case = ignore_case
1208
+
1209
+ def __bool__(self):
1210
+ return bool(self.ignore_case
1211
+ or self.ignore_order
1212
+ or getattr(self, 'ignore_keys', False))