mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
8ad19f2f3d
Convert `revng.project` to use the `revng2` command-line and the `revng project daemon` server instead of the legacy ones.
84 lines
2.7 KiB
Python
84 lines
2.7 KiB
Python
#
|
|
# This file is distributed under the MIT License. See LICENSE.md for details.
|
|
#
|
|
|
|
import socket
|
|
from pathlib import Path
|
|
from signal import SIGINT
|
|
from subprocess import DEVNULL, STDOUT, Popen
|
|
from time import sleep
|
|
from typing import Optional, Union
|
|
|
|
import requests
|
|
from requests.exceptions import RequestException
|
|
from urllib3.exceptions import HTTPError
|
|
|
|
from revng.project.common import CLIHelper
|
|
from revng.project.daemon_project import DaemonProject
|
|
|
|
|
|
class LocalDaemonProject(DaemonProject):
|
|
"""
|
|
This class extends the DaemonProject. When initialized it starts the revng daemon
|
|
server and setups the DaemonProject client used to connect to the server.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
project_directory: Union[str, Path, None] = None,
|
|
executable_path: Optional[str] = None,
|
|
*,
|
|
connection_retries: int = 10,
|
|
):
|
|
self._cli_helper = CLIHelper(project_directory, executable_path)
|
|
self._host = "127.0.0.1"
|
|
self._port: int = self._get_port()
|
|
self._daemon_url = f"http://{self._host}:{self._port}/"
|
|
self._daemon_process: Optional[Popen] = self._start_daemon(connection_retries)
|
|
# We need to delay calling the parent's constructor because we first
|
|
# needed to start the server that the parent will connect to.
|
|
super().__init__(self._daemon_url)
|
|
|
|
def __del__(self):
|
|
self._stop_daemon()
|
|
|
|
def _start_daemon(self, connection_retries: int):
|
|
"""
|
|
Start the `revng2 project daemon` and wait for it to be ready.
|
|
"""
|
|
cli_args = ["daemon", "--host", self._host, "--port", str(self._port)]
|
|
process = self._cli_helper.popen(cli_args, stdout=DEVNULL, stderr=STDOUT)
|
|
|
|
failed_retries = 0
|
|
while failed_retries <= connection_retries:
|
|
if self._is_server_running():
|
|
return process
|
|
failed_retries += 1
|
|
sleep(1)
|
|
raise RuntimeError(f"Couldn't connect to daemon server at {self._daemon_url}")
|
|
|
|
def _stop_daemon(self) -> int:
|
|
"""
|
|
Stop the daemon server.
|
|
"""
|
|
if self._daemon_process:
|
|
self._daemon_process.send_signal(SIGINT)
|
|
status_code = self._daemon_process.wait(30.0)
|
|
self._daemon_process = None
|
|
return status_code
|
|
return 0
|
|
|
|
def _is_server_running(self) -> bool:
|
|
try:
|
|
req = requests.get(f"{self._daemon_url}status", timeout=5)
|
|
return req.ok
|
|
except (RequestException, HTTPError):
|
|
return False
|
|
|
|
def _get_port(self) -> int:
|
|
s = socket.socket()
|
|
s.bind(("127.0.0.1", 0))
|
|
free_socket = s.getsockname()[1]
|
|
s.close()
|
|
return int(free_socket)
|