diff options
author | iddqd <iddqd@yandex-team.com> | 2024-05-13 17:19:30 +0300 |
---|---|---|
committer | iddqd <iddqd@yandex-team.com> | 2024-05-13 17:28:44 +0300 |
commit | 84d127b9b7e96ba4352e3f5ddc9222aee9a66053 (patch) | |
tree | 2ebb2689abf65e68dfe92a3ca9b161b4b6ae183f /contrib/python/allure-python-commons/allure_commons/utils.py | |
parent | b7deb7f0b71db7419781d1b0357dfa443ccc3ff1 (diff) | |
download | ydb-84d127b9b7e96ba4352e3f5ddc9222aee9a66053.tar.gz |
Add allure support to ydb github export
d6cba27d09fb5e50a99c36070a6a3545c8393ea1
Diffstat (limited to 'contrib/python/allure-python-commons/allure_commons/utils.py')
-rw-r--r-- | contrib/python/allure-python-commons/allure_commons/utils.py | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/contrib/python/allure-python-commons/allure_commons/utils.py b/contrib/python/allure-python-commons/allure_commons/utils.py new file mode 100644 index 0000000000..5ba0d3775b --- /dev/null +++ b/contrib/python/allure-python-commons/allure_commons/utils.py @@ -0,0 +1,385 @@ +import os +import string +import sys +import time +import uuid +import json +import socket +import inspect +import hashlib +import platform +import threading +import traceback +import collections + +from traceback import format_exception_only + + +def md5(*args): + m = hashlib.md5() + for arg in args: + if not isinstance(arg, bytes): + if not isinstance(arg, str): + arg = repr(arg) + arg = arg.encode('utf-8') + m.update(arg) + return m.hexdigest() + + +def uuid4(): + return str(uuid.uuid4()) + + +def now(): + return int(round(1000 * time.time())) + + +def platform_label(): + major_version, *_ = platform.python_version_tuple() + implementation = platform.python_implementation().lower() + return f'{implementation}{major_version}' + + +def thread_tag(): + return '{0}-{1}'.format(os.getpid(), threading.current_thread().name) + + +def host_tag(): + return socket.gethostname() + + +def represent(item): + """ + >>> represent(None) + 'None' + + >>> represent(123) + '123' + + >>> represent('hi') + "'hi'" + + >>> represent('привет') + "'привет'" + + >>> represent(bytearray([0xd0, 0xbf])) # doctest: +ELLIPSIS + "<... 'bytearray'>" + + >>> from struct import pack + >>> represent(pack('h', 0x89)) + "<class 'bytes'>" + + >>> represent(int) + "<class 'int'>" + + >>> represent(represent) # doctest: +ELLIPSIS + '<function represent at ...>' + + >>> represent([represent]) # doctest: +ELLIPSIS + '[<function represent at ...>]' + + >>> class ClassWithName: + ... pass + + >>> represent(ClassWithName) + "<class 'utils.ClassWithName'>" + """ + + if isinstance(item, str): + return f"'{item}'" + elif isinstance(item, (bytes, bytearray)): + return repr(type(item)) + else: + return repr(item) + + +def func_parameters(func, *args, **kwargs): + """ + >>> def helper(func): + ... def wrapper(*args, **kwargs): + ... params = func_parameters(func, *args, **kwargs) + ... print(list(params.items())) + ... return func(*args, **kwargs) + ... return wrapper + + >>> @helper + ... def args(a, b): + ... pass + + >>> args(1, 2) + [('a', '1'), ('b', '2')] + + >>> args(*(1,2)) + [('a', '1'), ('b', '2')] + + >>> args(1, b=2) + [('a', '1'), ('b', '2')] + + >>> @helper + ... def kwargs(a=1, b=2): + ... pass + + >>> kwargs() + [('a', '1'), ('b', '2')] + + >>> kwargs(a=3, b=4) + [('a', '3'), ('b', '4')] + + >>> kwargs(b=4, a=3) + [('a', '3'), ('b', '4')] + + >>> kwargs(a=3) + [('a', '3'), ('b', '2')] + + >>> kwargs(b=4) + [('a', '1'), ('b', '4')] + + >>> @helper + ... def args_kwargs(a, b, c=3, d=4): + ... pass + + >>> args_kwargs(1, 2) + [('a', '1'), ('b', '2'), ('c', '3'), ('d', '4')] + + >>> args_kwargs(1, 2, d=5) + [('a', '1'), ('b', '2'), ('c', '3'), ('d', '5')] + + >>> args_kwargs(1, 2, 5, 6) + [('a', '1'), ('b', '2'), ('c', '5'), ('d', '6')] + + >>> args_kwargs(1, b=2) + [('a', '1'), ('b', '2'), ('c', '3'), ('d', '4')] + + >>> @helper + ... def varargs(*a): + ... pass + + >>> varargs() + [] + + >>> varargs(1, 2) + [('a', '(1, 2)')] + + >>> @helper + ... def keywords(**a): + ... pass + + >>> keywords() + [] + + >>> keywords(a=1, b=2) + [('a', '1'), ('b', '2')] + + >>> @helper + ... def args_varargs(a, b, *c): + ... pass + + >>> args_varargs(1, 2) + [('a', '1'), ('b', '2')] + + >>> args_varargs(1, 2, 2) + [('a', '1'), ('b', '2'), ('c', '(2,)')] + + >>> @helper + ... def args_kwargs_varargs(a, b, c=3, **d): + ... pass + + >>> args_kwargs_varargs(1, 2) + [('a', '1'), ('b', '2'), ('c', '3')] + + >>> args_kwargs_varargs(1, 2, 4, d=5, e=6) + [('a', '1'), ('b', '2'), ('c', '4'), ('d', '5'), ('e', '6')] + + >>> @helper + ... def args_kwargs_varargs_keywords(a, b=2, *c, **d): + ... pass + + >>> args_kwargs_varargs_keywords(1) + [('a', '1'), ('b', '2')] + + >>> args_kwargs_varargs_keywords(1, 2, 4, d=5, e=6) + [('a', '1'), ('b', '2'), ('c', '(4,)'), ('d', '5'), ('e', '6')] + + >>> class Class: + ... @staticmethod + ... @helper + ... def static_args(a, b): + ... pass + ... + ... @classmethod + ... @helper + ... def method_args(cls, a, b): + ... pass + ... + ... @helper + ... def args(self, a, b): + ... pass + + >>> cls = Class() + + >>> cls.args(1, 2) + [('a', '1'), ('b', '2')] + + >>> cls.method_args(1, 2) + [('a', '1'), ('b', '2')] + + >>> cls.static_args(1, 2) + [('a', '1'), ('b', '2')] + + """ + parameters = {} + arg_spec = inspect.getfullargspec(func) + arg_order = list(arg_spec.args) + args_dict = dict(zip(arg_spec.args, args)) + + if arg_spec.defaults: + kwargs_defaults_dict = dict(zip(arg_spec.args[-len(arg_spec.defaults):], arg_spec.defaults)) + parameters.update(kwargs_defaults_dict) + + if arg_spec.varargs: + arg_order.append(arg_spec.varargs) + varargs = args[len(arg_spec.args):] + parameters.update({arg_spec.varargs: varargs} if varargs else {}) + + if arg_spec.args and arg_spec.args[0] in ['cls', 'self']: + args_dict.pop(arg_spec.args[0], None) + + if kwargs: + if sys.version_info < (3, 7): + # Sort alphabetically as old python versions does + # not preserve call order for kwargs. + arg_order.extend(sorted(list(kwargs.keys()))) + else: + # Keep py3.7 behaviour to preserve kwargs order + arg_order.extend(list(kwargs.keys())) + parameters.update(kwargs) + + parameters.update(args_dict) + + items = parameters.items() + sorted_items = sorted( + map( + lambda kv: (kv[0], represent(kv[1])), + items + ), + key=lambda x: arg_order.index(x[0]) + ) + + return collections.OrderedDict(sorted_items) + + +def format_traceback(exc_traceback): + return ''.join(traceback.format_tb(exc_traceback)) if exc_traceback else None + + +def format_exception(etype, value): + """ + >>> import sys + + >>> try: + ... assert False, 'Привет' + ... except AssertionError: + ... etype, e, _ = sys.exc_info() + ... format_exception(etype, e) # doctest: +ELLIPSIS + 'AssertionError: ...\\n' + + >>> try: + ... assert False, 'Привет' + ... except AssertionError: + ... etype, e, _ = sys.exc_info() + ... format_exception(etype, e) # doctest: +ELLIPSIS + 'AssertionError: ...\\n' + + >>> try: + ... compile("bla 'Привет'", "fake.py", "exec") + ... except SyntaxError: + ... etype, e, _ = sys.exc_info() + ... format_exception(etype, e) # doctest: +ELLIPSIS + ' File "fake.py", line 1...SyntaxError: invalid syntax\\n' + + >>> try: + ... compile("bla 'Привет'", "fake.py", "exec") + ... except SyntaxError: + ... etype, e, _ = sys.exc_info() + ... format_exception(etype, e) # doctest: +ELLIPSIS + ' File "fake.py", line 1...SyntaxError: invalid syntax\\n' + + >>> from hamcrest import assert_that, equal_to + + >>> try: + ... assert_that('left', equal_to('right')) + ... except AssertionError: + ... etype, e, _ = sys.exc_info() + ... format_exception(etype, e) # doctest: +ELLIPSIS + "AssertionError: \\nExpected:...but:..." + + >>> try: + ... assert_that('left', equal_to('right')) + ... except AssertionError: + ... etype, e, _ = sys.exc_info() + ... format_exception(etype, e) # doctest: +ELLIPSIS + "AssertionError: \\nExpected:...but:..." + """ + return '\n'.join(format_exception_only(etype, value)) if etype or value else None + + +def get_testplan(): + planned_tests = [] + file_path = os.environ.get("ALLURE_TESTPLAN_PATH") + + if file_path and os.path.exists(file_path): + with open(file_path, 'r') as plan_file: + plan = json.load(plan_file) + planned_tests = plan.get("tests", []) + + return planned_tests + + +class SafeFormatter(string.Formatter): + """ + Format string safely - skip any non-passed keys + >>> f = SafeFormatter().format + + Make sure we don't broke default formatting behaviour + >>> f("literal string") + 'literal string' + >>> f("{expected.format}", expected=str) + "<method 'format' of 'str' objects>" + >>> f("{expected[0]}", expected=["value"]) + 'value' + >>> f("{expected[0]}", expected=123) + Traceback (most recent call last): + ... + TypeError: 'int' object is not subscriptable + >>> f("{expected[0]}", expected=[]) + Traceback (most recent call last): + ... + IndexError: list index out of range + >>> f("{expected.format}", expected=int) + Traceback (most recent call last): + ... + AttributeError: type object 'int' has no attribute 'format' + + Check that unexpected keys do not cause some errors + >>> f("{expected} {unexpected}", expected="value") + 'value {unexpected}' + >>> f("{unexpected[0]}", expected=["value"]) + '{unexpected[0]}' + >>> f("{unexpected.format}", expected=str) + '{unexpected.format}' + """ + + class SafeKeyOrIndexError(Exception): + pass + + def get_field(self, field_name, args, kwargs): + try: + return super().get_field(field_name, args, kwargs) + except self.SafeKeyOrIndexError: + return "{" + field_name + "}", field_name + + def get_value(self, key, args, kwargs): + try: + return super().get_value(key, args, kwargs) + except (KeyError, IndexError): + raise self.SafeKeyOrIndexError() |