forked from zackees/template-docker-fastapi-site
-
Notifications
You must be signed in to change notification settings - Fork 0
/
prod.py
135 lines (117 loc) · 3.68 KB
/
prod.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import os
import signal
import subprocess
from contextlib import contextmanager
import time
from warnings import warn
from pathlib import Path
import atexit
from threading import Thread
HERE = Path(__file__).parent
WWW = HERE / "www"
MAX_SPACE_NPM = 256
APP_NAME = "mediabiasscorer"
os.environ["NODE_OPTIONS"] = f"--max_old_space_size={MAX_SPACE_NPM}"
# Function to clean up background processes
def cleanup(pro: subprocess.Popen):
print("Cleaning up background processes...")
pro.terminate() # Attempt graceful termination
try:
pro.wait(timeout=5) # Wait up to 5 seconds for process to terminate
except subprocess.TimeoutExpired:
pro.kill() # Force kill if not terminated after timeout
# Context manager to handle process start and cleanup
@contextmanager # type: ignore
def run_background_process() -> subprocess.Popen: # type: ignore
pro: subprocess.Popen | None = None
try:
# Switch to using the script's current directory:
os.chdir(os.path.dirname(os.path.abspath(__file__)))
# Start background processes
pro = subprocess.Popen(
[
"uvicorn",
"--host",
"0.0.0.0",
"--port",
"80",
"--workers",
"8",
"--forwarded-allow-ips=*",
f"{APP_NAME}.app:app", # TODO: programmatically pull this name
]
)
# Trap SIGINT (Ctrl-C) to call the cleanup function
signal.signal(signal.SIGINT, lambda signum, frame: lambda: cleanup(pro))
yield pro
finally:
if pro:
cleanup(pro)
def perform_npm_build() -> None:
# install first
print("Building front end with npm...")
cmd_list: list[str] = [
"cd",
str(WWW.absolute()),
"&&",
"npm",
"install",
]
cmd_str = subprocess.list2cmdline(cmd_list)
return_code = os.system(cmd_str)
if return_code != 0:
warn(f"npm install returned {return_code}")
return
# Then build to www/dist
cmd_list: list[str] = [
"cd",
str(WWW.absolute()),
"&&",
"npm",
"run",
"build",
]
cmd_str = subprocess.list2cmdline(cmd_list)
print(f"Running: {cmd_str}, in {WWW.absolute()}...")
return_code = os.system(cmd_str)
if return_code != 0:
warn(f"npm run build returned {return_code}, you will not have a file server")
return
print("Front end built.")
def run_background_tasks() -> None:
"""Run the background tasks."""
data: list[subprocess.Popen] = list()
def kill_proc() -> None:
if data:
proc = data[0]
proc.kill()
atexit.register(kill_proc)
while True:
proc = subprocess.Popen(f"python -m {APP_NAME}.background_tasks", shell=True, cwd=HERE)
if len(data) == 0:
data.append(proc)
else:
data[0] = proc
while proc.poll() is None:
time.sleep(1)
def main() -> None:
# run npm build first
# install first
perform_npm_build()
background_task = Thread(target=run_background_tasks, daemon=True, name="background_tasks")
background_task.start()
# Use the context manager to run and manage the background process
process: subprocess.Popen
with run_background_process() as process:
# Wait for the background process to finish
assert process is not None
try:
rtn = process.wait()
if rtn != 0:
warn(f"Background process returned {rtn}")
except KeyboardInterrupt:
# Handle Ctrl-C
pass
# Main function
if __name__ == "__main__":
main()