test_multicall.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import pytest
  2. from pluggy import HookCallError, HookspecMarker, HookimplMarker
  3. from pluggy._hooks import HookImpl
  4. from pluggy._callers import _multicall
  5. hookspec = HookspecMarker("example")
  6. hookimpl = HookimplMarker("example")
  7. def MC(methods, kwargs, firstresult=False):
  8. caller = _multicall
  9. hookfuncs = []
  10. for method in methods:
  11. f = HookImpl(None, "<temp>", method, method.example_impl)
  12. hookfuncs.append(f)
  13. return caller("foo", hookfuncs, kwargs, firstresult)
  14. def test_keyword_args():
  15. @hookimpl
  16. def f(x):
  17. return x + 1
  18. class A:
  19. @hookimpl
  20. def f(self, x, y):
  21. return x + y
  22. reslist = MC([f, A().f], dict(x=23, y=24))
  23. assert reslist == [24 + 23, 24]
  24. def test_keyword_args_with_defaultargs():
  25. @hookimpl
  26. def f(x, z=1):
  27. return x + z
  28. reslist = MC([f], dict(x=23, y=24))
  29. assert reslist == [24]
  30. def test_tags_call_error():
  31. @hookimpl
  32. def f(x):
  33. return x
  34. with pytest.raises(HookCallError):
  35. MC([f], {})
  36. def test_call_none_is_no_result():
  37. @hookimpl
  38. def m1():
  39. return 1
  40. @hookimpl
  41. def m2():
  42. return None
  43. res = MC([m1, m2], {}, firstresult=True)
  44. assert res == 1
  45. res = MC([m1, m2], {}, {})
  46. assert res == [1]
  47. def test_hookwrapper():
  48. out = []
  49. @hookimpl(hookwrapper=True)
  50. def m1():
  51. out.append("m1 init")
  52. yield None
  53. out.append("m1 finish")
  54. @hookimpl
  55. def m2():
  56. out.append("m2")
  57. return 2
  58. res = MC([m2, m1], {})
  59. assert res == [2]
  60. assert out == ["m1 init", "m2", "m1 finish"]
  61. out[:] = []
  62. res = MC([m2, m1], {}, firstresult=True)
  63. assert res == 2
  64. assert out == ["m1 init", "m2", "m1 finish"]
  65. def test_hookwrapper_order():
  66. out = []
  67. @hookimpl(hookwrapper=True)
  68. def m1():
  69. out.append("m1 init")
  70. yield 1
  71. out.append("m1 finish")
  72. @hookimpl(hookwrapper=True)
  73. def m2():
  74. out.append("m2 init")
  75. yield 2
  76. out.append("m2 finish")
  77. res = MC([m2, m1], {})
  78. assert res == []
  79. assert out == ["m1 init", "m2 init", "m2 finish", "m1 finish"]
  80. def test_hookwrapper_not_yield():
  81. @hookimpl(hookwrapper=True)
  82. def m1():
  83. pass
  84. with pytest.raises(TypeError):
  85. MC([m1], {})
  86. def test_hookwrapper_too_many_yield():
  87. @hookimpl(hookwrapper=True)
  88. def m1():
  89. yield 1
  90. yield 2
  91. with pytest.raises(RuntimeError) as ex:
  92. MC([m1], {})
  93. assert "m1" in str(ex.value)
  94. assert (__file__ + ":") in str(ex.value)
  95. @pytest.mark.parametrize("exc", [ValueError, SystemExit])
  96. def test_hookwrapper_exception(exc):
  97. out = []
  98. @hookimpl(hookwrapper=True)
  99. def m1():
  100. out.append("m1 init")
  101. yield None
  102. out.append("m1 finish")
  103. @hookimpl
  104. def m2():
  105. raise exc
  106. with pytest.raises(exc):
  107. MC([m2, m1], {})
  108. assert out == ["m1 init", "m1 finish"]