forked from extern/httpie-cli
542a2d35de
* httpie/internal/daemons.py * httpie/utils.py
122 lines
3.3 KiB
Python
122 lines
3.3 KiB
Python
"""
|
|
This module provides an interface to spawn a detached task to be
|
|
run with httpie.internal.daemon_runner on a separate process. It is
|
|
based on DVC's daemon system.
|
|
https://github.com/iterative/dvc/blob/main/dvc/daemon.py
|
|
"""
|
|
|
|
import inspect
|
|
import os
|
|
import platform
|
|
import sys
|
|
import httpie.__main__
|
|
from contextlib import suppress
|
|
from subprocess import Popen, DEVNULL
|
|
from typing import Dict, List
|
|
from httpie.compat import is_frozen, is_windows
|
|
|
|
|
|
ProcessContext = Dict[str, str]
|
|
|
|
|
|
def _start_process(cmd: List[str], **kwargs) -> Popen:
|
|
prefix = [sys.executable]
|
|
# If it is frozen, sys.executable points to the binary (http).
|
|
# Otherwise it points to the python interpreter.
|
|
if not is_frozen:
|
|
main_entrypoint = httpie.__main__.__file__
|
|
prefix += [main_entrypoint]
|
|
return Popen(prefix + cmd, close_fds=True, shell=False, stdout=DEVNULL, stderr=DEVNULL, **kwargs)
|
|
|
|
|
|
def _spawn_windows(cmd: List[str], process_context: ProcessContext) -> None:
|
|
from subprocess import (
|
|
CREATE_NEW_PROCESS_GROUP,
|
|
CREATE_NO_WINDOW,
|
|
STARTF_USESHOWWINDOW,
|
|
STARTUPINFO,
|
|
)
|
|
|
|
# https://stackoverflow.com/a/7006424
|
|
# https://bugs.python.org/issue41619
|
|
creationflags = CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW
|
|
|
|
startupinfo = STARTUPINFO()
|
|
startupinfo.dwFlags |= STARTF_USESHOWWINDOW
|
|
|
|
_start_process(
|
|
cmd,
|
|
env=process_context,
|
|
creationflags=creationflags,
|
|
startupinfo=startupinfo,
|
|
)
|
|
|
|
|
|
def _spawn_posix(args: List[str], process_context: ProcessContext) -> None:
|
|
"""
|
|
Perform a double fork procedure* to detach from the parent
|
|
process so that we don't block the user even if their original
|
|
command's execution is done but the release fetcher is not.
|
|
|
|
[1]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap11.html#tag_11_01_03
|
|
"""
|
|
|
|
from httpie.core import main
|
|
|
|
try:
|
|
pid = os.fork()
|
|
if pid > 0:
|
|
return
|
|
except OSError:
|
|
os._exit(1)
|
|
|
|
os.setsid()
|
|
|
|
try:
|
|
pid = os.fork()
|
|
if pid > 0:
|
|
os._exit(0)
|
|
except OSError:
|
|
os._exit(1)
|
|
|
|
# Close all standard inputs/outputs
|
|
sys.stdin.close()
|
|
sys.stdout.close()
|
|
sys.stderr.close()
|
|
|
|
if platform.system() == 'Darwin':
|
|
# Double-fork is not reliable on MacOS, so we'll use a subprocess
|
|
# to ensure the task is isolated properly.
|
|
process = _start_process(args, env=process_context)
|
|
# Unlike windows, since we already completed the fork procedure
|
|
# we can simply join the process and wait for it.
|
|
process.communicate()
|
|
else:
|
|
os.environ.update(process_context)
|
|
with suppress(BaseException):
|
|
main(['http'] + args)
|
|
|
|
os._exit(0)
|
|
|
|
|
|
def _spawn(args: List[str], process_context: ProcessContext) -> None:
|
|
"""
|
|
Spawn a new process to run the given command.
|
|
"""
|
|
if is_windows:
|
|
_spawn_windows(args, process_context)
|
|
else:
|
|
_spawn_posix(args, process_context)
|
|
|
|
|
|
def spawn_daemon(task: str) -> None:
|
|
args = [task, '--daemon']
|
|
process_context = os.environ.copy()
|
|
if not is_frozen:
|
|
file_path = os.path.abspath(inspect.stack()[0][1])
|
|
process_context['PYTHONPATH'] = os.path.dirname(
|
|
os.path.dirname(os.path.dirname(file_path))
|
|
)
|
|
|
|
_spawn(args, process_context)
|