81494453b0
Change auto reloader enviroment varible name to SANIC_SERVER_RUNNING Fix some typo mistakes, flake uncompatibilities and such problems. Raise NotImplementedError for operating systems except posix systems for auto reloading.
122 lines
3.7 KiB
Python
122 lines
3.7 KiB
Python
import os
|
|
import sys
|
|
import signal
|
|
import subprocess
|
|
from time import sleep
|
|
from multiprocessing import Process
|
|
|
|
|
|
def _iter_module_files():
|
|
"""This iterates over all relevant Python files.
|
|
|
|
It goes through all
|
|
loaded files from modules, all files in folders of already loaded modules
|
|
as well as all files reachable through a package.
|
|
"""
|
|
# The list call is necessary on Python 3 in case the module
|
|
# dictionary modifies during iteration.
|
|
for module in list(sys.modules.values()):
|
|
if module is None:
|
|
continue
|
|
filename = getattr(module, '__file__', None)
|
|
if filename:
|
|
old = None
|
|
while not os.path.isfile(filename):
|
|
old = filename
|
|
filename = os.path.dirname(filename)
|
|
if filename == old:
|
|
break
|
|
else:
|
|
if filename[-4:] in ('.pyc', '.pyo'):
|
|
filename = filename[:-1]
|
|
yield filename
|
|
|
|
|
|
def _get_args_for_reloading():
|
|
"""Returns the executable."""
|
|
rv = [sys.executable]
|
|
rv.extend(sys.argv)
|
|
return rv
|
|
|
|
|
|
def restart_with_reloader():
|
|
"""Create a new process and a subprocess in it with the same arguments as
|
|
this one.
|
|
"""
|
|
args = _get_args_for_reloading()
|
|
new_environ = os.environ.copy()
|
|
new_environ['SANIC_SERVER_RUNNING'] = 'true'
|
|
cmd = ' '.join(args)
|
|
worker_process = Process(
|
|
target=subprocess.call, args=(cmd,),
|
|
kwargs=dict(shell=True, env=new_environ))
|
|
worker_process.start()
|
|
return worker_process
|
|
|
|
|
|
def kill_process_children_unix(pid):
|
|
"""Find and kill child process of a process (maximum two level).
|
|
|
|
:param pid: PID of process (process ID)
|
|
:return: Nothing
|
|
"""
|
|
root_process_path = "/proc/{pid}/task/{pid}/children".format(pid=pid)
|
|
if not os.path.isfile(root_process_path):
|
|
return
|
|
with open(root_process_path) as children_list_file:
|
|
children_list_pid = children_list_file.read().split()
|
|
|
|
for child_pid in children_list_pid:
|
|
children_proc_path = "/proc/%s/task/%s/children" % \
|
|
(child_pid, child_pid)
|
|
if not os.path.isfile(children_proc_path):
|
|
continue
|
|
with open(children_proc_path) as children_list_file_2:
|
|
children_list_pid_2 = children_list_file_2.read().split()
|
|
for _pid in children_list_pid_2:
|
|
os.kill(int(_pid), signal.SIGTERM)
|
|
|
|
|
|
def kill_program_completly(proc):
|
|
"""Kill worker and it's child processes and exit.
|
|
|
|
:param proc: worker process (process ID)
|
|
:return: Nothing
|
|
"""
|
|
kill_process_children_unix(proc.pid)
|
|
proc.terminate()
|
|
os._exit(0)
|
|
|
|
|
|
def watchdog(sleep_interval):
|
|
"""Watch project files, restart worker process if a change happened.
|
|
|
|
:param sleep_interval: interval in second.
|
|
:return: Nothing
|
|
"""
|
|
mtimes = {}
|
|
worker_process = restart_with_reloader()
|
|
signal.signal(
|
|
signal.SIGTERM, lambda *args: kill_program_completly(worker_process))
|
|
signal.signal(
|
|
signal.SIGINT, lambda *args: kill_program_completly(worker_process))
|
|
while True:
|
|
for filename in _iter_module_files():
|
|
try:
|
|
mtime = os.stat(filename).st_mtime
|
|
except OSError:
|
|
continue
|
|
|
|
old_time = mtimes.get(filename)
|
|
if old_time is None:
|
|
mtimes[filename] = mtime
|
|
continue
|
|
elif mtime > old_time:
|
|
kill_process_children_unix(worker_process.pid)
|
|
worker_process = restart_with_reloader()
|
|
|
|
mtimes[filename] = mtime
|
|
break
|
|
|
|
sleep(sleep_interval)
|