mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
3e3779498e
Add a python interface (`revng.profile`) for interacting with the rev.ng infrastructure as a whole; either through the CLI (`CLIProject`) or the GraphQL API (`DaemonProject`).
84 lines
2.5 KiB
Python
84 lines
2.5 KiB
Python
#
|
|
# This file is distributed under the MIT License. See LICENSE.md for details.
|
|
#
|
|
|
|
import os
|
|
import socket
|
|
from signal import SIGINT
|
|
from subprocess import DEVNULL, STDOUT, Popen
|
|
from time import sleep
|
|
from typing import Optional
|
|
|
|
import requests
|
|
|
|
from .daemon_project import DaemonProject
|
|
from .project import CLIProjectMixin, ResumeProjectMixin
|
|
|
|
|
|
class LocalDaemonProject(DaemonProject, CLIProjectMixin, ResumeProjectMixin):
|
|
"""
|
|
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,
|
|
resume_path: Optional[str] = None,
|
|
revng_executable_path: Optional[str] = None,
|
|
*,
|
|
connection_retries: int = 10,
|
|
):
|
|
CLIProjectMixin.__init__(self, revng_executable_path)
|
|
ResumeProjectMixin.__init__(self, resume_path)
|
|
|
|
self.port: int = self._get_port()
|
|
self.daemon_process: Optional[Popen] = None
|
|
self.start_daemon(connection_retries)
|
|
super().__init__(f"http://127.0.0.1:{self.port}/graphql/")
|
|
|
|
def __del__(self):
|
|
self.stop_daemon()
|
|
|
|
def start_daemon(self, connection_retries: int):
|
|
"""
|
|
Start the `revng` daemon and wait for it to be ready.
|
|
"""
|
|
env = os.environ
|
|
env["REVNG_DATA_DIR"] = self.resume_path
|
|
|
|
cli_args = [self.revng_executable_path, "daemon", "-b", f"tcp:127.0.0.1:{self.port}"]
|
|
self.daemon_process = Popen(cli_args, stdout=DEVNULL, stderr=STDOUT, env=env)
|
|
|
|
failed_retries = 0
|
|
while failed_retries <= connection_retries:
|
|
if self._is_server_running():
|
|
return
|
|
failed_retries += 1
|
|
sleep(1)
|
|
raise RuntimeError(f"Couldn't connect to daemon server at http://127.0.0.1:{self.port}")
|
|
|
|
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:
|
|
requests.get(f"http://127.0.0.1:{self.port}/status", timeout=5)
|
|
return True
|
|
except requests.exceptions.ConnectionError:
|
|
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)
|