1from __future__ import absolute_import
2
3from typing import Callable
4from typing import Optional
5
6
7class uWSGIConfigError(Exception):
8    """uWSGI configuration error.
9
10    This is raised when uwsgi configuration is incompatible with the library.
11    """
12
13
14class uWSGIMasterProcess(Exception):
15    """The process is uWSGI master process."""
16
17
18def check_uwsgi(worker_callback=None, atexit=None):
19    # type: (Optional[Callable], Optional[Callable]) -> None
20    """Check whetever uwsgi is running and what needs to be done.
21
22    :param worker_callback: Callback function to call in uWSGI worker processes.
23    """
24    try:
25        import uwsgi
26    except ImportError:
27        return
28
29    if not uwsgi.opt.get("enable-threads"):
30        raise uWSGIConfigError("enable-threads option must be set to true")
31
32    # If uwsgi has more than one process, it is running in prefork operational mode: uwsgi is going to fork multiple
33    # sub-processes.
34    # If lazy-app is enabled, then the app is loaded in each subprocess independently. This is fine.
35    # If it's not enabled, then the app will be loaded in the master process, and uwsgi will `fork()` abruptly,
36    # bypassing Python sanity checks. We need to handle this case properly.
37    # The proper way to handle that is to allow to register a callback function to run in the subprocess at their
38    # startup, and warn the caller that this is the master process and that (probably) nothing should be done.
39    if uwsgi.numproc > 1 and not uwsgi.opt.get("lazy-apps") and uwsgi.worker_id() == 0:
40
41        if not uwsgi.opt.get("master"):
42            # Having multiple workers without the master process is not supported:
43            # the postfork hooks are not available, so there's no way to start a different profiler in each
44            # worker
45            raise uWSGIConfigError("master option must be enabled when multiple processes are used")
46
47        # Register the function to be called in child process at startup
48        if worker_callback is not None:
49            try:
50                import uwsgidecorators
51            except ImportError:
52                raise uWSGIConfigError("Running under uwsgi but uwsgidecorators cannot be imported")
53            uwsgidecorators.postfork(worker_callback)
54
55        if atexit is not None:
56
57            original_atexit = getattr(uwsgi, "atexit", None)
58
59            def _atexit():
60                try:
61                    atexit()
62                except Exception:
63                    pass
64
65                if original_atexit is not None:
66                    original_atexit()
67
68            uwsgi.atexit = _atexit
69
70        raise uWSGIMasterProcess()
71