reinvent_fixtures_py2k.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. """
  2. invent a quick version of pytest autouse fixtures as pytest's unacceptably slow
  3. collection/high memory use in pytest 4.6.11, which is the highest version that
  4. works in py2k.
  5. by "too-slow" we mean the test suite can't even manage to be collected for a
  6. single process in less than 70 seconds or so and memory use seems to be very
  7. high as well. for two or four workers the job just times out after ten
  8. minutes.
  9. so instead we have invented a very limited form of these fixtures, as our
  10. current use of "autouse" fixtures are limited to those in fixtures.py.
  11. assumptions for these fixtures:
  12. 1. we are only using "function" or "class" scope
  13. 2. the functions must be associated with a test class
  14. 3. the fixture functions cannot themselves use pytest fixtures
  15. 4. the fixture functions must use yield, not return
  16. When py2k support is removed and we can stay on a modern pytest version, this
  17. can all be removed.
  18. """
  19. import collections
  20. _py2k_fixture_fn_names = collections.defaultdict(set)
  21. _py2k_class_fixtures = collections.defaultdict(
  22. lambda: collections.defaultdict(set)
  23. )
  24. _py2k_function_fixtures = collections.defaultdict(
  25. lambda: collections.defaultdict(set)
  26. )
  27. _py2k_cls_fixture_stack = []
  28. _py2k_fn_fixture_stack = []
  29. def add_fixture(fn, fixture):
  30. assert fixture.scope in ("class", "function")
  31. _py2k_fixture_fn_names[fn.__name__].add((fn, fixture.scope))
  32. def scan_for_fixtures_to_use_for_class(item):
  33. test_class = item.parent.parent.obj
  34. for name in _py2k_fixture_fn_names:
  35. for fixture_fn, scope in _py2k_fixture_fn_names[name]:
  36. meth = getattr(test_class, name, None)
  37. if meth and meth.im_func is fixture_fn:
  38. for sup in test_class.__mro__:
  39. if name in sup.__dict__:
  40. if scope == "class":
  41. _py2k_class_fixtures[test_class][sup].add(meth)
  42. elif scope == "function":
  43. _py2k_function_fixtures[test_class][sup].add(meth)
  44. break
  45. break
  46. def run_class_fixture_setup(request):
  47. cls = request.cls
  48. self = cls.__new__(cls)
  49. fixtures_for_this_class = _py2k_class_fixtures.get(cls)
  50. if fixtures_for_this_class:
  51. for sup_ in cls.__mro__:
  52. for fn in fixtures_for_this_class.get(sup_, ()):
  53. iter_ = fn(self)
  54. next(iter_)
  55. _py2k_cls_fixture_stack.append(iter_)
  56. def run_class_fixture_teardown(request):
  57. while _py2k_cls_fixture_stack:
  58. iter_ = _py2k_cls_fixture_stack.pop(-1)
  59. try:
  60. next(iter_)
  61. except StopIteration:
  62. pass
  63. def run_fn_fixture_setup(request):
  64. cls = request.cls
  65. self = request.instance
  66. fixtures_for_this_class = _py2k_function_fixtures.get(cls)
  67. if fixtures_for_this_class:
  68. for sup_ in reversed(cls.__mro__):
  69. for fn in fixtures_for_this_class.get(sup_, ()):
  70. iter_ = fn(self)
  71. next(iter_)
  72. _py2k_fn_fixture_stack.append(iter_)
  73. def run_fn_fixture_teardown(request):
  74. while _py2k_fn_fixture_stack:
  75. iter_ = _py2k_fn_fixture_stack.pop(-1)
  76. try:
  77. next(iter_)
  78. except StopIteration:
  79. pass