1"""
2Skipping a test from inside its body.
3
4`@skip` declares that a test never runs. `skip_test()` is for the case only
5the running test can see — a condition computed from its arguments, or an
6outcome it has to try for first.
7"""
8
9from typing import NoReturn
10
11__all__ = ["TestSkipped", "skip_test"]
12
13
14class TestSkipped(BaseException):
15 """
16 Raised by `skip_test()`. The runner reports the test as skipped.
17
18 A BaseException, so an `except Exception:` in the test or in the code it
19 exercises can't swallow the skip and let the test carry on.
20 """
21
22 def __init__(self, reason: str) -> None:
23 super().__init__(reason)
24 self.reason = reason
25
26
27def skip_test(reason: str) -> NoReturn:
28 """
29 Stop this test here and report it as skipped, with the reason shown.
30
31 def test_upload_to_bucket():
32 if not bucket_is_reachable():
33 skip_test("No bucket reachable from this machine")
34 ...
35
36 Everything the test entered is still exited: `with` blocks unwind, and
37 the database transaction is rolled back like any other test's.
38 """
39 if not isinstance(reason, str) or not reason.strip():
40 raise TypeError('skip_test() requires a reason: skip_test("why")')
41 raise TestSkipped(reason)