utils.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. import os
  2. import string
  3. import sys
  4. import time
  5. import uuid
  6. import json
  7. import socket
  8. import inspect
  9. import hashlib
  10. import platform
  11. import threading
  12. import traceback
  13. import collections
  14. from traceback import format_exception_only
  15. def md5(*args):
  16. m = hashlib.md5()
  17. for arg in args:
  18. if not isinstance(arg, bytes):
  19. if not isinstance(arg, str):
  20. arg = repr(arg)
  21. arg = arg.encode('utf-8')
  22. m.update(arg)
  23. return m.hexdigest()
  24. def uuid4():
  25. return str(uuid.uuid4())
  26. def now():
  27. return int(round(1000 * time.time()))
  28. def platform_label():
  29. major_version, *_ = platform.python_version_tuple()
  30. implementation = platform.python_implementation().lower()
  31. return f'{implementation}{major_version}'
  32. def thread_tag():
  33. return '{0}-{1}'.format(os.getpid(), threading.current_thread().name)
  34. def host_tag():
  35. return socket.gethostname()
  36. def represent(item):
  37. """
  38. >>> represent(None)
  39. 'None'
  40. >>> represent(123)
  41. '123'
  42. >>> represent('hi')
  43. "'hi'"
  44. >>> represent('привет')
  45. "'привет'"
  46. >>> represent(bytearray([0xd0, 0xbf])) # doctest: +ELLIPSIS
  47. "<... 'bytearray'>"
  48. >>> from struct import pack
  49. >>> represent(pack('h', 0x89))
  50. "<class 'bytes'>"
  51. >>> represent(int)
  52. "<class 'int'>"
  53. >>> represent(represent) # doctest: +ELLIPSIS
  54. '<function represent at ...>'
  55. >>> represent([represent]) # doctest: +ELLIPSIS
  56. '[<function represent at ...>]'
  57. >>> class ClassWithName:
  58. ... pass
  59. >>> represent(ClassWithName)
  60. "<class 'utils.ClassWithName'>"
  61. """
  62. if isinstance(item, str):
  63. return f"'{item}'"
  64. elif isinstance(item, (bytes, bytearray)):
  65. return repr(type(item))
  66. else:
  67. return repr(item)
  68. def func_parameters(func, *args, **kwargs):
  69. """
  70. >>> def helper(func):
  71. ... def wrapper(*args, **kwargs):
  72. ... params = func_parameters(func, *args, **kwargs)
  73. ... print(list(params.items()))
  74. ... return func(*args, **kwargs)
  75. ... return wrapper
  76. >>> @helper
  77. ... def args(a, b):
  78. ... pass
  79. >>> args(1, 2)
  80. [('a', '1'), ('b', '2')]
  81. >>> args(*(1,2))
  82. [('a', '1'), ('b', '2')]
  83. >>> args(1, b=2)
  84. [('a', '1'), ('b', '2')]
  85. >>> @helper
  86. ... def kwargs(a=1, b=2):
  87. ... pass
  88. >>> kwargs()
  89. [('a', '1'), ('b', '2')]
  90. >>> kwargs(a=3, b=4)
  91. [('a', '3'), ('b', '4')]
  92. >>> kwargs(b=4, a=3)
  93. [('a', '3'), ('b', '4')]
  94. >>> kwargs(a=3)
  95. [('a', '3'), ('b', '2')]
  96. >>> kwargs(b=4)
  97. [('a', '1'), ('b', '4')]
  98. >>> @helper
  99. ... def args_kwargs(a, b, c=3, d=4):
  100. ... pass
  101. >>> args_kwargs(1, 2)
  102. [('a', '1'), ('b', '2'), ('c', '3'), ('d', '4')]
  103. >>> args_kwargs(1, 2, d=5)
  104. [('a', '1'), ('b', '2'), ('c', '3'), ('d', '5')]
  105. >>> args_kwargs(1, 2, 5, 6)
  106. [('a', '1'), ('b', '2'), ('c', '5'), ('d', '6')]
  107. >>> args_kwargs(1, b=2)
  108. [('a', '1'), ('b', '2'), ('c', '3'), ('d', '4')]
  109. >>> @helper
  110. ... def varargs(*a):
  111. ... pass
  112. >>> varargs()
  113. []
  114. >>> varargs(1, 2)
  115. [('a', '(1, 2)')]
  116. >>> @helper
  117. ... def keywords(**a):
  118. ... pass
  119. >>> keywords()
  120. []
  121. >>> keywords(a=1, b=2)
  122. [('a', '1'), ('b', '2')]
  123. >>> @helper
  124. ... def args_varargs(a, b, *c):
  125. ... pass
  126. >>> args_varargs(1, 2)
  127. [('a', '1'), ('b', '2')]
  128. >>> args_varargs(1, 2, 2)
  129. [('a', '1'), ('b', '2'), ('c', '(2,)')]
  130. >>> @helper
  131. ... def args_kwargs_varargs(a, b, c=3, **d):
  132. ... pass
  133. >>> args_kwargs_varargs(1, 2)
  134. [('a', '1'), ('b', '2'), ('c', '3')]
  135. >>> args_kwargs_varargs(1, 2, 4, d=5, e=6)
  136. [('a', '1'), ('b', '2'), ('c', '4'), ('d', '5'), ('e', '6')]
  137. >>> @helper
  138. ... def args_kwargs_varargs_keywords(a, b=2, *c, **d):
  139. ... pass
  140. >>> args_kwargs_varargs_keywords(1)
  141. [('a', '1'), ('b', '2')]
  142. >>> args_kwargs_varargs_keywords(1, 2, 4, d=5, e=6)
  143. [('a', '1'), ('b', '2'), ('c', '(4,)'), ('d', '5'), ('e', '6')]
  144. >>> class Class:
  145. ... @staticmethod
  146. ... @helper
  147. ... def static_args(a, b):
  148. ... pass
  149. ...
  150. ... @classmethod
  151. ... @helper
  152. ... def method_args(cls, a, b):
  153. ... pass
  154. ...
  155. ... @helper
  156. ... def args(self, a, b):
  157. ... pass
  158. >>> cls = Class()
  159. >>> cls.args(1, 2)
  160. [('a', '1'), ('b', '2')]
  161. >>> cls.method_args(1, 2)
  162. [('a', '1'), ('b', '2')]
  163. >>> cls.static_args(1, 2)
  164. [('a', '1'), ('b', '2')]
  165. """
  166. parameters = {}
  167. arg_spec = inspect.getfullargspec(func)
  168. arg_order = list(arg_spec.args)
  169. args_dict = dict(zip(arg_spec.args, args))
  170. if arg_spec.defaults:
  171. kwargs_defaults_dict = dict(zip(arg_spec.args[-len(arg_spec.defaults):], arg_spec.defaults))
  172. parameters.update(kwargs_defaults_dict)
  173. if arg_spec.varargs:
  174. arg_order.append(arg_spec.varargs)
  175. varargs = args[len(arg_spec.args):]
  176. parameters.update({arg_spec.varargs: varargs} if varargs else {})
  177. if arg_spec.args and arg_spec.args[0] in ['cls', 'self']:
  178. args_dict.pop(arg_spec.args[0], None)
  179. if kwargs:
  180. if sys.version_info < (3, 7):
  181. # Sort alphabetically as old python versions does
  182. # not preserve call order for kwargs.
  183. arg_order.extend(sorted(list(kwargs.keys())))
  184. else:
  185. # Keep py3.7 behaviour to preserve kwargs order
  186. arg_order.extend(list(kwargs.keys()))
  187. parameters.update(kwargs)
  188. parameters.update(args_dict)
  189. items = parameters.items()
  190. sorted_items = sorted(
  191. map(
  192. lambda kv: (kv[0], represent(kv[1])),
  193. items
  194. ),
  195. key=lambda x: arg_order.index(x[0])
  196. )
  197. return collections.OrderedDict(sorted_items)
  198. def format_traceback(exc_traceback):
  199. return ''.join(traceback.format_tb(exc_traceback)) if exc_traceback else None
  200. def format_exception(etype, value):
  201. """
  202. >>> import sys
  203. >>> try:
  204. ... assert False, 'Привет'
  205. ... except AssertionError:
  206. ... etype, e, _ = sys.exc_info()
  207. ... format_exception(etype, e) # doctest: +ELLIPSIS
  208. 'AssertionError: ...\\n'
  209. >>> try:
  210. ... assert False, 'Привет'
  211. ... except AssertionError:
  212. ... etype, e, _ = sys.exc_info()
  213. ... format_exception(etype, e) # doctest: +ELLIPSIS
  214. 'AssertionError: ...\\n'
  215. >>> try:
  216. ... compile("bla 'Привет'", "fake.py", "exec")
  217. ... except SyntaxError:
  218. ... etype, e, _ = sys.exc_info()
  219. ... format_exception(etype, e) # doctest: +ELLIPSIS
  220. ' File "fake.py", line 1...SyntaxError: invalid syntax\\n'
  221. >>> try:
  222. ... compile("bla 'Привет'", "fake.py", "exec")
  223. ... except SyntaxError:
  224. ... etype, e, _ = sys.exc_info()
  225. ... format_exception(etype, e) # doctest: +ELLIPSIS
  226. ' File "fake.py", line 1...SyntaxError: invalid syntax\\n'
  227. >>> from hamcrest import assert_that, equal_to
  228. >>> try:
  229. ... assert_that('left', equal_to('right'))
  230. ... except AssertionError:
  231. ... etype, e, _ = sys.exc_info()
  232. ... format_exception(etype, e) # doctest: +ELLIPSIS
  233. "AssertionError: \\nExpected:...but:..."
  234. >>> try:
  235. ... assert_that('left', equal_to('right'))
  236. ... except AssertionError:
  237. ... etype, e, _ = sys.exc_info()
  238. ... format_exception(etype, e) # doctest: +ELLIPSIS
  239. "AssertionError: \\nExpected:...but:..."
  240. """
  241. return '\n'.join(format_exception_only(etype, value)) if etype or value else None
  242. def get_testplan():
  243. planned_tests = []
  244. file_path = os.environ.get("ALLURE_TESTPLAN_PATH")
  245. if file_path and os.path.exists(file_path):
  246. with open(file_path, 'r') as plan_file:
  247. plan = json.load(plan_file)
  248. planned_tests = plan.get("tests", [])
  249. return planned_tests
  250. class SafeFormatter(string.Formatter):
  251. """
  252. Format string safely - skip any non-passed keys
  253. >>> f = SafeFormatter().format
  254. Make sure we don't broke default formatting behaviour
  255. >>> f("literal string")
  256. 'literal string'
  257. >>> f("{expected.format}", expected=str)
  258. "<method 'format' of 'str' objects>"
  259. >>> f("{expected[0]}", expected=["value"])
  260. 'value'
  261. >>> f("{expected[0]}", expected=123)
  262. Traceback (most recent call last):
  263. ...
  264. TypeError: 'int' object is not subscriptable
  265. >>> f("{expected[0]}", expected=[])
  266. Traceback (most recent call last):
  267. ...
  268. IndexError: list index out of range
  269. >>> f("{expected.format}", expected=int)
  270. Traceback (most recent call last):
  271. ...
  272. AttributeError: type object 'int' has no attribute 'format'
  273. Check that unexpected keys do not cause some errors
  274. >>> f("{expected} {unexpected}", expected="value")
  275. 'value {unexpected}'
  276. >>> f("{unexpected[0]}", expected=["value"])
  277. '{unexpected[0]}'
  278. >>> f("{unexpected.format}", expected=str)
  279. '{unexpected.format}'
  280. """
  281. class SafeKeyOrIndexError(Exception):
  282. pass
  283. def get_field(self, field_name, args, kwargs):
  284. try:
  285. return super().get_field(field_name, args, kwargs)
  286. except self.SafeKeyOrIndexError:
  287. return "{" + field_name + "}", field_name
  288. def get_value(self, key, args, kwargs):
  289. try:
  290. return super().get_value(key, args, kwargs)
  291. except (KeyError, IndexError):
  292. raise self.SafeKeyOrIndexError()