Best Python code snippet using robotframework
BuiltIn.py
Source:BuiltIn.py
...39if JYTHON:40 from java.lang import String, Number41# TODO: Clean-up registering run keyword variants in RF 3.1.42# https://github.com/robotframework/robotframework/issues/219043def run_keyword_variant(resolve):44 def decorator(method):45 RUN_KW_REGISTER.register_run_keyword('BuiltIn', method.__name__,46 resolve, deprecation_warning=False)47 return method48 return decorator49class _BuiltInBase(object):50 @property51 def _context(self):52 return self._get_context()53 def _get_context(self, top=False):54 ctx = EXECUTION_CONTEXTS.current if not top else EXECUTION_CONTEXTS.top55 if ctx is None:56 raise RobotNotRunningError('Cannot access execution context')57 return ctx58 @property59 def _namespace(self):60 return self._get_context().namespace61 @property62 def _variables(self):63 return self._namespace.variables64 def _matches(self, string, pattern):65 # Must use this instead of fnmatch when string may contain newlines.66 matcher = Matcher(pattern, caseless=False, spaceless=False)67 return matcher.match(string)68 def _is_true(self, condition):69 if is_string(condition):70 condition = self.evaluate(condition, modules='os,sys')71 return bool(condition)72 def _log_types(self, *args):73 self._log_types_at_level('DEBUG', *args)74 def _log_types_at_level(self, level, *args):75 msg = ["Argument types are:"] + [self._get_type(a) for a in args]76 self.log('\n'.join(msg), level)77 def _get_type(self, arg):78 # In IronPython type(u'x') is str. We want to report unicode anyway.79 if is_unicode(arg):80 return "<type 'unicode'>"81 return str(type(arg))82class _Converter(_BuiltInBase):83 def convert_to_integer(self, item, base=None):84 """Converts the given item to an integer number.85 If the given item is a string, it is by default expected to be an86 integer in base 10. There are two ways to convert from other bases:87 - Give base explicitly to the keyword as ``base`` argument.88 - Prefix the given string with the base so that ``0b`` means binary89 (base 2), ``0o`` means octal (base 8), and ``0x`` means hex (base 16).90 The prefix is considered only when ``base`` argument is not given and91 may itself be prefixed with a plus or minus sign.92 The syntax is case-insensitive and possible spaces are ignored.93 Examples:94 | ${result} = | Convert To Integer | 100 | | # Result is 100 |95 | ${result} = | Convert To Integer | FF AA | 16 | # Result is 65450 |96 | ${result} = | Convert To Integer | 100 | 8 | # Result is 64 |97 | ${result} = | Convert To Integer | -100 | 2 | # Result is -4 |98 | ${result} = | Convert To Integer | 0b100 | | # Result is 4 |99 | ${result} = | Convert To Integer | -0x100 | | # Result is -256 |100 See also `Convert To Number`, `Convert To Binary`, `Convert To Octal`,101 `Convert To Hex`, and `Convert To Bytes`.102 """103 self._log_types(item)104 return self._convert_to_integer(item, base)105 def _convert_to_integer(self, orig, base=None):106 try:107 item = self._handle_java_numbers(orig)108 item, base = self._get_base(item, base)109 if base:110 return int(item, self._convert_to_integer(base))111 return int(item)112 except:113 raise RuntimeError("'%s' cannot be converted to an integer: %s"114 % (orig, get_error_message()))115 def _handle_java_numbers(self, item):116 if not JYTHON:117 return item118 if isinstance(item, String):119 return unic(item)120 if isinstance(item, Number):121 return item.doubleValue()122 return item123 def _get_base(self, item, base):124 if not is_string(item):125 return item, base126 item = normalize(item)127 if item.startswith(('-', '+')):128 sign = item[0]129 item = item[1:]130 else:131 sign = ''132 bases = {'0b': 2, '0o': 8, '0x': 16}133 if base or not item.startswith(tuple(bases)):134 return sign+item, base135 return sign+item[2:], bases[item[:2]]136 def convert_to_binary(self, item, base=None, prefix=None, length=None):137 """Converts the given item to a binary string.138 The ``item``, with an optional ``base``, is first converted to an139 integer using `Convert To Integer` internally. After that it140 is converted to a binary number (base 2) represented as a141 string such as ``1011``.142 The returned value can contain an optional ``prefix`` and can be143 required to be of minimum ``length`` (excluding the prefix and a144 possible minus sign). If the value is initially shorter than145 the required length, it is padded with zeros.146 Examples:147 | ${result} = | Convert To Binary | 10 | | | # Result is 1010 |148 | ${result} = | Convert To Binary | F | base=16 | prefix=0b | # Result is 0b1111 |149 | ${result} = | Convert To Binary | -2 | prefix=B | length=4 | # Result is -B0010 |150 See also `Convert To Integer`, `Convert To Octal` and `Convert To Hex`.151 """152 return self._convert_to_bin_oct_hex(item, base, prefix, length, 'b')153 def convert_to_octal(self, item, base=None, prefix=None, length=None):154 """Converts the given item to an octal string.155 The ``item``, with an optional ``base``, is first converted to an156 integer using `Convert To Integer` internally. After that it157 is converted to an octal number (base 8) represented as a158 string such as ``775``.159 The returned value can contain an optional ``prefix`` and can be160 required to be of minimum ``length`` (excluding the prefix and a161 possible minus sign). If the value is initially shorter than162 the required length, it is padded with zeros.163 Examples:164 | ${result} = | Convert To Octal | 10 | | | # Result is 12 |165 | ${result} = | Convert To Octal | -F | base=16 | prefix=0 | # Result is -017 |166 | ${result} = | Convert To Octal | 16 | prefix=oct | length=4 | # Result is oct0020 |167 See also `Convert To Integer`, `Convert To Binary` and `Convert To Hex`.168 """169 return self._convert_to_bin_oct_hex(item, base, prefix, length, 'o')170 def convert_to_hex(self, item, base=None, prefix=None, length=None,171 lowercase=False):172 """Converts the given item to a hexadecimal string.173 The ``item``, with an optional ``base``, is first converted to an174 integer using `Convert To Integer` internally. After that it175 is converted to a hexadecimal number (base 16) represented as176 a string such as ``FF0A``.177 The returned value can contain an optional ``prefix`` and can be178 required to be of minimum ``length`` (excluding the prefix and a179 possible minus sign). If the value is initially shorter than180 the required length, it is padded with zeros.181 By default the value is returned as an upper case string, but the182 ``lowercase`` argument a true value (see `Boolean arguments`) turns183 the value (but not the given prefix) to lower case.184 Examples:185 | ${result} = | Convert To Hex | 255 | | | # Result is FF |186 | ${result} = | Convert To Hex | -10 | prefix=0x | length=2 | # Result is -0x0A |187 | ${result} = | Convert To Hex | 255 | prefix=X | lowercase=yes | # Result is Xff |188 See also `Convert To Integer`, `Convert To Binary` and `Convert To Octal`.189 """190 spec = 'x' if is_truthy(lowercase) else 'X'191 return self._convert_to_bin_oct_hex(item, base, prefix, length, spec)192 def _convert_to_bin_oct_hex(self, item, base, prefix, length, format_spec):193 self._log_types(item)194 ret = format(self._convert_to_integer(item, base), format_spec)195 prefix = prefix or ''196 if ret[0] == '-':197 prefix = '-' + prefix198 ret = ret[1:]199 if length:200 ret = ret.rjust(self._convert_to_integer(length), '0')201 return prefix + ret202 def convert_to_number(self, item, precision=None):203 """Converts the given item to a floating point number.204 If the optional ``precision`` is positive or zero, the returned number205 is rounded to that number of decimal digits. Negative precision means206 that the number is rounded to the closest multiple of 10 to the power207 of the absolute precision. If a number is equally close to a certain208 precision, it is always rounded away from zero.209 Examples:210 | ${result} = | Convert To Number | 42.512 | | # Result is 42.512 |211 | ${result} = | Convert To Number | 42.512 | 1 | # Result is 42.5 |212 | ${result} = | Convert To Number | 42.512 | 0 | # Result is 43.0 |213 | ${result} = | Convert To Number | 42.512 | -1 | # Result is 40.0 |214 Notice that machines generally cannot store floating point numbers215 accurately. This may cause surprises with these numbers in general216 and also when they are rounded. For more information see, for example,217 these resources:218 - http://docs.python.org/2/tutorial/floatingpoint.html219 - http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition220 If you need an integer number, use `Convert To Integer` instead.221 """222 self._log_types(item)223 return self._convert_to_number(item, precision)224 def _convert_to_number(self, item, precision=None):225 number = self._convert_to_number_without_precision(item)226 if precision is not None:227 number = roundup(number, self._convert_to_integer(precision),228 return_type=float)229 return number230 def _convert_to_number_without_precision(self, item):231 try:232 if JYTHON:233 item = self._handle_java_numbers(item)234 return float(item)235 except:236 error = get_error_message()237 try:238 return float(self._convert_to_integer(item))239 except RuntimeError:240 raise RuntimeError("'%s' cannot be converted to a floating "241 "point number: %s" % (item, error))242 def convert_to_string(self, item):243 """Converts the given item to a Unicode string.244 Uses ``__unicode__`` or ``__str__`` method with Python objects and245 ``toString`` with Java objects.246 Use `Encode String To Bytes` and `Decode Bytes To String` keywords247 in ``String`` library if you need to convert between Unicode and byte248 strings using different encodings. Use `Convert To Bytes` if you just249 want to create byte strings.250 """251 self._log_types(item)252 return self._convert_to_string(item)253 def _convert_to_string(self, item):254 return unic(item)255 def convert_to_boolean(self, item):256 """Converts the given item to Boolean true or false.257 Handles strings ``True`` and ``False`` (case-insensitive) as expected,258 otherwise returns item's259 [http://docs.python.org/2/library/stdtypes.html#truth|truth value]260 using Python's ``bool()`` method.261 """262 self._log_types(item)263 if is_string(item):264 if item.upper() == 'TRUE':265 return True266 if item.upper() == 'FALSE':267 return False268 return bool(item)269 def convert_to_bytes(self, input, input_type='text'):270 u"""Converts the given ``input`` to bytes according to the ``input_type``.271 Valid input types are listed below:272 - ``text:`` Converts text to bytes character by character. All273 characters with ordinal below 256 can be used and are converted to274 bytes with same values. Many characters are easiest to represent275 using escapes like ``\\x00`` or ``\\xff``. Supports both Unicode276 strings and bytes.277 - ``int:`` Converts integers separated by spaces to bytes. Similarly as278 with `Convert To Integer`, it is possible to use binary, octal, or279 hex values by prefixing the values with ``0b``, ``0o``, or ``0x``,280 respectively.281 - ``hex:`` Converts hexadecimal values to bytes. Single byte is always282 two characters long (e.g. ``01`` or ``FF``). Spaces are ignored and283 can be used freely as a visual separator.284 - ``bin:`` Converts binary values to bytes. Single byte is always eight285 characters long (e.g. ``00001010``). Spaces are ignored and can be286 used freely as a visual separator.287 In addition to giving the input as a string, it is possible to use288 lists or other iterables containing individual characters or numbers.289 In that case numbers do not need to be padded to certain length and290 they cannot contain extra spaces.291 Examples (last column shows returned bytes):292 | ${bytes} = | Convert To Bytes | hyv\xe4 | | # hyv\\xe4 |293 | ${bytes} = | Convert To Bytes | \\xff\\x07 | | # \\xff\\x07 |294 | ${bytes} = | Convert To Bytes | 82 70 | int | # RF |295 | ${bytes} = | Convert To Bytes | 0b10 0x10 | int | # \\x02\\x10 |296 | ${bytes} = | Convert To Bytes | ff 00 07 | hex | # \\xff\\x00\\x07 |297 | ${bytes} = | Convert To Bytes | 5246212121 | hex | # RF!!! |298 | ${bytes} = | Convert To Bytes | 0000 1000 | bin | # \\x08 |299 | ${input} = | Create List | 1 | 2 | 12 |300 | ${bytes} = | Convert To Bytes | ${input} | int | # \\x01\\x02\\x0c |301 | ${bytes} = | Convert To Bytes | ${input} | hex | # \\x01\\x02\\x12 |302 Use `Encode String To Bytes` in ``String`` library if you need to303 convert text to bytes using a certain encoding.304 New in Robot Framework 2.8.2.305 """306 try:307 try:308 ordinals = getattr(self, '_get_ordinals_from_%s' % input_type)309 except AttributeError:310 raise RuntimeError("Invalid input type '%s'." % input_type)311 return bytes(bytearray(o for o in ordinals(input)))312 except:313 raise RuntimeError("Creating bytes failed: %s" % get_error_message())314 def _get_ordinals_from_text(self, input):315 # https://github.com/IronLanguages/main/issues/1237316 if IRONPYTHON and isinstance(input, bytearray):317 input = bytes(input)318 for char in input:319 ordinal = char if is_integer(char) else ord(char)320 yield self._test_ordinal(ordinal, char, 'Character')321 def _test_ordinal(self, ordinal, original, type):322 if 0 <= ordinal <= 255:323 return ordinal324 raise RuntimeError("%s '%s' cannot be represented as a byte."325 % (type, original))326 def _get_ordinals_from_int(self, input):327 if is_string(input):328 input = input.split()329 elif is_integer(input):330 input = [input]331 for integer in input:332 ordinal = self._convert_to_integer(integer)333 yield self._test_ordinal(ordinal, integer, 'Integer')334 def _get_ordinals_from_hex(self, input):335 for token in self._input_to_tokens(input, length=2):336 ordinal = self._convert_to_integer(token, base=16)337 yield self._test_ordinal(ordinal, token, 'Hex value')338 def _get_ordinals_from_bin(self, input):339 for token in self._input_to_tokens(input, length=8):340 ordinal = self._convert_to_integer(token, base=2)341 yield self._test_ordinal(ordinal, token, 'Binary value')342 def _input_to_tokens(self, input, length):343 if not is_string(input):344 return input345 input = ''.join(input.split())346 if len(input) % length != 0:347 raise RuntimeError('Expected input to be multiple of %d.' % length)348 return (input[i:i+length] for i in range(0, len(input), length))349 def create_list(self, *items):350 """Returns a list containing given items.351 The returned list can be assigned both to ``${scalar}`` and ``@{list}``352 variables.353 Examples:354 | @{list} = | Create List | a | b | c |355 | ${scalar} = | Create List | a | b | c |356 | ${ints} = | Create List | ${1} | ${2} | ${3} |357 """358 return list(items)359 @run_keyword_variant(resolve=0)360 def create_dictionary(self, *items):361 """Creates and returns a dictionary based on the given ``items``.362 Items are typically given using the ``key=value`` syntax same way as363 ``&{dictionary}`` variables are created in the Variable table. Both364 keys and values can contain variables, and possible equal sign in key365 can be escaped with a backslash like ``escaped\\=key=value``. It is366 also possible to get items from existing dictionaries by simply using367 them like ``&{dict}``.368 Alternatively items can be specified so that keys and values are given369 separately. This and the ``key=value`` syntax can even be combined,370 but separately given items must be first.371 If same key is used multiple times, the last value has precedence.372 The returned dictionary is ordered, and values with strings as keys373 can also be accessed using a convenient dot-access syntax like374 ``${dict.key}``.375 Examples:376 | &{dict} = | Create Dictionary | key=value | foo=bar | | | # key=value syntax |377 | Should Be True | ${dict} == {'key': 'value', 'foo': 'bar'} |378 | &{dict2} = | Create Dictionary | key | value | foo | bar | # separate key and value |379 | Should Be Equal | ${dict} | ${dict2} |380 | &{dict} = | Create Dictionary | ${1}=${2} | &{dict} | foo=new | | # using variables |381 | Should Be True | ${dict} == {1: 2, 'key': 'value', 'foo': 'new'} |382 | Should Be Equal | ${dict.key} | value | | | | # dot-access |383 This keyword was changed in Robot Framework 2.9 in many ways:384 - Moved from ``Collections`` library to ``BuiltIn``.385 - Support also non-string keys in ``key=value`` syntax.386 - Returned dictionary is ordered and dot-accessible.387 - Old syntax to give keys and values separately was deprecated, but388 deprecation was later removed in RF 3.0.1.389 """390 separate, combined = self._split_dict_items(items)391 result = DotDict(self._format_separate_dict_items(separate))392 combined = DictVariableTableValue(combined).resolve(self._variables)393 result.update(combined)394 return result395 def _split_dict_items(self, items):396 separate = []397 for item in items:398 name, value = split_from_equals(item)399 if value is not None or VariableSplitter(item).is_dict_variable():400 break401 separate.append(item)402 return separate, items[len(separate):]403 def _format_separate_dict_items(self, separate):404 separate = self._variables.replace_list(separate)405 if len(separate) % 2 != 0:406 raise DataError('Expected even number of keys and values, got %d.'407 % len(separate))408 return [separate[i:i+2] for i in range(0, len(separate), 2)]409class _Verify(_BuiltInBase):410 def _set_and_remove_tags(self, tags):411 set_tags = [tag for tag in tags if not tag.startswith('-')]412 remove_tags = [tag[1:] for tag in tags if tag.startswith('-')]413 if remove_tags:414 self.remove_tags(*remove_tags)415 if set_tags:416 self.set_tags(*set_tags)417 def fail(self, msg=None, *tags):418 """Fails the test with the given message and optionally alters its tags.419 The error message is specified using the ``msg`` argument.420 It is possible to use HTML in the given error message, similarly421 as with any other keyword accepting an error message, by prefixing422 the error with ``*HTML*``.423 It is possible to modify tags of the current test case by passing tags424 after the message. Tags starting with a hyphen (e.g. ``-regression``)425 are removed and others added. Tags are modified using `Set Tags` and426 `Remove Tags` internally, and the semantics setting and removing them427 are the same as with these keywords.428 Examples:429 | Fail | Test not ready | | | # Fails with the given message. |430 | Fail | *HTML*<b>Test not ready</b> | | | # Fails using HTML in the message. |431 | Fail | Test not ready | not-ready | | # Fails and adds 'not-ready' tag. |432 | Fail | OS not supported | -regression | | # Removes tag 'regression'. |433 | Fail | My message | tag | -t* | # Removes all tags starting with 't' except the newly added 'tag'. |434 See `Fatal Error` if you need to stop the whole test execution.435 Support for modifying tags was added in Robot Framework 2.7.4 and436 HTML message support in 2.8.437 """438 self._set_and_remove_tags(tags)439 raise AssertionError(msg) if msg else AssertionError()440 def fatal_error(self, msg=None):441 """Stops the whole test execution.442 The test or suite where this keyword is used fails with the provided443 message, and subsequent tests fail with a canned message.444 Possible teardowns will nevertheless be executed.445 See `Fail` if you only want to stop one test case unconditionally.446 """447 error = AssertionError(msg) if msg else AssertionError()448 error.ROBOT_EXIT_ON_FAILURE = True449 raise error450 def should_not_be_true(self, condition, msg=None):451 """Fails if the given condition is true.452 See `Should Be True` for details about how ``condition`` is evaluated453 and how ``msg`` can be used to override the default error message.454 """455 if self._is_true(condition):456 raise AssertionError(msg or "'%s' should not be true." % condition)457 def should_be_true(self, condition, msg=None):458 """Fails if the given condition is not true.459 If ``condition`` is a string (e.g. ``${rc} < 10``), it is evaluated as460 a Python expression as explained in `Evaluating expressions` and the461 keyword status is decided based on the result. If a non-string item is462 given, the status is got directly from its463 [http://docs.python.org/2/library/stdtypes.html#truth|truth value].464 The default error message (``<condition> should be true``) is not very465 informative, but it can be overridden with the ``msg`` argument.466 Examples:467 | Should Be True | ${rc} < 10 |468 | Should Be True | '${status}' == 'PASS' | # Strings must be quoted |469 | Should Be True | ${number} | # Passes if ${number} is not zero |470 | Should Be True | ${list} | # Passes if ${list} is not empty |471 Variables used like ``${variable}``, as in the examples above, are472 replaced in the expression before evaluation. Variables are also473 available in the evaluation namespace and can be accessed using special474 syntax ``$variable``. This is a new feature in Robot Framework 2.9475 and it is explained more thoroughly in `Evaluating expressions`.476 Examples:477 | Should Be True | $rc < 10 |478 | Should Be True | $status == 'PASS' | # Expected string must be quoted |479 Starting from Robot Framework 2.8, `Should Be True` automatically480 imports Python's [http://docs.python.org/2/library/os.html|os] and481 [http://docs.python.org/2/library/sys.html|sys] modules that contain482 several useful attributes:483 | Should Be True | os.linesep == '\\n' | # Unixy |484 | Should Be True | os.linesep == '\\r\\n' | # Windows |485 | Should Be True | sys.platform == 'darwin' | # OS X |486 | Should Be True | sys.platform.startswith('java') | # Jython |487 """488 if not self._is_true(condition):489 raise AssertionError(msg or "'%s' should be true." % condition)490 def should_be_equal(self, first, second, msg=None, values=True,491 ignore_case=False):492 """Fails if the given objects are unequal.493 Optional ``msg`` and ``values`` arguments specify how to construct494 the error message if this keyword fails:495 - If ``msg`` is not given, the error message is ``<first> != <second>``.496 - If ``msg`` is given and ``values`` gets a true value (default),497 the error message is ``<msg>: <first> != <second>``.498 - If ``msg`` is given and ``values`` gets a false value, the error499 message is simply ``<msg>``. See `Boolean arguments` for more details500 about using false values.501 If ``ignore_case`` is given a true value (see `Boolean arguments`) and502 arguments are strings, it indicates that comparison should be503 case-insensitive. New option in Robot Framework 3.0.1.504 If both arguments are multiline strings, the comparison is done using505 `multiline string comparisons`.506 Examples:507 | Should Be Equal | ${x} | expected |508 | Should Be Equal | ${x} | expected | Custom error message |509 | Should Be Equal | ${x} | expected | Custom message | values=False |510 | Should Be Equal | ${x} | expected | ignore_case=True |511 """512 self._log_types_at_info_if_different(first, second)513 if is_truthy(ignore_case) and is_string(first) and is_string(second):514 first = first.lower()515 second = second.lower()516 self._should_be_equal(first, second, msg, values)517 def _should_be_equal(self, first, second, msg, values):518 if first == second:519 return520 include_values = self._include_values(values)521 if include_values and is_string(first) and is_string(second):522 self._raise_multi_diff(first, second)523 assert_equal(first, second, msg, include_values)524 def _log_types_at_info_if_different(self, first, second):525 level = 'DEBUG' if type(first) == type(second) else 'INFO'526 self._log_types_at_level(level, first, second)527 def _raise_multi_diff(self, first, second):528 first_lines, second_lines = first.splitlines(), second.splitlines()529 if len(first_lines) < 3 or len(second_lines) < 3:530 return531 self.log("%s\n!=\n%s" % (first, second))532 err = 'Multiline strings are different:\n'533 for line in difflib.unified_diff(first_lines, second_lines,534 fromfile='first', tofile='second',535 lineterm=''):536 err += line + '\n'537 raise AssertionError(err)538 def _include_values(self, values):539 return is_truthy(values) and str(values).upper() != 'NO VALUES'540 def should_not_be_equal(self, first, second, msg=None, values=True,541 ignore_case=False):542 """Fails if the given objects are equal.543 See `Should Be Equal` for an explanation on how to override the default544 error message with ``msg`` and ``values``.545 If ``ignore_case`` is given a true value (see `Boolean arguments`) and546 both arguments are strings, it indicates that comparison should be547 case-insensitive. New option in Robot Framework 3.0.1.548 """549 self._log_types_at_info_if_different(first, second)550 if is_truthy(ignore_case) and is_string(first) and is_string(second):551 first = first.lower()552 second = second.lower()553 self._should_not_be_equal(first, second, msg, values)554 def _should_not_be_equal(self, first, second, msg, values):555 assert_not_equal(first, second, msg, self._include_values(values))556 def should_not_be_equal_as_integers(self, first, second, msg=None,557 values=True, base=None):558 """Fails if objects are equal after converting them to integers.559 See `Convert To Integer` for information how to convert integers from560 other bases than 10 using ``base`` argument or ``0b/0o/0x`` prefixes.561 See `Should Be Equal` for an explanation on how to override the default562 error message with ``msg`` and ``values``.563 See `Should Be Equal As Integers` for some usage examples.564 """565 self._log_types_at_info_if_different(first, second)566 self._should_not_be_equal(self._convert_to_integer(first, base),567 self._convert_to_integer(second, base),568 msg, values)569 def should_be_equal_as_integers(self, first, second, msg=None, values=True,570 base=None):571 """Fails if objects are unequal after converting them to integers.572 See `Convert To Integer` for information how to convert integers from573 other bases than 10 using ``base`` argument or ``0b/0o/0x`` prefixes.574 See `Should Be Equal` for an explanation on how to override the default575 error message with ``msg`` and ``values``.576 Examples:577 | Should Be Equal As Integers | 42 | ${42} | Error message |578 | Should Be Equal As Integers | ABCD | abcd | base=16 |579 | Should Be Equal As Integers | 0b1011 | 11 |580 """581 self._log_types_at_info_if_different(first, second)582 self._should_be_equal(self._convert_to_integer(first, base),583 self._convert_to_integer(second, base),584 msg, values)585 def should_not_be_equal_as_numbers(self, first, second, msg=None,586 values=True, precision=6):587 """Fails if objects are equal after converting them to real numbers.588 The conversion is done with `Convert To Number` keyword using the589 given ``precision``.590 See `Should Be Equal As Numbers` for examples on how to use591 ``precision`` and why it does not always work as expected. See also592 `Should Be Equal` for an explanation on how to override the default593 error message with ``msg`` and ``values``.594 """595 self._log_types_at_info_if_different(first, second)596 first = self._convert_to_number(first, precision)597 second = self._convert_to_number(second, precision)598 self._should_not_be_equal(first, second, msg, values)599 def should_be_equal_as_numbers(self, first, second, msg=None, values=True,600 precision=6):601 """Fails if objects are unequal after converting them to real numbers.602 The conversion is done with `Convert To Number` keyword using the603 given ``precision``.604 Examples:605 | Should Be Equal As Numbers | ${x} | 1.1 | | # Passes if ${x} is 1.1 |606 | Should Be Equal As Numbers | 1.123 | 1.1 | precision=1 | # Passes |607 | Should Be Equal As Numbers | 1.123 | 1.4 | precision=0 | # Passes |608 | Should Be Equal As Numbers | 112.3 | 75 | precision=-2 | # Passes |609 As discussed in the documentation of `Convert To Number`, machines610 generally cannot store floating point numbers accurately. Because of611 this limitation, comparing floats for equality is problematic and612 a correct approach to use depends on the context. This keyword uses613 a very naive approach of rounding the numbers before comparing them,614 which is both prone to rounding errors and does not work very well if615 numbers are really big or small. For more information about comparing616 floats, and ideas on how to implement your own context specific617 comparison algorithm, see618 http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/.619 See `Should Not Be Equal As Numbers` for a negative version of this620 keyword and `Should Be Equal` for an explanation on how to override621 the default error message with ``msg`` and ``values``.622 """623 self._log_types_at_info_if_different(first, second)624 first = self._convert_to_number(first, precision)625 second = self._convert_to_number(second, precision)626 self._should_be_equal(first, second, msg, values)627 def should_not_be_equal_as_strings(self, first, second, msg=None,628 values=True, ignore_case=False):629 """Fails if objects are equal after converting them to strings.630 If ``ignore_case`` is given a true value (see `Boolean arguments`), it631 indicates that comparison should be case-insensitive. New option in632 Robot Framework 3.0.1.633 See `Should Be Equal` for an explanation on how to override the default634 error message with ``msg`` and ``values``.635 """636 self._log_types_at_info_if_different(first, second)637 first = self._convert_to_string(first)638 second = self._convert_to_string(second)639 if is_truthy(ignore_case):640 first = first.lower()641 second = second.lower()642 self._should_not_be_equal(first, second, msg, values)643 def should_be_equal_as_strings(self, first, second, msg=None, values=True,644 ignore_case=False):645 """Fails if objects are unequal after converting them to strings.646 See `Should Be Equal` for an explanation on how to override the default647 error message with ``msg`` and ``values``.648 If ``ignore_case`` is given a true value (see `Boolean arguments`), it649 indicates that comparison should be case-insensitive. New option in650 Robot Framework 3.0.1.651 If both arguments are multiline strings, the comparison is done using652 `multiline string comparisons`.653 """654 self._log_types_at_info_if_different(first, second)655 first = self._convert_to_string(first)656 second = self._convert_to_string(second)657 if is_truthy(ignore_case):658 first = first.lower()659 second = second.lower()660 self._should_be_equal(first, second, msg, values)661 def should_not_start_with(self, str1, str2, msg=None, values=True,662 ignore_case=False):663 """Fails if the string ``str1`` starts with the string ``str2``.664 See `Should Be Equal` for an explanation on how to override the default665 error message with ``msg`` and ``values``, as well as for semantics666 of the ``ignore_case`` option.667 """668 if is_truthy(ignore_case):669 str1 = str1.lower()670 str2 = str2.lower()671 if str1.startswith(str2):672 raise AssertionError(self._get_string_msg(str1, str2, msg, values,673 'starts with'))674 def should_start_with(self, str1, str2, msg=None, values=True,675 ignore_case=False):676 """Fails if the string ``str1`` does not start with the string ``str2``.677 See `Should Be Equal` for an explanation on how to override the default678 error message with ``msg`` and ``values``, as well as for semantics679 of the ``ignore_case`` option.680 """681 if is_truthy(ignore_case):682 str1 = str1.lower()683 str2 = str2.lower()684 if not str1.startswith(str2):685 raise AssertionError(self._get_string_msg(str1, str2, msg, values,686 'does not start with'))687 def should_not_end_with(self, str1, str2, msg=None, values=True,688 ignore_case=False):689 """Fails if the string ``str1`` ends with the string ``str2``.690 See `Should Be Equal` for an explanation on how to override the default691 error message with ``msg`` and ``values``, as well as for semantics692 of the ``ignore_case`` option.693 """694 if is_truthy(ignore_case):695 str1 = str1.lower()696 str2 = str2.lower()697 if str1.endswith(str2):698 raise AssertionError(self._get_string_msg(str1, str2, msg, values,699 'ends with'))700 def should_end_with(self, str1, str2, msg=None, values=True,701 ignore_case=False):702 """Fails if the string ``str1`` does not end with the string ``str2``.703 See `Should Be Equal` for an explanation on how to override the default704 error message with ``msg`` and ``values``, as well as for semantics705 of the ``ignore_case`` option.706 """707 if is_truthy(ignore_case):708 str1 = str1.lower()709 str2 = str2.lower()710 if not str1.endswith(str2):711 raise AssertionError(self._get_string_msg(str1, str2, msg, values,712 'does not end with'))713 def should_not_contain(self, container, item, msg=None, values=True,714 ignore_case=False):715 """Fails if ``container`` contains ``item`` one or more times.716 Works with strings, lists, and anything that supports Python's ``in``717 operator.718 See `Should Be Equal` for an explanation on how to override the default719 error message with arguments ``msg`` and ``values``. ``ignore_case``720 has exactly the same semantics as with `Should Contain`.721 Examples:722 | Should Not Contain | ${some list} | value |723 | Should Not Contain | ${output} | FAILED | ignore_case=True |724 """725 # TODO: It is inconsistent that errors show original case in 'container'726 # 'item' is in lower case. Should rather show original case everywhere727 # and add separate '(case-insensitive)' not to the error message.728 # This same logic should be used with all keywords supporting729 # case-insensitive comparisons.730 orig_container = container731 if is_truthy(ignore_case) and is_string(item):732 item = item.lower()733 if is_string(container):734 container = container.lower()735 elif is_list_like(container):736 container = set(x.lower() if is_string(x) else x for x in container)737 if item in container:738 raise AssertionError(self._get_string_msg(orig_container, item, msg,739 values, 'contains'))740 def should_contain(self, container, item, msg=None, values=True,741 ignore_case=False):742 """Fails if ``container`` does not contain ``item`` one or more times.743 Works with strings, lists, and anything that supports Python's ``in``744 operator.745 See `Should Be Equal` for an explanation on how to override the default746 error message with arguments ``msg`` and ``values``.747 If ``ignore_case`` is given a true value (see `Boolean arguments`) and748 compared items are strings, it indicates that comparison should be749 case-insensitive. If the ``container`` is a list-like object, string750 items in it are compared case-insensitively. New option in Robot751 Framework 3.0.1.752 Examples:753 | Should Contain | ${output} | PASS |754 | Should Contain | ${some list} | value | msg=Failure! | values=False |755 | Should Contain | ${some list} | value | case_insensitive=True |756 """757 orig_container = container758 if is_truthy(ignore_case) and is_string(item):759 item = item.lower()760 if is_string(container):761 container = container.lower()762 elif is_list_like(container):763 container = set(x.lower() if is_string(x) else x for x in container)764 if item not in container:765 raise AssertionError(self._get_string_msg(orig_container, item, msg,766 values, 'does not contain'))767 def should_contain_any(self, container, *items, **configuration):768 """Fails if ``container`` does not contain any of the ``*items``.769 Works with strings, lists, and anything that supports Python's ``in``770 operator.771 Supports additional configuration parameters ``msg``, ``values``772 and ``ignore_case``, which have exactly the same semantics as arguments773 with same names have with `Should Contain`. These arguments must774 always be given using ``name=value`` syntax after all ``items``.775 Note that possible equal signs in ``items`` must be escaped with776 a backslash (e.g. ``foo\\=bar``) to avoid them to be passed in777 as ``**configuration``.778 Examples:779 | Should Contain Any | ${string} | substring 1 | substring 2 |780 | Should Contain Any | ${list} | item 1 | item 2 | item 3 |781 | Should Contain Any | ${list} | item 1 | item 2 | item 3 | ignore_case=True |782 | Should Contain Any | ${list} | @{items} | msg=Custom message | values=False |783 New in Robot Framework 3.0.1.784 """785 msg = configuration.pop('msg', None)786 values = configuration.pop('values', True)787 ignore_case = configuration.pop('ignore_case', False)788 if configuration:789 raise RuntimeError("Unsupported configuration parameter%s: %s."790 % (s(configuration),791 seq2str(sorted(configuration))))792 if not items:793 raise RuntimeError('One or more items required.')794 orig_container = container795 if is_truthy(ignore_case):796 items = [x.lower() if is_string(x) else x for x in items]797 if is_string(container):798 container = container.lower()799 elif is_list_like(container):800 container = set(x.lower() if is_string(x) else x for x in container)801 if not any(item in container for item in items):802 msg = self._get_string_msg(orig_container,803 seq2str(items, lastsep=' or '),804 msg, values,805 'does not contain any of',806 quote_item2=False)807 raise AssertionError(msg)808 def should_not_contain_any(self, container, *items, **configuration):809 """Fails if ``container`` contains one or more of the ``*items``.810 Works with strings, lists, and anything that supports Python's ``in``811 operator.812 Supports additional configuration parameters ``msg``, ``values``813 and ``ignore_case``, which have exactly the same semantics as arguments814 with same names have with `Should Contain`. These arguments must815 always be given using ``name=value`` syntax after all ``items``.816 Note that possible equal signs in ``items`` must be escaped with817 a backslash (e.g. ``foo\\=bar``) to avoid them to be passed in818 as ``**configuration``.819 Examples:820 | Should Not Contain Any | ${string} | substring 1 | substring 2 |821 | Should Not Contain Any | ${list} | item 1 | item 2 | item 3 |822 | Should Not Contain Any | ${list} | item 1 | item 2 | item 3 | ignore_case=True |823 | Should Not Contain Any | ${list} | @{items} | msg=Custom message | values=False |824 New in Robot Framework 3.0.1.825 """826 msg = configuration.pop('msg', None)827 values = configuration.pop('values', True)828 ignore_case = configuration.pop('ignore_case', False)829 if configuration:830 raise RuntimeError("Unsupported configuration parameter%s: %s."831 % (s(configuration),832 seq2str(sorted(configuration))))833 if not items:834 raise RuntimeError('One or more items required.')835 orig_container = container836 if is_truthy(ignore_case):837 items = [x.lower() if is_string(x) else x for x in items]838 if is_string(container):839 container = container.lower()840 elif is_list_like(container):841 container = set(x.lower() if is_string(x) else x for x in container)842 if any(item in container for item in items):843 msg = self._get_string_msg(orig_container,844 seq2str(items, lastsep=' or '),845 msg, values,846 'contains one or more of',847 quote_item2=False)848 raise AssertionError(msg)849 def should_contain_x_times(self, item1, item2, count, msg=None,850 ignore_case=False):851 """Fails if ``item1`` does not contain ``item2`` ``count`` times.852 Works with strings, lists and all objects that `Get Count` works853 with. The default error message can be overridden with ``msg`` and854 the actual count is always logged.855 If ``ignore_case`` is given a true value (see `Boolean arguments`) and856 compared items are strings, it indicates that comparison should be857 case-insensitive. If the ``item1`` is a list-like object, string858 items in it are compared case-insensitively. New option in Robot859 Framework 3.0.1.860 Examples:861 | Should Contain X Times | ${output} | hello | 2 |862 | Should Contain X Times | ${some list} | value | 3 | ignore_case=True |863 """864 # TODO: Rename 'item1' and 'item2' to 'container' and 'item' in RF 3.1.865 # Other 'contain' keywords use these names. And 'Get Count' should too.866 # Cannot be done in minor release due to backwards compatibility.867 # Remember to update it also in the docstring!!868 count = self._convert_to_integer(count)869 orig_item1 = item1870 if is_truthy(ignore_case) and is_string(item2):871 item2 = item2.lower()872 if is_string(item1):873 item1 = item1.lower()874 elif is_list_like(item1):875 item1 = [x.lower() if is_string(x) else x for x in item1]876 x = self.get_count(item1, item2)877 if not msg:878 msg = "'%s' contains '%s' %d time%s, not %d time%s." \879 % (unic(orig_item1), unic(item2), x, s(x), count, s(count))880 self.should_be_equal_as_integers(x, count, msg, values=False)881 def get_count(self, item1, item2):882 """Returns and logs how many times ``item2`` is found from ``item1``.883 This keyword works with Python strings and lists and all objects884 that either have ``count`` method or can be converted to Python lists.885 Example:886 | ${count} = | Get Count | ${some item} | interesting value |887 | Should Be True | 5 < ${count} < 10 |888 """889 if not hasattr(item1, 'count'):890 try:891 item1 = list(item1)892 except:893 raise RuntimeError("Converting '%s' to list failed: %s"894 % (item1, get_error_message()))895 count = item1.count(item2)896 self.log('Item found from the first item %d time%s' % (count, s(count)))897 return count898 def should_not_match(self, string, pattern, msg=None, values=True,899 ignore_case=False):900 """Fails if the given ``string`` matches the given ``pattern``.901 Pattern matching is similar as matching files in a shell, and it is902 always case-sensitive. In the pattern ``*`` matches to anything and903 ``?`` matches to any single character.904 See `Should Be Equal` for an explanation on how to override the default905 error message with ``msg`` and ``values``, as well as for semantics906 of the ``ignore_case`` option.907 """908 if is_truthy(ignore_case):909 string = string.lower()910 pattern = pattern.lower()911 if self._matches(string, pattern):912 raise AssertionError(self._get_string_msg(string, pattern, msg,913 values, 'matches'))914 def should_match(self, string, pattern, msg=None, values=True,915 ignore_case=False):916 """Fails unless the given ``string`` matches the given ``pattern``.917 Pattern matching is similar as matching files in a shell, and it is918 always case-sensitive. In the pattern, ``*`` matches to anything and919 ``?`` matches to any single character.920 See `Should Be Equal` for an explanation on how to override the default921 error message with ``msg`` and ``values``, as well as for semantics922 of the ``ignore_case`` option.923 """924 if is_truthy(ignore_case):925 string = string.lower()926 pattern = pattern.lower()927 if not self._matches(string, pattern):928 raise AssertionError(self._get_string_msg(string, pattern, msg,929 values, 'does not match'))930 def should_match_regexp(self, string, pattern, msg=None, values=True):931 """Fails if ``string`` does not match ``pattern`` as a regular expression.932 Regular expression check is implemented using the Python933 [http://docs.python.org/2/library/re.html|re module]. Python's regular934 expression syntax is derived from Perl, and it is thus also very935 similar to the syntax used, for example, in Java, Ruby and .NET.936 Things to note about the regexp syntax in Robot Framework test data:937 1) Backslash is an escape character in the test data, and possible938 backslashes in the pattern must thus be escaped with another backslash939 (e.g. ``\\\\d\\\\w+``).940 2) Strings that may contain special characters, but should be handled941 as literal strings, can be escaped with the `Regexp Escape` keyword.942 3) The given pattern does not need to match the whole string. For943 example, the pattern ``ello`` matches the string ``Hello world!``. If944 a full match is needed, the ``^`` and ``$`` characters can be used to945 denote the beginning and end of the string, respectively. For example,946 ``^ello$`` only matches the exact string ``ello``.947 4) Possible flags altering how the expression is parsed (e.g.948 ``re.IGNORECASE``, ``re.MULTILINE``) can be set by prefixing the949 pattern with the ``(?iLmsux)`` group like ``(?im)pattern``. The950 available flags are ``i`` (case-insensitive), ``m`` (multiline mode),951 ``s`` (dotall mode), ``x`` (verbose), ``u`` (Unicode dependent) and952 ``L`` (locale dependent).953 If this keyword passes, it returns the portion of the string that954 matched the pattern. Additionally, the possible captured groups are955 returned.956 See the `Should Be Equal` keyword for an explanation on how to override957 the default error message with the ``msg`` and ``values`` arguments.958 Examples:959 | Should Match Regexp | ${output} | \\\\d{6} | # Output contains six numbers |960 | Should Match Regexp | ${output} | ^\\\\d{6}$ | # Six numbers and nothing more |961 | ${ret} = | Should Match Regexp | Foo: 42 | (?i)foo: \\\\d+ |962 | ${match} | ${group1} | ${group2} = |963 | ... | Should Match Regexp | Bar: 43 | (Foo|Bar): (\\\\d+) |964 =>965 | ${ret} = 'Foo: 42'966 | ${match} = 'Bar: 43'967 | ${group1} = 'Bar'968 | ${group2} = '43'969 """970 res = re.search(pattern, string)971 if res is None:972 raise AssertionError(self._get_string_msg(string, pattern, msg,973 values, 'does not match'))974 match = res.group(0)975 groups = res.groups()976 if groups:977 return [match] + list(groups)978 return match979 def should_not_match_regexp(self, string, pattern, msg=None, values=True):980 """Fails if ``string`` matches ``pattern`` as a regular expression.981 See `Should Match Regexp` for more information about arguments.982 """983 if re.search(pattern, string) is not None:984 raise AssertionError(self._get_string_msg(string, pattern, msg,985 values, 'matches'))986 def get_length(self, item):987 """Returns and logs the length of the given item as an integer.988 The item can be anything that has a length, for example, a string,989 a list, or a mapping. The keyword first tries to get the length with990 the Python function ``len``, which calls the item's ``__len__`` method991 internally. If that fails, the keyword tries to call the item's992 possible ``length`` and ``size`` methods directly. The final attempt is993 trying to get the value of the item's ``length`` attribute. If all994 these attempts are unsuccessful, the keyword fails.995 Examples:996 | ${length} = | Get Length | Hello, world! | |997 | Should Be Equal As Integers | ${length} | 13 |998 | @{list} = | Create List | Hello, | world! |999 | ${length} = | Get Length | ${list} | |1000 | Should Be Equal As Integers | ${length} | 2 |1001 See also `Length Should Be`, `Should Be Empty` and `Should Not Be1002 Empty`.1003 """1004 length = self._get_length(item)1005 self.log('Length is %d' % length)1006 return length1007 def _get_length(self, item):1008 try:1009 return len(item)1010 except RERAISED_EXCEPTIONS:1011 raise1012 except:1013 try:1014 return item.length()1015 except RERAISED_EXCEPTIONS:1016 raise1017 except:1018 try:1019 return item.size()1020 except RERAISED_EXCEPTIONS:1021 raise1022 except:1023 try:1024 return item.length1025 except RERAISED_EXCEPTIONS:1026 raise1027 except:1028 raise RuntimeError("Could not get length of '%s'." % item)1029 def length_should_be(self, item, length, msg=None):1030 """Verifies that the length of the given item is correct.1031 The length of the item is got using the `Get Length` keyword. The1032 default error message can be overridden with the ``msg`` argument.1033 """1034 length = self._convert_to_integer(length)1035 actual = self.get_length(item)1036 if actual != length:1037 raise AssertionError(msg or "Length of '%s' should be %d but is %d."1038 % (item, length, actual))1039 def should_be_empty(self, item, msg=None):1040 """Verifies that the given item is empty.1041 The length of the item is got using the `Get Length` keyword. The1042 default error message can be overridden with the ``msg`` argument.1043 """1044 if self.get_length(item) > 0:1045 raise AssertionError(msg or "'%s' should be empty." % item)1046 def should_not_be_empty(self, item, msg=None):1047 """Verifies that the given item is not empty.1048 The length of the item is got using the `Get Length` keyword. The1049 default error message can be overridden with the ``msg`` argument.1050 """1051 if self.get_length(item) == 0:1052 raise AssertionError(msg or "'%s' should not be empty." % item)1053 def _get_string_msg(self, item1, item2, custom_message, include_values,1054 delimiter, quote_item1=True, quote_item2=True):1055 if custom_message and not self._include_values(include_values):1056 return custom_message1057 item1 = "'%s'" % unic(item1) if quote_item1 else unic(item1)1058 item2 = "'%s'" % unic(item2) if quote_item2 else unic(item2)1059 default_message = '%s %s %s' % (item1, delimiter, item2)1060 if not custom_message:1061 return default_message1062 return '%s: %s' % (custom_message, default_message)1063class _Variables(_BuiltInBase):1064 def get_variables(self, no_decoration=False):1065 """Returns a dictionary containing all variables in the current scope.1066 Variables are returned as a special dictionary that allows accessing1067 variables in space, case, and underscore insensitive manner similarly1068 as accessing variables in the test data. This dictionary supports all1069 same operations as normal Python dictionaries and, for example,1070 Collections library can be used to access or modify it. Modifying the1071 returned dictionary has no effect on the variables available in the1072 current scope.1073 By default variables are returned with ``${}``, ``@{}`` or ``&{}``1074 decoration based on variable types. Giving a true value (see `Boolean1075 arguments`) to the optional argument ``no_decoration`` will return1076 the variables without the decoration. This option is new in Robot1077 Framework 2.9.1078 Example:1079 | ${example_variable} = | Set Variable | example value |1080 | ${variables} = | Get Variables | |1081 | Dictionary Should Contain Key | ${variables} | \\${example_variable} |1082 | Dictionary Should Contain Key | ${variables} | \\${ExampleVariable} |1083 | Set To Dictionary | ${variables} | \\${name} | value |1084 | Variable Should Not Exist | \\${name} | | |1085 | ${no decoration} = | Get Variables | no_decoration=Yes |1086 | Dictionary Should Contain Key | ${no decoration} | example_variable |1087 Note: Prior to Robot Framework 2.7.4 variables were returned as1088 a custom object that did not support all dictionary methods.1089 """1090 return self._variables.as_dict(decoration=is_falsy(no_decoration))1091 @run_keyword_variant(resolve=0)1092 def get_variable_value(self, name, default=None):1093 """Returns variable value or ``default`` if the variable does not exist.1094 The name of the variable can be given either as a normal variable name1095 (e.g. ``${NAME}``) or in escaped format (e.g. ``\\${NAME}``). Notice1096 that the former has some limitations explained in `Set Suite Variable`.1097 Examples:1098 | ${x} = | Get Variable Value | ${a} | default |1099 | ${y} = | Get Variable Value | ${a} | ${b} |1100 | ${z} = | Get Variable Value | ${z} | |1101 =>1102 | ${x} gets value of ${a} if ${a} exists and string 'default' otherwise1103 | ${y} gets value of ${a} if ${a} exists and value of ${b} otherwise1104 | ${z} is set to Python None if it does not exist previously1105 See `Set Variable If` for another keyword to set variables dynamically.1106 """1107 try:1108 return self._variables[self._get_var_name(name)]1109 except DataError:1110 return self._variables.replace_scalar(default)1111 def log_variables(self, level='INFO'):1112 """Logs all variables in the current scope with given log level."""1113 variables = self.get_variables()1114 for name in sorted(variables, key=lambda s: s[2:-1].lower()):1115 msg = format_assign_message(name, variables[name], cut_long=False)1116 self.log(msg, level)1117 @run_keyword_variant(resolve=0)1118 def variable_should_exist(self, name, msg=None):1119 """Fails unless the given variable exists within the current scope.1120 The name of the variable can be given either as a normal variable name1121 (e.g. ``${NAME}``) or in escaped format (e.g. ``\\${NAME}``). Notice1122 that the former has some limitations explained in `Set Suite Variable`.1123 The default error message can be overridden with the ``msg`` argument.1124 See also `Variable Should Not Exist` and `Keyword Should Exist`.1125 """1126 name = self._get_var_name(name)1127 msg = self._variables.replace_string(msg) if msg \1128 else "Variable %s does not exist." % name1129 try:1130 self._variables[name]1131 except DataError:1132 raise AssertionError(msg)1133 @run_keyword_variant(resolve=0)1134 def variable_should_not_exist(self, name, msg=None):1135 """Fails if the given variable exists within the current scope.1136 The name of the variable can be given either as a normal variable name1137 (e.g. ``${NAME}``) or in escaped format (e.g. ``\\${NAME}``). Notice1138 that the former has some limitations explained in `Set Suite Variable`.1139 The default error message can be overridden with the ``msg`` argument.1140 See also `Variable Should Exist` and `Keyword Should Exist`.1141 """1142 name = self._get_var_name(name)1143 msg = self._variables.replace_string(msg) if msg \1144 else "Variable %s exists." % name1145 try:1146 self._variables[name]1147 except DataError:1148 pass1149 else:1150 raise AssertionError(msg)1151 def replace_variables(self, text):1152 """Replaces variables in the given text with their current values.1153 If the text contains undefined variables, this keyword fails.1154 If the given ``text`` contains only a single variable, its value is1155 returned as-is and it can be any object. Otherwise this keyword1156 always returns a string.1157 Example:1158 The file ``template.txt`` contains ``Hello ${NAME}!`` and variable1159 ``${NAME}`` has the value ``Robot``.1160 | ${template} = | Get File | ${CURDIR}/template.txt |1161 | ${message} = | Replace Variables | ${template} |1162 | Should Be Equal | ${message} | Hello Robot! |1163 """1164 return self._variables.replace_scalar(text)1165 def set_variable(self, *values):1166 """Returns the given values which can then be assigned to a variables.1167 This keyword is mainly used for setting scalar variables.1168 Additionally it can be used for converting a scalar variable1169 containing a list to a list variable or to multiple scalar variables.1170 It is recommended to use `Create List` when creating new lists.1171 Examples:1172 | ${hi} = | Set Variable | Hello, world! |1173 | ${hi2} = | Set Variable | I said: ${hi} |1174 | ${var1} | ${var2} = | Set Variable | Hello | world |1175 | @{list} = | Set Variable | ${list with some items} |1176 | ${item1} | ${item2} = | Set Variable | ${list with 2 items} |1177 Variables created with this keyword are available only in the1178 scope where they are created. See `Set Global Variable`,1179 `Set Test Variable` and `Set Suite Variable` for information on how to1180 set variables so that they are available also in a larger scope.1181 """1182 if len(values) == 0:1183 return ''1184 elif len(values) == 1:1185 return values[0]1186 else:1187 return list(values)1188 @run_keyword_variant(resolve=0)1189 def set_test_variable(self, name, *values):1190 """Makes a variable available everywhere within the scope of the current test.1191 Variables set with this keyword are available everywhere within the1192 scope of the currently executed test case. For example, if you set a1193 variable in a user keyword, it is available both in the test case level1194 and also in all other user keywords used in the current test. Other1195 test cases will not see variables set with this keyword.1196 See `Set Suite Variable` for more information and examples.1197 """1198 name = self._get_var_name(name)1199 value = self._get_var_value(name, values)1200 self._variables.set_test(name, value)1201 self._log_set_variable(name, value)1202 @run_keyword_variant(resolve=0)1203 def set_suite_variable(self, name, *values):1204 """Makes a variable available everywhere within the scope of the current suite.1205 Variables set with this keyword are available everywhere within the1206 scope of the currently executed test suite. Setting variables with this1207 keyword thus has the same effect as creating them using the Variable1208 table in the test data file or importing them from variable files.1209 Possible child test suites do not see variables set with this keyword1210 by default. Starting from Robot Framework 2.9, that can be controlled1211 by using ``children=<option>`` as the last argument. If the specified1212 ``<option>`` is a non-empty string or any other value considered true1213 in Python, the variable is set also to the child suites. Parent and1214 sibling suites will never see variables set with this keyword.1215 The name of the variable can be given either as a normal variable name1216 (e.g. ``${NAME}``) or in escaped format as ``\\${NAME}`` or ``$NAME``.1217 Variable value can be given using the same syntax as when variables1218 are created in the Variable table.1219 If a variable already exists within the new scope, its value will be1220 overwritten. Otherwise a new variable is created. If a variable already1221 exists within the current scope, the value can be left empty and the1222 variable within the new scope gets the value within the current scope.1223 Examples:1224 | Set Suite Variable | ${SCALAR} | Hello, world! |1225 | Set Suite Variable | ${SCALAR} | Hello, world! | children=true |1226 | Set Suite Variable | @{LIST} | First item | Second item |1227 | Set Suite Variable | &{DICT} | key=value | foo=bar |1228 | ${ID} = | Get ID |1229 | Set Suite Variable | ${ID} |1230 To override an existing value with an empty value, use built-in1231 variables ``${EMPTY}``, ``@{EMPTY}`` or ``&{EMPTY}``:1232 | Set Suite Variable | ${SCALAR} | ${EMPTY} |1233 | Set Suite Variable | @{LIST} | @{EMPTY} | # New in RF 2.7.4 |1234 | Set Suite Variable | &{DICT} | &{EMPTY} | # New in RF 2.9 |1235 *NOTE:* If the variable has value which itself is a variable (escaped1236 or not), you must always use the escaped format to set the variable:1237 Example:1238 | ${NAME} = | Set Variable | \\${var} |1239 | Set Suite Variable | ${NAME} | value | # Sets variable ${var} |1240 | Set Suite Variable | \\${NAME} | value | # Sets variable ${NAME} |1241 This limitation applies also to `Set Test Variable`, `Set Global1242 Variable`, `Variable Should Exist`, `Variable Should Not Exist` and1243 `Get Variable Value` keywords.1244 """1245 name = self._get_var_name(name)1246 if (values and is_string(values[-1]) and1247 values[-1].startswith('children=')):1248 children = self._variables.replace_scalar(values[-1][9:])1249 children = is_truthy(children)1250 values = values[:-1]1251 else:1252 children = False1253 value = self._get_var_value(name, values)1254 self._variables.set_suite(name, value, children=children)1255 self._log_set_variable(name, value)1256 @run_keyword_variant(resolve=0)1257 def set_global_variable(self, name, *values):1258 """Makes a variable available globally in all tests and suites.1259 Variables set with this keyword are globally available in all test1260 cases and suites executed after setting them. Setting variables with1261 this keyword thus has the same effect as creating from the command line1262 using the options ``--variable`` or ``--variablefile``. Because this1263 keyword can change variables everywhere, it should be used with care.1264 See `Set Suite Variable` for more information and examples.1265 """1266 name = self._get_var_name(name)1267 value = self._get_var_value(name, values)1268 self._variables.set_global(name, value)1269 self._log_set_variable(name, value)1270 # Helpers1271 def _get_var_name(self, orig):1272 name = self._resolve_possible_variable(orig)1273 try:1274 return self._unescape_variable_if_needed(name)1275 except ValueError:1276 raise RuntimeError("Invalid variable syntax '%s'." % orig)1277 def _resolve_possible_variable(self, name):1278 try:1279 resolved = self._variables.replace_string(name)1280 return self._unescape_variable_if_needed(resolved)1281 except (KeyError, ValueError, DataError):1282 return name1283 def _unescape_variable_if_needed(self, name):1284 if name.startswith('\\'):1285 name = name[1:]1286 if len(name) < 2:1287 raise ValueError1288 if name[0] in '$@&' and name[1] != '{':1289 name = '%s{%s}' % (name[0], name[1:])1290 if is_var(name):1291 return name1292 # Support for possible internal variables (issue 397)1293 name = '%s{%s}' % (name[0], self.replace_variables(name[2:-1]))1294 if is_var(name):1295 return name1296 raise ValueError1297 def _get_var_value(self, name, values):1298 if not values:1299 return self._variables[name]1300 if name[0] == '$':1301 # We could consider catenating values similarly as when creating1302 # scalar variables in the variable table, but that would require1303 # handling non-string values somehow. For details see1304 # https://github.com/robotframework/robotframework/issues/19191305 if len(values) != 1 or VariableSplitter(values[0]).is_list_variable():1306 raise DataError("Setting list value to scalar variable '%s' "1307 "is not supported anymore. Create list "1308 "variable '@%s' instead." % (name, name[1:]))1309 return self._variables.replace_scalar(values[0])1310 return VariableTableValue(values, name).resolve(self._variables)1311 def _log_set_variable(self, name, value):1312 self.log(format_assign_message(name, value))1313class _RunKeyword(_BuiltInBase):1314 # If you use any of these run keyword variants from another library, you1315 # should register those keywords with 'register_run_keyword' method. See1316 # the documentation of that method at the end of this file. There are also1317 # other run keyword variant keywords in BuiltIn which can also be seen1318 # at the end of this file.1319 @run_keyword_variant(resolve=1)1320 def run_keyword(self, name, *args):1321 """Executes the given keyword with the given arguments.1322 Because the name of the keyword to execute is given as an argument, it1323 can be a variable and thus set dynamically, e.g. from a return value of1324 another keyword or from the command line.1325 """1326 if not is_string(name):1327 raise RuntimeError('Keyword name must be a string.')1328 kw = Keyword(name, args=args)1329 return kw.run(self._context)1330 @run_keyword_variant(resolve=0)1331 def run_keywords(self, *keywords):1332 """Executes all the given keywords in a sequence.1333 This keyword is mainly useful in setups and teardowns when they need1334 to take care of multiple actions and creating a new higher level user1335 keyword would be an overkill.1336 By default all arguments are expected to be keywords to be executed.1337 Examples:1338 | Run Keywords | Initialize database | Start servers | Clear logs |1339 | Run Keywords | ${KW 1} | ${KW 2} |1340 | Run Keywords | @{KEYWORDS} |1341 Starting from Robot Framework 2.7.6, keywords can also be run with1342 arguments using upper case ``AND`` as a separator between keywords.1343 The keywords are executed so that the first argument is the first1344 keyword and proceeding arguments until the first ``AND`` are arguments1345 to it. First argument after the first ``AND`` is the second keyword and1346 proceeding arguments until the next ``AND`` are its arguments. And so on.1347 Examples:1348 | Run Keywords | Initialize database | db1 | AND | Start servers | server1 | server2 |1349 | Run Keywords | Initialize database | ${DB NAME} | AND | Start servers | @{SERVERS} | AND | Clear logs |1350 | Run Keywords | ${KW} | AND | @{KW WITH ARGS} |1351 Notice that the ``AND`` control argument must be used explicitly and1352 cannot itself come from a variable. If you need to use literal ``AND``1353 string as argument, you can either use variables or escape it with1354 a backslash like ``\\AND``.1355 """1356 self._run_keywords(self._split_run_keywords(list(keywords)))1357 def _run_keywords(self, iterable):1358 errors = []1359 for kw, args in iterable:1360 try:1361 self.run_keyword(kw, *args)1362 except ExecutionPassed as err:1363 err.set_earlier_failures(errors)1364 raise err1365 except ExecutionFailed as err:1366 errors.extend(err.get_errors())1367 if not err.can_continue(self._context.in_teardown):1368 break1369 if errors:1370 raise ExecutionFailures(errors)1371 def _split_run_keywords(self, keywords):1372 if 'AND' not in keywords:1373 for name in self._variables.replace_list(keywords):1374 yield name, ()1375 else:1376 for name, args in self._split_run_keywords_from_and(keywords):1377 yield name, args1378 def _split_run_keywords_from_and(self, keywords):1379 while 'AND' in keywords:1380 index = keywords.index('AND')1381 yield self._resolve_run_keywords_name_and_args(keywords[:index])1382 keywords = keywords[index+1:]1383 yield self._resolve_run_keywords_name_and_args(keywords)1384 def _resolve_run_keywords_name_and_args(self, kw_call):1385 kw_call = self._variables.replace_list(kw_call, replace_until=1)1386 if not kw_call:1387 raise DataError('Incorrect use of AND')1388 return kw_call[0], kw_call[1:]1389 @run_keyword_variant(resolve=2)1390 def run_keyword_if(self, condition, name, *args):1391 """Runs the given keyword with the given arguments, if ``condition`` is true.1392 The given ``condition`` is evaluated in Python as explained in1393 `Evaluating expressions`, and ``name`` and ``*args`` have same1394 semantics as with `Run Keyword`.1395 Example, a simple if/else construct:1396 | ${status} | ${value} = | `Run Keyword And Ignore Error` | `My Keyword` |1397 | `Run Keyword If` | '${status}' == 'PASS' | `Some Action` | arg |1398 | `Run Keyword Unless` | '${status}' == 'PASS' | `Another Action` |1399 In this example, only either `Some Action` or `Another Action` is1400 executed, based on the status of `My Keyword`. Instead of `Run Keyword1401 And Ignore Error` you can also use `Run Keyword And Return Status`.1402 Variables used like ``${variable}``, as in the examples above, are1403 replaced in the expression before evaluation. Variables are also1404 available in the evaluation namespace and can be accessed using special1405 syntax ``$variable``. This is a new feature in Robot Framework 2.91406 and it is explained more thoroughly in `Evaluating expressions`.1407 Example:1408 | `Run Keyword If` | $result is None or $result == 'FAIL' | `Keyword` |1409 Starting from Robot version 2.7.4, this keyword supports also optional1410 ELSE and ELSE IF branches. Both of these are defined in ``*args`` and1411 must use exactly format ``ELSE`` or ``ELSE IF``, respectively. ELSE1412 branches must contain first the name of the keyword to execute and then1413 its possible arguments. ELSE IF branches must first contain a condition,1414 like the first argument to this keyword, and then the keyword to execute1415 and its possible arguments. It is possible to have ELSE branch after1416 ELSE IF and to have multiple ELSE IF branches.1417 Given previous example, if/else construct can also be created like this:1418 | ${status} | ${value} = | `Run Keyword And Ignore Error` | My Keyword |1419 | `Run Keyword If` | '${status}' == 'PASS' | `Some Action` | arg | ELSE | `Another Action` |1420 The return value is the one of the keyword that was executed or None if1421 no keyword was executed (i.e. if ``condition`` was false). Hence, it is1422 recommended to use ELSE and/or ELSE IF branches to conditionally assign1423 return values from keyword to variables (to conditionally assign fixed1424 values to variables, see `Set Variable If`). This is illustrated by the1425 example below:1426 | ${var1} = | `Run Keyword If` | ${rc} == 0 | `Some keyword returning a value` |1427 | ... | ELSE IF | 0 < ${rc} < 42 | `Another keyword` |1428 | ... | ELSE IF | ${rc} < 0 | `Another keyword with args` | ${rc} | arg2 |1429 | ... | ELSE | `Final keyword to handle abnormal cases` | ${rc} |1430 | ${var2} = | `Run Keyword If` | ${condition} | `Some keyword` |1431 In this example, ${var2} will be set to None if ${condition} is false.1432 Notice that ``ELSE`` and ``ELSE IF`` control words must be used1433 explicitly and thus cannot come from variables. If you need to use1434 literal ``ELSE`` and ``ELSE IF`` strings as arguments, you can escape1435 them with a backslash like ``\\ELSE`` and ``\\ELSE IF``.1436 Starting from Robot Framework 2.8, Python's1437 [http://docs.python.org/2/library/os.html|os] and1438 [http://docs.python.org/2/library/sys.html|sys] modules are1439 automatically imported when evaluating the ``condition``.1440 Attributes they contain can thus be used in the condition:1441 | `Run Keyword If` | os.sep == '/' | `Unix Keyword` |1442 | ... | ELSE IF | sys.platform.startswith('java') | `Jython Keyword` |1443 | ... | ELSE | `Windows Keyword` |1444 """1445 args, branch = self._split_elif_or_else_branch(args)1446 if self._is_true(condition):1447 return self.run_keyword(name, *args)1448 return branch()1449 def _split_elif_or_else_branch(self, args):1450 if 'ELSE IF' in args:1451 args, branch = self._split_branch(args, 'ELSE IF', 2,1452 'condition and keyword')1453 return args, lambda: self.run_keyword_if(*branch)1454 if 'ELSE' in args:1455 args, branch = self._split_branch(args, 'ELSE', 1, 'keyword')1456 return args, lambda: self.run_keyword(*branch)1457 return args, lambda: None1458 def _split_branch(self, args, control_word, required, required_error):1459 index = list(args).index(control_word)1460 branch = self._variables.replace_list(args[index+1:], required)1461 if len(branch) < required:1462 raise DataError('%s requires %s.' % (control_word, required_error))1463 return args[:index], branch1464 @run_keyword_variant(resolve=2)1465 def run_keyword_unless(self, condition, name, *args):1466 """Runs the given keyword with the given arguments, if ``condition`` is false.1467 See `Run Keyword If` for more information and an example.1468 """1469 if not self._is_true(condition):1470 return self.run_keyword(name, *args)1471 @run_keyword_variant(resolve=1)1472 def run_keyword_and_ignore_error(self, name, *args):1473 """Runs the given keyword with the given arguments and ignores possible error.1474 This keyword returns two values, so that the first is either string1475 ``PASS`` or ``FAIL``, depending on the status of the executed keyword.1476 The second value is either the return value of the keyword or the1477 received error message. See `Run Keyword And Return Status` If you are1478 only interested in the execution status.1479 The keyword name and arguments work as in `Run Keyword`. See1480 `Run Keyword If` for a usage example.1481 Errors caused by invalid syntax, timeouts, or fatal exceptions are not1482 caught by this keyword. Otherwise this keyword itself never fails.1483 Since Robot Framework 2.9, variable errors are caught by this keyword.1484 """1485 try:1486 return 'PASS', self.run_keyword(name, *args)1487 except ExecutionFailed as err:1488 if err.dont_continue:1489 raise1490 return 'FAIL', unic(err)1491 @run_keyword_variant(resolve=1)1492 def run_keyword_and_return_status(self, name, *args):1493 """Runs the given keyword with given arguments and returns the status as a Boolean value.1494 This keyword returns Boolean ``True`` if the keyword that is executed1495 succeeds and ``False`` if it fails. This is useful, for example, in1496 combination with `Run Keyword If`. If you are interested in the error1497 message or return value, use `Run Keyword And Ignore Error` instead.1498 The keyword name and arguments work as in `Run Keyword`.1499 Example:1500 | ${passed} = | `Run Keyword And Return Status` | Keyword | args |1501 | `Run Keyword If` | ${passed} | Another keyword |1502 Errors caused by invalid syntax, timeouts, or fatal exceptions are not1503 caught by this keyword. Otherwise this keyword itself never fails.1504 New in Robot Framework 2.7.6.1505 """1506 status, _ = self.run_keyword_and_ignore_error(name, *args)1507 return status == 'PASS'1508 @run_keyword_variant(resolve=1)1509 def run_keyword_and_continue_on_failure(self, name, *args):1510 """Runs the keyword and continues execution even if a failure occurs.1511 The keyword name and arguments work as with `Run Keyword`.1512 Example:1513 | Run Keyword And Continue On Failure | Fail | This is a stupid example |1514 | Log | This keyword is executed |1515 The execution is not continued if the failure is caused by invalid syntax,1516 timeout, or fatal exception.1517 Since Robot Framework 2.9, variable errors are caught by this keyword.1518 """1519 try:1520 return self.run_keyword(name, *args)1521 except ExecutionFailed as err:1522 if not err.dont_continue:1523 err.continue_on_failure = True1524 raise err1525 @run_keyword_variant(resolve=2)1526 def run_keyword_and_expect_error(self, expected_error, name, *args):1527 """Runs the keyword and checks that the expected error occurred.1528 The expected error must be given in the same format as in1529 Robot Framework reports. It can be a pattern containing1530 characters ``?``, which matches to any single character and1531 ``*``, which matches to any number of any characters. ``name`` and1532 ``*args`` have same semantics as with `Run Keyword`.1533 If the expected error occurs, the error message is returned and it can1534 be further processed/tested, if needed. If there is no error, or the1535 error does not match the expected error, this keyword fails.1536 Examples:1537 | Run Keyword And Expect Error | My error | Some Keyword | arg1 | arg2 |1538 | ${msg} = | Run Keyword And Expect Error | * | My KW |1539 | Should Start With | ${msg} | Once upon a time in |1540 Errors caused by invalid syntax, timeouts, or fatal exceptions are not1541 caught by this keyword.1542 Since Robot Framework 2.9, variable errors are caught by this keyword.1543 """1544 try:1545 self.run_keyword(name, *args)1546 except ExecutionFailed as err:1547 if err.dont_continue:1548 raise1549 error = err1550 else:1551 raise AssertionError("Expected error '%s' did not occur."1552 % expected_error)1553 if not self._matches(unic(error), expected_error):1554 raise AssertionError("Expected error '%s' but got '%s'."1555 % (expected_error, error))1556 return unic(error)1557 @run_keyword_variant(resolve=2)1558 def repeat_keyword(self, repeat, name, *args):1559 """Executes the specified keyword multiple times.1560 ``name`` and ``args`` define the keyword that is executed similarly as1561 with `Run Keyword`. ``repeat`` specifies how many times (as a count) or1562 how long time (as a timeout) the keyword should be executed.1563 If ``repeat`` is given as count, it specifies how many times the1564 keyword should be executed. ``repeat`` can be given as an integer or1565 as a string that can be converted to an integer. If it is a string,1566 it can have postfix ``times`` or ``x`` (case and space insensitive)1567 to make the expression more explicit.1568 If ``repeat`` is given as timeout, it must be in Robot Framework's1569 time format (e.g. ``1 minute``, ``2 min 3 s``). Using a number alone1570 (e.g. ``1`` or ``1.5``) does not work in this context.1571 If ``repeat`` is zero or negative, the keyword is not executed at1572 all. This keyword fails immediately if any of the execution1573 rounds fails.1574 Examples:1575 | Repeat Keyword | 5 times | Go to Previous Page |1576 | Repeat Keyword | ${var} | Some Keyword | arg1 | arg2 |1577 | Repeat Keyword | 2 minutes | Some Keyword | arg1 | arg2 |1578 Specifying ``repeat`` as a timeout is new in Robot Framework 3.0.1579 """1580 try:1581 count = self._get_repeat_count(repeat)1582 except RuntimeError as err:1583 timeout = self._get_repeat_timeout(repeat)1584 if timeout is None:1585 raise err1586 keywords = self._keywords_repeated_by_timeout(timeout, name, args)1587 else:1588 keywords = self._keywords_repeated_by_count(count, name, args)1589 self._run_keywords(keywords)1590 def _get_repeat_count(self, times, require_postfix=False):1591 times = normalize(str(times))1592 if times.endswith('times'):1593 times = times[:-5]1594 elif times.endswith('x'):1595 times = times[:-1]1596 elif require_postfix:1597 raise ValueError1598 return self._convert_to_integer(times)1599 def _get_repeat_timeout(self, timestr):1600 try:1601 float(timestr)1602 except ValueError:1603 pass1604 else:1605 return None1606 try:1607 return timestr_to_secs(timestr)1608 except ValueError:1609 return None1610 def _keywords_repeated_by_count(self, count, name, args):1611 if count <= 0:1612 self.log("Keyword '%s' repeated zero times." % name)1613 for i in range(count):1614 self.log("Repeating keyword, round %d/%d." % (i + 1, count))1615 yield name, args1616 def _keywords_repeated_by_timeout(self, timeout, name, args):1617 if timeout <= 0:1618 self.log("Keyword '%s' repeated zero times." % name)1619 repeat_round = 01620 maxtime = time.time() + timeout1621 while time.time() < maxtime:1622 repeat_round += 11623 self.log("Repeating keyword, round %d, %s remaining."1624 % (repeat_round,1625 secs_to_timestr(maxtime - time.time(), compact=True)))1626 yield name, args1627 @run_keyword_variant(resolve=3)1628 def wait_until_keyword_succeeds(self, retry, retry_interval, name, *args):1629 """Runs the specified keyword and retries if it fails.1630 ``name`` and ``args`` define the keyword that is executed similarly1631 as with `Run Keyword`. How long to retry running the keyword is1632 defined using ``retry`` argument either as timeout or count.1633 ``retry_interval`` is the time to wait before trying to run the1634 keyword again after the previous run has failed.1635 If ``retry`` is given as timeout, it must be in Robot Framework's1636 time format (e.g. ``1 minute``, ``2 min 3 s``, ``4.5``) that is1637 explained in an appendix of Robot Framework User Guide. If it is1638 given as count, it must have ``times`` or ``x`` postfix (e.g.1639 ``5 times``, ``10 x``). ``retry_interval`` must always be given in1640 Robot Framework's time format.1641 If the keyword does not succeed regardless of retries, this keyword1642 fails. If the executed keyword passes, its return value is returned.1643 Examples:1644 | Wait Until Keyword Succeeds | 2 min | 5 sec | My keyword | argument |1645 | ${result} = | Wait Until Keyword Succeeds | 3x | 200ms | My keyword |1646 All normal failures are caught by this keyword. Errors caused by1647 invalid syntax, test or keyword timeouts, or fatal exceptions (caused1648 e.g. by `Fatal Error`) are not caught.1649 Running the same keyword multiple times inside this keyword can create1650 lots of output and considerably increase the size of the generated1651 output files. Starting from Robot Framework 2.7, it is possible to1652 remove unnecessary keywords from the outputs using1653 ``--RemoveKeywords WUKS`` command line option.1654 Support for specifying ``retry`` as a number of times to retry is1655 a new feature in Robot Framework 2.9.1656 Since Robot Framework 2.9, variable errors are caught by this keyword.1657 """1658 maxtime = count = -11659 try:1660 count = self._get_repeat_count(retry, require_postfix=True)1661 except ValueError:1662 timeout = timestr_to_secs(retry)1663 maxtime = time.time() + timeout1664 message = 'for %s' % secs_to_timestr(timeout)1665 else:1666 if count <= 0:1667 raise ValueError('Retry count %d is not positive.' % count)1668 message = '%d time%s' % (count, s(count))1669 retry_interval = timestr_to_secs(retry_interval)1670 while True:1671 try:1672 return self.run_keyword(name, *args)1673 except ExecutionFailed as err:1674 if err.dont_continue:1675 raise1676 count -= 11677 if time.time() > maxtime > 0 or count == 0:1678 raise AssertionError("Keyword '%s' failed after retrying "1679 "%s. The last error was: %s"1680 % (name, message, err))1681 self._sleep_in_parts(retry_interval)1682 @run_keyword_variant(resolve=1)1683 def set_variable_if(self, condition, *values):1684 """Sets variable based on the given condition.1685 The basic usage is giving a condition and two values. The1686 given condition is first evaluated the same way as with the1687 `Should Be True` keyword. If the condition is true, then the1688 first value is returned, and otherwise the second value is1689 returned. The second value can also be omitted, in which case1690 it has a default value None. This usage is illustrated in the1691 examples below, where ``${rc}`` is assumed to be zero.1692 | ${var1} = | Set Variable If | ${rc} == 0 | zero | nonzero |1693 | ${var2} = | Set Variable If | ${rc} > 0 | value1 | value2 |1694 | ${var3} = | Set Variable If | ${rc} > 0 | whatever | |1695 =>1696 | ${var1} = 'zero'1697 | ${var2} = 'value2'1698 | ${var3} = None1699 It is also possible to have 'else if' support by replacing the1700 second value with another condition, and having two new values1701 after it. If the first condition is not true, the second is1702 evaluated and one of the values after it is returned based on1703 its truth value. This can be continued by adding more1704 conditions without a limit.1705 | ${var} = | Set Variable If | ${rc} == 0 | zero |1706 | ... | ${rc} > 0 | greater than zero | less then zero |1707 | |1708 | ${var} = | Set Variable If |1709 | ... | ${rc} == 0 | zero |1710 | ... | ${rc} == 1 | one |1711 | ... | ${rc} == 2 | two |1712 | ... | ${rc} > 2 | greater than two |1713 | ... | ${rc} < 0 | less than zero |1714 Use `Get Variable Value` if you need to set variables1715 dynamically based on whether a variable exist or not.1716 """1717 values = self._verify_values_for_set_variable_if(list(values))1718 if self._is_true(condition):1719 return self._variables.replace_scalar(values[0])1720 values = self._verify_values_for_set_variable_if(values[1:], True)1721 if len(values) == 1:1722 return self._variables.replace_scalar(values[0])1723 return self.run_keyword('BuiltIn.Set Variable If', *values[0:])1724 def _verify_values_for_set_variable_if(self, values, default=False):1725 if not values:1726 if default:1727 return [None]1728 raise RuntimeError('At least one value is required')1729 if is_list_var(values[0]):1730 values[:1] = [escape(item) for item in self._variables[values[0]]]1731 return self._verify_values_for_set_variable_if(values)1732 return values1733 @run_keyword_variant(resolve=1)1734 def run_keyword_if_test_failed(self, name, *args):1735 """Runs the given keyword with the given arguments, if the test failed.1736 This keyword can only be used in a test teardown. Trying to use it1737 anywhere else results in an error.1738 Otherwise, this keyword works exactly like `Run Keyword`, see its1739 documentation for more details.1740 Prior to Robot Framework 2.9 failures in test teardown itself were1741 not detected by this keyword.1742 """1743 test = self._get_test_in_teardown('Run Keyword If Test Failed')1744 if not test.passed:1745 return self.run_keyword(name, *args)1746 @run_keyword_variant(resolve=1)1747 def run_keyword_if_test_passed(self, name, *args):1748 """Runs the given keyword with the given arguments, if the test passed.1749 This keyword can only be used in a test teardown. Trying to use it1750 anywhere else results in an error.1751 Otherwise, this keyword works exactly like `Run Keyword`, see its1752 documentation for more details.1753 Prior to Robot Framework 2.9 failures in test teardown itself were1754 not detected by this keyword.1755 """1756 test = self._get_test_in_teardown('Run Keyword If Test Passed')1757 if test.passed:1758 return self.run_keyword(name, *args)1759 @run_keyword_variant(resolve=1)1760 def run_keyword_if_timeout_occurred(self, name, *args):1761 """Runs the given keyword if either a test or a keyword timeout has occurred.1762 This keyword can only be used in a test teardown. Trying to use it1763 anywhere else results in an error.1764 Otherwise, this keyword works exactly like `Run Keyword`, see its1765 documentation for more details.1766 """1767 self._get_test_in_teardown('Run Keyword If Timeout Occurred')1768 if self._context.timeout_occurred:1769 return self.run_keyword(name, *args)1770 def _get_test_in_teardown(self, kwname):1771 ctx = self._context1772 if ctx.test and ctx.in_test_teardown:1773 return ctx.test1774 raise RuntimeError("Keyword '%s' can only be used in test teardown."1775 % kwname)1776 @run_keyword_variant(resolve=1)1777 def run_keyword_if_all_critical_tests_passed(self, name, *args):1778 """Runs the given keyword with the given arguments, if all critical tests passed.1779 This keyword can only be used in suite teardown. Trying to use it in1780 any other place will result in an error.1781 Otherwise, this keyword works exactly like `Run Keyword`, see its1782 documentation for more details.1783 """1784 suite = self._get_suite_in_teardown('Run Keyword If '1785 'All Critical Tests Passed')1786 if suite.statistics.critical.failed == 0:1787 return self.run_keyword(name, *args)1788 @run_keyword_variant(resolve=1)1789 def run_keyword_if_any_critical_tests_failed(self, name, *args):1790 """Runs the given keyword with the given arguments, if any critical tests failed.1791 This keyword can only be used in a suite teardown. Trying to use it1792 anywhere else results in an error.1793 Otherwise, this keyword works exactly like `Run Keyword`, see its1794 documentation for more details.1795 """1796 suite = self._get_suite_in_teardown('Run Keyword If '1797 'Any Critical Tests Failed')1798 if suite.statistics.critical.failed > 0:1799 return self.run_keyword(name, *args)1800 @run_keyword_variant(resolve=1)1801 def run_keyword_if_all_tests_passed(self, name, *args):1802 """Runs the given keyword with the given arguments, if all tests passed.1803 This keyword can only be used in a suite teardown. Trying to use it1804 anywhere else results in an error.1805 Otherwise, this keyword works exactly like `Run Keyword`, see its1806 documentation for more details.1807 """1808 suite = self._get_suite_in_teardown('Run Keyword If All Tests Passed')1809 if suite.statistics.all.failed == 0:1810 return self.run_keyword(name, *args)1811 @run_keyword_variant(resolve=1)1812 def run_keyword_if_any_tests_failed(self, name, *args):1813 """Runs the given keyword with the given arguments, if one or more tests failed.1814 This keyword can only be used in a suite teardown. Trying to use it1815 anywhere else results in an error.1816 Otherwise, this keyword works exactly like `Run Keyword`, see its1817 documentation for more details.1818 """1819 suite = self._get_suite_in_teardown('Run Keyword If Any Tests Failed')1820 if suite.statistics.all.failed > 0:1821 return self.run_keyword(name, *args)1822 def _get_suite_in_teardown(self, kwname):1823 if not self._context.in_suite_teardown:1824 raise RuntimeError("Keyword '%s' can only be used in suite teardown."1825 % kwname)1826 return self._context.suite1827class _Control(_BuiltInBase):1828 def continue_for_loop(self):1829 """Skips the current for loop iteration and continues from the next.1830 Skips the remaining keywords in the current for loop iteration and1831 continues from the next one. Can be used directly in a for loop or1832 in a keyword that the loop uses.1833 Example:1834 | :FOR | ${var} | IN | @{VALUES} |1835 | | Run Keyword If | '${var}' == 'CONTINUE' | Continue For Loop |1836 | | Do Something | ${var} |1837 See `Continue For Loop If` to conditionally continue a for loop without1838 using `Run Keyword If` or other wrapper keywords.1839 New in Robot Framework 2.8.1840 """1841 self.log("Continuing for loop from the next iteration.")1842 raise ContinueForLoop()1843 def continue_for_loop_if(self, condition):1844 """Skips the current for loop iteration if the ``condition`` is true.1845 A wrapper for `Continue For Loop` to continue a for loop based on1846 the given condition. The condition is evaluated using the same1847 semantics as with `Should Be True` keyword.1848 Example:1849 | :FOR | ${var} | IN | @{VALUES} |1850 | | Continue For Loop If | '${var}' == 'CONTINUE' |1851 | | Do Something | ${var} |1852 New in Robot Framework 2.8.1853 """1854 if self._is_true(condition):1855 self.continue_for_loop()1856 def exit_for_loop(self):1857 """Stops executing the enclosing for loop.1858 Exits the enclosing for loop and continues execution after it.1859 Can be used directly in a for loop or in a keyword that the loop uses.1860 Example:1861 | :FOR | ${var} | IN | @{VALUES} |1862 | | Run Keyword If | '${var}' == 'EXIT' | Exit For Loop |1863 | | Do Something | ${var} |1864 See `Exit For Loop If` to conditionally exit a for loop without1865 using `Run Keyword If` or other wrapper keywords.1866 """1867 self.log("Exiting for loop altogether.")1868 raise ExitForLoop()1869 def exit_for_loop_if(self, condition):1870 """Stops executing the enclosing for loop if the ``condition`` is true.1871 A wrapper for `Exit For Loop` to exit a for loop based on1872 the given condition. The condition is evaluated using the same1873 semantics as with `Should Be True` keyword.1874 Example:1875 | :FOR | ${var} | IN | @{VALUES} |1876 | | Exit For Loop If | '${var}' == 'EXIT' |1877 | | Do Something | ${var} |1878 New in Robot Framework 2.8.1879 """1880 if self._is_true(condition):1881 self.exit_for_loop()1882 @run_keyword_variant(resolve=0)1883 def return_from_keyword(self, *return_values):1884 """Returns from the enclosing user keyword.1885 This keyword can be used to return from a user keyword with PASS status1886 without executing it fully. It is also possible to return values1887 similarly as with the ``[Return]`` setting. For more detailed information1888 about working with the return values, see the User Guide.1889 This keyword is typically wrapped to some other keyword, such as1890 `Run Keyword If` or `Run Keyword If Test Passed`, to return based1891 on a condition:1892 | Run Keyword If | ${rc} < 0 | Return From Keyword |1893 | Run Keyword If Test Passed | Return From Keyword |1894 It is possible to use this keyword to return from a keyword also inside1895 a for loop. That, as well as returning values, is demonstrated by the1896 `Find Index` keyword in the following somewhat advanced example.1897 Notice that it is often a good idea to move this kind of complicated1898 logic into a test library.1899 | ***** Variables *****1900 | @{LIST} = foo baz1901 |1902 | ***** Test Cases *****1903 | Example1904 | ${index} = Find Index baz @{LIST}1905 | Should Be Equal ${index} ${1}1906 | ${index} = Find Index non existing @{LIST}1907 | Should Be Equal ${index} ${-1}1908 |1909 | ***** Keywords *****1910 | Find Index1911 | [Arguments] ${element} @{items}1912 | ${index} = Set Variable ${0}1913 | :FOR ${item} IN @{items}1914 | \\ Run Keyword If '${item}' == '${element}' Return From Keyword ${index}1915 | \\ ${index} = Set Variable ${index + 1}1916 | Return From Keyword ${-1} # Also [Return] would work here.1917 The most common use case, returning based on an expression, can be1918 accomplished directly with `Return From Keyword If`. Both of these1919 keywords are new in Robot Framework 2.8.1920 See also `Run Keyword And Return` and `Run Keyword And Return If`.1921 """1922 self.log('Returning from the enclosing user keyword.')1923 raise ReturnFromKeyword(return_values)1924 @run_keyword_variant(resolve=1)1925 def return_from_keyword_if(self, condition, *return_values):1926 """Returns from the enclosing user keyword if ``condition`` is true.1927 A wrapper for `Return From Keyword` to return based on the given1928 condition. The condition is evaluated using the same semantics as1929 with `Should Be True` keyword.1930 Given the same example as in `Return From Keyword`, we can rewrite the1931 `Find Index` keyword as follows:1932 | ***** Keywords *****1933 | Find Index1934 | [Arguments] ${element} @{items}1935 | ${index} = Set Variable ${0}1936 | :FOR ${item} IN @{items}1937 | \\ Return From Keyword If '${item}' == '${element}' ${index}1938 | \\ ${index} = Set Variable ${index + 1}1939 | Return From Keyword ${-1} # Also [Return] would work here.1940 See also `Run Keyword And Return` and `Run Keyword And Return If`.1941 New in Robot Framework 2.8.1942 """1943 if self._is_true(condition):1944 self.return_from_keyword(*return_values)1945 @run_keyword_variant(resolve=1)1946 def run_keyword_and_return(self, name, *args):1947 """Runs the specified keyword and returns from the enclosing user keyword.1948 The keyword to execute is defined with ``name`` and ``*args`` exactly1949 like with `Run Keyword`. After running the keyword, returns from the1950 enclosing user keyword and passes possible return value from the1951 executed keyword further. Returning from a keyword has exactly same1952 semantics as with `Return From Keyword`.1953 Example:1954 | `Run Keyword And Return` | `My Keyword` | arg1 | arg2 |1955 | # Above is equivalent to: |1956 | ${result} = | `My Keyword` | arg1 | arg2 |1957 | `Return From Keyword` | ${result} | | |1958 Use `Run Keyword And Return If` if you want to run keyword and return1959 based on a condition.1960 New in Robot Framework 2.8.2.1961 """1962 ret = self.run_keyword(name, *args)1963 self.return_from_keyword(escape(ret))1964 @run_keyword_variant(resolve=2)1965 def run_keyword_and_return_if(self, condition, name, *args):1966 """Runs the specified keyword and returns from the enclosing user keyword.1967 A wrapper for `Run Keyword And Return` to run and return based on1968 the given ``condition``. The condition is evaluated using the same1969 semantics as with `Should Be True` keyword.1970 Example:1971 | `Run Keyword And Return If` | ${rc} > 0 | `My Keyword` | arg1 | arg2 |1972 | # Above is equivalent to: |1973 | `Run Keyword If` | ${rc} > 0 | `Run Keyword And Return` | `My Keyword ` | arg1 | arg2 |1974 Use `Return From Keyword If` if you want to return a certain value1975 based on a condition.1976 New in Robot Framework 2.8.2.1977 """1978 if self._is_true(condition):1979 self.run_keyword_and_return(name, *args)1980 def pass_execution(self, message, *tags):1981 """Skips rest of the current test, setup, or teardown with PASS status.1982 This keyword can be used anywhere in the test data, but the place where1983 used affects the behavior:1984 - When used in any setup or teardown (suite, test or keyword), passes1985 that setup or teardown. Possible keyword teardowns of the started1986 keywords are executed. Does not affect execution or statuses1987 otherwise.1988 - When used in a test outside setup or teardown, passes that particular1989 test case. Possible test and keyword teardowns are executed.1990 Possible continuable failures before this keyword is used, as well as1991 failures in executed teardowns, will fail the execution.1992 It is mandatory to give a message explaining why execution was passed.1993 By default the message is considered plain text, but starting it with1994 ``*HTML*`` allows using HTML formatting.1995 It is also possible to modify test tags passing tags after the message1996 similarly as with `Fail` keyword. Tags starting with a hyphen1997 (e.g. ``-regression``) are removed and others added. Tags are modified1998 using `Set Tags` and `Remove Tags` internally, and the semantics1999 setting and removing them are the same as with these keywords.2000 Examples:2001 | Pass Execution | All features available in this version tested. |2002 | Pass Execution | Deprecated test. | deprecated | -regression |2003 This keyword is typically wrapped to some other keyword, such as2004 `Run Keyword If`, to pass based on a condition. The most common case2005 can be handled also with `Pass Execution If`:2006 | Run Keyword If | ${rc} < 0 | Pass Execution | Negative values are cool. |2007 | Pass Execution If | ${rc} < 0 | Negative values are cool. |2008 Passing execution in the middle of a test, setup or teardown should be2009 used with care. In the worst case it leads to tests that skip all the2010 parts that could actually uncover problems in the tested application.2011 In cases where execution cannot continue do to external factors,2012 it is often safer to fail the test case and make it non-critical.2013 New in Robot Framework 2.8.2014 """2015 message = message.strip()2016 if not message:2017 raise RuntimeError('Message cannot be empty.')2018 self._set_and_remove_tags(tags)2019 log_message, level = self._get_logged_test_message_and_level(message)2020 self.log('Execution passed with message:\n%s' % log_message, level)2021 raise PassExecution(message)2022 @run_keyword_variant(resolve=1)2023 def pass_execution_if(self, condition, message, *tags):2024 """Conditionally skips rest of the current test, setup, or teardown with PASS status.2025 A wrapper for `Pass Execution` to skip rest of the current test,2026 setup or teardown based the given ``condition``. The condition is2027 evaluated similarly as with `Should Be True` keyword, and ``message``2028 and ``*tags`` have same semantics as with `Pass Execution`.2029 Example:2030 | :FOR | ${var} | IN | @{VALUES} |2031 | | Pass Execution If | '${var}' == 'EXPECTED' | Correct value was found |2032 | | Do Something | ${var} |2033 New in Robot Framework 2.8.2034 """2035 if self._is_true(condition):2036 message = self._variables.replace_string(message)2037 tags = self._variables.replace_list(tags)2038 self.pass_execution(message, *tags)2039class _Misc(_BuiltInBase):2040 def no_operation(self):2041 """Does absolutely nothing."""2042 def sleep(self, time_, reason=None):2043 """Pauses the test executed for the given time.2044 ``time`` may be either a number or a time string. Time strings are in2045 a format such as ``1 day 2 hours 3 minutes 4 seconds 5milliseconds`` or2046 ``1d 2h 3m 4s 5ms``, and they are fully explained in an appendix of2047 Robot Framework User Guide. Optional `reason` can be used to explain why2048 sleeping is necessary. Both the time slept and the reason are logged.2049 Examples:2050 | Sleep | 42 |2051 | Sleep | 1.5 |2052 | Sleep | 2 minutes 10 seconds |2053 | Sleep | 10s | Wait for a reply |2054 """2055 seconds = timestr_to_secs(time_)2056 # Python hangs with negative values2057 if seconds < 0:2058 seconds = 02059 self._sleep_in_parts(seconds)2060 self.log('Slept %s' % secs_to_timestr(seconds))2061 if reason:2062 self.log(reason)2063 def _sleep_in_parts(self, seconds):2064 # time.sleep can't be stopped in windows2065 # to ensure that we can signal stop (with timeout)2066 # split sleeping to small pieces2067 endtime = time.time() + float(seconds)2068 while True:2069 remaining = endtime - time.time()2070 if remaining <= 0:2071 break2072 time.sleep(min(remaining, 0.01))2073 def catenate(self, *items):2074 """Catenates the given items together and returns the resulted string.2075 By default, items are catenated with spaces, but if the first item2076 contains the string ``SEPARATOR=<sep>``, the separator ``<sep>`` is2077 used instead. Items are converted into strings when necessary.2078 Examples:2079 | ${str1} = | Catenate | Hello | world | |2080 | ${str2} = | Catenate | SEPARATOR=--- | Hello | world |2081 | ${str3} = | Catenate | SEPARATOR= | Hello | world |2082 =>2083 | ${str1} = 'Hello world'2084 | ${str2} = 'Hello---world'2085 | ${str3} = 'Helloworld'2086 """2087 if not items:2088 return ''2089 items = [unic(item) for item in items]2090 if items[0].startswith('SEPARATOR='):2091 sep = items[0][len('SEPARATOR='):]2092 items = items[1:]2093 else:2094 sep = ' '2095 return sep.join(items)2096 def log(self, message, level='INFO', html=False, console=False, repr=False):2097 u"""Logs the given message with the given level.2098 Valid levels are TRACE, DEBUG, INFO (default), HTML, WARN, and ERROR.2099 Messages below the current active log level are ignored. See2100 `Set Log Level` keyword and ``--loglevel`` command line option2101 for more details about setting the level.2102 Messages logged with the WARN or ERROR levels will be automatically2103 visible also in the console and in the Test Execution Errors section2104 in the log file.2105 Logging can be configured using optional ``html``, ``console`` and2106 ``repr`` arguments. They are off by default, but can be enabled2107 by giving them a true value. See `Boolean arguments` section for more2108 information about true and false values.2109 If the ``html`` argument is given a true value, the message will be2110 considered HTML and special characters such as ``<`` in it are not2111 escaped. For example, logging ``<img src="image.png">`` creates an2112 image when ``html`` is true, but otherwise the message is that exact2113 string. An alternative to using the ``html`` argument is using the HTML2114 pseudo log level. It logs the message as HTML using the INFO level.2115 If the ``console`` argument is true, the message will be written to2116 the console where test execution was started from in addition to2117 the log file. This keyword always uses the standard output stream2118 and adds a newline after the written message. Use `Log To Console`2119 instead if either of these is undesirable,2120 If the ``repr`` argument is true, the given item will be passed through2121 a custom version of Python's ``pprint.pformat()`` function before2122 logging it. This is useful, for example, when working with strings or2123 bytes containing invisible characters, or when working with nested data2124 structures. The custom version differs from the standard one so that it2125 omits the ``u`` prefix from Unicode strings and adds ``b`` prefix to2126 byte strings.2127 Examples:2128 | Log | Hello, world! | | | # Normal INFO message. |2129 | Log | Warning, world! | WARN | | # Warning. |2130 | Log | <b>Hello</b>, world! | html=yes | | # INFO message as HTML. |2131 | Log | <b>Hello</b>, world! | HTML | | # Same as above. |2132 | Log | <b>Hello</b>, world! | DEBUG | html=true | # DEBUG as HTML. |2133 | Log | Hello, console! | console=yes | | # Log also to the console. |2134 | Log | Hyv\xe4 \\x00 | repr=yes | | # Log ``'Hyv\\xe4 \\x00'``. |2135 See `Log Many` if you want to log multiple messages in one go, and2136 `Log To Console` if you only want to write to the console.2137 Arguments ``html``, ``console``, and ``repr`` are new in Robot Framework2138 2.8.2.2139 Pprint support when ``repr`` is used is new in Robot Framework 2.8.6,2140 and it was changed to drop the ``u`` prefix and add the ``b`` prefix2141 in Robot Framework 2.9.2142 """2143 if is_truthy(repr):2144 message = prepr(message, width=80)2145 logger.write(message, level, is_truthy(html))2146 if is_truthy(console):2147 logger.console(message)2148 @run_keyword_variant(resolve=0)2149 def log_many(self, *messages):2150 """Logs the given messages as separate entries using the INFO level.2151 Supports also logging list and dictionary variable items individually.2152 Examples:2153 | Log Many | Hello | ${var} |2154 | Log Many | @{list} | &{dict} |2155 See `Log` and `Log To Console` keywords if you want to use alternative2156 log levels, use HTML, or log to the console.2157 """2158 for msg in self._yield_logged_messages(messages):2159 self.log(msg)2160 def _yield_logged_messages(self, messages):2161 for msg in messages:2162 var = VariableSplitter(msg)2163 value = self._variables.replace_scalar(msg)2164 if var.is_list_variable():2165 for item in value:2166 yield item2167 elif var.is_dict_variable():2168 for name, value in value.items():2169 yield '%s=%s' % (name, value)2170 else:2171 yield value2172 def log_to_console(self, message, stream='STDOUT', no_newline=False):2173 """Logs the given message to the console.2174 By default uses the standard output stream. Using the standard error2175 stream is possibly by giving the ``stream`` argument value ``STDERR``2176 (case-insensitive).2177 By default appends a newline to the logged message. This can be2178 disabled by giving the ``no_newline`` argument a true value (see2179 `Boolean arguments`).2180 Examples:2181 | Log To Console | Hello, console! | |2182 | Log To Console | Hello, stderr! | STDERR |2183 | Log To Console | Message starts here and is | no_newline=true |2184 | Log To Console | continued without newline. | |2185 This keyword does not log the message to the normal log file. Use2186 `Log` keyword, possibly with argument ``console``, if that is desired.2187 New in Robot Framework 2.8.2.2188 """2189 logger.console(message, newline=is_falsy(no_newline), stream=stream)2190 @run_keyword_variant(resolve=0)2191 def comment(self, *messages):2192 """Displays the given messages in the log file as keyword arguments.2193 This keyword does nothing with the arguments it receives, but as they2194 are visible in the log, this keyword can be used to display simple2195 messages. Given arguments are ignored so thoroughly that they can even2196 contain non-existing variables. If you are interested about variable2197 values, you can use the `Log` or `Log Many` keywords.2198 """2199 pass2200 def set_log_level(self, level):2201 """Sets the log threshold to the specified level and returns the old level.2202 Messages below the level will not logged. The default logging level is2203 INFO, but it can be overridden with the command line option2204 ``--loglevel``.2205 The available levels: TRACE, DEBUG, INFO (default), WARN, ERROR and NONE (no2206 logging).2207 """2208 try:2209 old = self._context.output.set_log_level(level)2210 except DataError as err:2211 raise RuntimeError(unic(err))2212 self._namespace.variables.set_global('${LOG_LEVEL}', level.upper())2213 self.log('Log level changed from %s to %s.' % (old, level.upper()))2214 return old2215 def reload_library(self, name_or_instance):2216 """Rechecks what keywords the specified library provides.2217 Can be called explicitly in the test data or by a library itself2218 when keywords it provides have changed.2219 The library can be specified by its name or as the active instance of2220 the library. The latter is especially useful if the library itself2221 calls this keyword as a method.2222 New in Robot Framework 2.9.2223 """2224 library = self._namespace.reload_library(name_or_instance)2225 self.log('Reloaded library %s with %s keywords.' % (library.name,2226 len(library)))2227 @run_keyword_variant(resolve=0)2228 def import_library(self, name, *args):2229 """Imports a library with the given name and optional arguments.2230 This functionality allows dynamic importing of libraries while tests2231 are running. That may be necessary, if the library itself is dynamic2232 and not yet available when test data is processed. In a normal case,2233 libraries should be imported using the Library setting in the Setting2234 table.2235 This keyword supports importing libraries both using library2236 names and physical paths. When paths are used, they must be2237 given in absolute format or found from2238 [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#pythonpath-jythonpath-and-ironpythonpath|2239 search path]. Forward slashes can be used as path separators in all2240 operating systems.2241 It is possible to pass arguments to the imported library and also2242 named argument syntax works if the library supports it. ``WITH NAME``2243 syntax can be used to give a custom name to the imported library.2244 Examples:2245 | Import Library | MyLibrary |2246 | Import Library | ${CURDIR}/../Library.py | arg1 | named=arg2 |2247 | Import Library | ${LIBRARIES}/Lib.java | arg | WITH NAME | JavaLib |2248 """2249 try:2250 self._namespace.import_library(name, list(args))2251 except DataError as err:2252 raise RuntimeError(unic(err))2253 @run_keyword_variant(resolve=0)2254 def import_variables(self, path, *args):2255 """Imports a variable file with the given path and optional arguments.2256 Variables imported with this keyword are set into the test suite scope2257 similarly when importing them in the Setting table using the Variables2258 setting. These variables override possible existing variables with2259 the same names. This functionality can thus be used to import new2260 variables, for example, for each test in a test suite.2261 The given path must be absolute or found from2262 [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#pythonpath-jythonpath-and-ironpythonpath|2263 search path]. Forward slashes can be used as path separator regardless2264 the operating system.2265 Examples:2266 | Import Variables | ${CURDIR}/variables.py | | |2267 | Import Variables | ${CURDIR}/../vars/env.py | arg1 | arg2 |2268 | Import Variables | file_from_pythonpath.py | | |2269 """2270 try:2271 self._namespace.import_variables(path, list(args), overwrite=True)2272 except DataError as err:2273 raise RuntimeError(unic(err))2274 @run_keyword_variant(resolve=0)2275 def import_resource(self, path):2276 """Imports a resource file with the given path.2277 Resources imported with this keyword are set into the test suite scope2278 similarly when importing them in the Setting table using the Resource2279 setting.2280 The given path must be absolute or found from2281 [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#pythonpath-jythonpath-and-ironpythonpath|2282 search path]. Forward slashes can be used as path separator regardless2283 the operating system.2284 Examples:2285 | Import Resource | ${CURDIR}/resource.txt |2286 | Import Resource | ${CURDIR}/../resources/resource.html |2287 | Import Resource | found_from_pythonpath.robot |2288 """...
keywords.py
Source:keywords.py
...27 if show_intro:28 print_output('\n>>>>>', 'Exit shell.')29 # put stdout back where it was30 sys.stdout = old_stdout31 @run_keyword_variant(resolve=1)32 def debug_if(self, condition, *args):33 """Runs the Debug keyword if condition is true."""34 return run_debug_if(condition, *args)35 def get_remote_url(self):36 """Get selenium URL for connecting to remote WebDriver."""37 return get_remote_url()38 def get_session_id(self):39 """Get selenium browser session id."""40 return get_session_id()41 def get_webdriver_remote(self):42 """Print the way connecting to remote selenium server."""...
MyLib2.py
Source:MyLib2.py
...3from robot.running import Keyword, RUN_KW_REGISTER4from enum import Enum567def run_keyword_variant(resolve):8 def decorator(method):9 RUN_KW_REGISTER.register_run_keyword('MyLib2', method.__name__, resolve, deprecation_warning=False)10 return method11 return decorator121314class MyLib2:15 '''*Das ist meine Doku*1617 Hier weitere Doku18 '''1920 ROBOT_LIBRARY_SCOPE = 'GLOBAL'2122 def __init__(self, ip='127.0.0.1'):23 """Das ding hat ne Option IP"""24 self.ip = ip2526 def my_keyword(self):27 """Das ist ein keyword"""28 logger.debug('Hello World')2930 def hello_someone(self, name: str):31 print(f'Hello {name}')3233 @run_keyword_variant(resolve=0)34 def set_my_var(self, var, val):35 BuiltIn().set_test_variable(var, val)
...
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!