1from typing import TYPE_CHECKING
2
3if TYPE_CHECKING:
4 from .client import ClientResponse
5
6__all__ = ["AppRequiredError", "RedirectCycleError", "require_app"]
7
8
9class RedirectCycleError(Exception):
10 """The test client has been asked to follow a redirect loop."""
11
12 def __init__(self, message: str, last_response: ClientResponse) -> None:
13 super().__init__(message)
14 self.last_response = last_response
15
16
17class AppRequiredError(RuntimeError):
18 """A test helper that needs a Plain app was used in a run without one."""
19
20
21def require_app(helper: str) -> None:
22 """Raise `AppRequiredError` unless a Plain app is set up.
23
24 `helper` is the name to put in the message: `require_app("Client")`.
25 """
26 from plain.packages import packages_registry
27
28 if packages_registry.ready:
29 return
30
31 raise AppRequiredError(
32 f"{helper} needs a Plain app, and this run has none: there is no"
33 " `app/` directory where `plain test` was started, so the tests are"
34 " running on their own. That works for code that doesn't touch the"
35 " app — `raises`, `@cases`, `@skip`, `@tag`, `skip_test`, `patch`,"
36 " and bare `assert` all do. Anything that reads settings, routes or"
37 f" the database does not. To use {helper}, run `plain test` from the"
38 " directory that holds `app/`."
39 )