|
| 1 | +from __future__ import annotations |
| 2 | +import contextlib |
| 3 | +import os.path |
| 4 | +# import shlex |
| 5 | +import subprocess |
| 6 | +import urllib.request |
| 7 | +import sys |
| 8 | +import time |
| 9 | +from typing import Generator |
| 10 | +# from typing import IO |
| 11 | +import psutil |
| 12 | + |
| 13 | + |
| 14 | +rev = subprocess.check_output(('git', 'rev-parse', 'HEAD'), text=True).strip() |
| 15 | + |
| 16 | +prefix = os.path.abspath('../prefix') |
| 17 | +venv = os.path.abspath('../venv') |
| 18 | +logs = os.path.abspath('../logs') |
| 19 | +uwsgi_src = os.path.abspath('../pyuwsgi-wheels/uwsgi') |
| 20 | + |
| 21 | + |
| 22 | +def tee() -> None: |
| 23 | + ... |
| 24 | + |
| 25 | + |
| 26 | +def _cmd_q() -> None: |
| 27 | + ... |
| 28 | + |
| 29 | + |
| 30 | +def _build_cpython() -> None: |
| 31 | + print('!!! building cpython') |
| 32 | + _cmd_q('git', 'clean', '-fxfd') |
| 33 | + _cmd_q('./configure', '--prefix', prefix) |
| 34 | + _cmd_q('make', '-j5') |
| 35 | + _cmd_q('make', 'install') |
| 36 | + |
| 37 | + |
| 38 | +def _make_venv() -> None: |
| 39 | + print('!!! making venv') |
| 40 | + _cmd_q('rm', '-rf', venv) |
| 41 | + _cmd_q(os.path.join(prefix, 'bin', 'python3'), '-mvenv', venv) |
| 42 | + |
| 43 | + |
| 44 | +def _install_uwsgi() -> None: |
| 45 | + print('!!! installing uwsgi') |
| 46 | + _cmd_q(os.path.join(venv, 'bin', 'pip'), 'install', uwsgi_src) |
| 47 | + |
| 48 | + |
| 49 | +def _req() -> None: |
| 50 | + urllib.request.urlopen('http://127.0.0.1:9001/', timeout=1).read() |
| 51 | + |
| 52 | + |
| 53 | +def _wait_for_ready() -> None: |
| 54 | + for _ in range(10): |
| 55 | + try: |
| 56 | + _req() |
| 57 | + except OSError: |
| 58 | + print('...waiting for start', file=sys.stderr) |
| 59 | + time.sleep(.25) |
| 60 | + else: |
| 61 | + return |
| 62 | + |
| 63 | + |
| 64 | +def _child_pids(pid: int) -> tuple[int, ...]: |
| 65 | + return tuple(child.pid for child in psutil.Process(pid).children()) |
| 66 | + |
| 67 | + |
| 68 | +@contextlib.contextmanager |
| 69 | +def uwsgi_proc() -> Generator[int]: |
| 70 | + uwsgi = os.path.join(venv, 'bin', 'uwsgi') |
| 71 | + _cmd_q(uwsgi, '--help') |
| 72 | + env = { |
| 73 | + **os.environ, |
| 74 | + 'UWSGI_MASTER': 'true', |
| 75 | + 'UWSGI_BINARY_PATH': os.path.join(venv, 'bin', 'python3'), |
| 76 | + 'UWSGI_VIRTUALENV': venv, |
| 77 | + 'UWSGI_WORKERS': '2', |
| 78 | + 'UWSGI_ENABLE_THREADS': 'true', |
| 79 | + 'UWSGI_MAX_REQUESTS': '2', |
| 80 | + 'UWSGI_SINGLE_INTERPRETER': 'true', |
| 81 | + 'UWSGI_HTTP_SOCKET': 'localhost:9001', |
| 82 | + 'UWSGI_WSGI_FILE': 'wsgi.py', |
| 83 | + 'UWSGI_DISABLE_LOGGING': '1', |
| 84 | + } |
| 85 | + with open(os.path.join(logs, rev), 'a+') as logfile: |
| 86 | + tee('+ uwsgi', logfile, sys.stderr) |
| 87 | + proc = subprocess.Popen( |
| 88 | + (uwsgi, ), |
| 89 | + env=env, |
| 90 | + cwd='..', |
| 91 | + stdout=logfile, |
| 92 | + stderr=logfile, |
| 93 | + ) |
| 94 | + _wait_for_ready() |
| 95 | + try: |
| 96 | + yield proc.pid |
| 97 | + finally: |
| 98 | + to_kill = [str(proc.pid)] |
| 99 | + for child_pid in _child_pids(proc.pid): |
| 100 | + to_kill.append(str(child_pid)) |
| 101 | + subprocess.call(('kill', '-9', *to_kill)) |
| 102 | + |
| 103 | + |
| 104 | +def _run_test() -> int: |
| 105 | + with uwsgi_proc() as pid: |
| 106 | + children = set(_child_pids(pid)) |
| 107 | + print(f'started with pid: {pid} {children}!', file=sys.stderr) |
| 108 | + while True: |
| 109 | + time.sleep(.25) |
| 110 | + try: |
| 111 | + _req() |
| 112 | + except OSError: |
| 113 | + cand_children = set(_child_pids(pid)) |
| 114 | + assert not children & cand_children, (children, cand_children) |
| 115 | + print('\ngot deadlock!!') |
| 116 | + return 1 |
| 117 | + else: |
| 118 | + cand_children = set(_child_pids(pid)) |
| 119 | + if cand_children & children: |
| 120 | + print('z', flush=True, end='') |
| 121 | + continue |
| 122 | + else: |
| 123 | + print('\nsuccessful request after all children recycled!') |
| 124 | + return 0 |
| 125 | + |
| 126 | + |
| 127 | +def main() -> int: |
| 128 | + assert os.path.exists('Python'), 'not in cpython' |
| 129 | + _build_cpython() |
| 130 | + _make_venv() |
| 131 | + _install_uwsgi() |
| 132 | + return _run_test() |
| 133 | + |
| 134 | + |
| 135 | +if __name__ == '__main__': |
| 136 | + raise SystemExit(main()) |
0 commit comments