From c71d03d572f91523b8bc21491042a2808c04c98b Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Thu, 29 Feb 2024 17:44:37 +0100 Subject: [PATCH 1/5] Update README.md --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 290aa70..6e9bee8 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ -# bedboss +

bedboss

+
+ [![PEP compatible](https://pepkit.github.io/img/PEP-compatible-green.svg)](https://pep.databio.org/) ![Run pytests](https://github.com/bedbase/bedboss/workflows/Run%20instalation%20test/badge.svg) [![pypi-badge](https://img.shields.io/pypi/v/bedboss?color=%2334D058)](https://pypi.org/project/bedboss) @@ -7,6 +9,8 @@ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![Github badge](https://img.shields.io/badge/source-github-354a75?logo=github)](https://github.com/databio/bedboss) +
+ --- **Documentation**: https://docs.bedbase.org/bedboss From 677005054fdbf30968bde8f3f4807aefbadb4a4b Mon Sep 17 00:00:00 2001 From: nsheff Date: Sat, 5 Oct 2024 18:59:12 +0000 Subject: [PATCH 2/5] add proof of concept for external R service --- bedboss/bedstat/r_demo.py | 40 +++++++++++++++++++++++++++++++ bedboss/bedstat/tools/r-service.R | 20 ++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 bedboss/bedstat/r_demo.py create mode 100644 bedboss/bedstat/tools/r-service.R diff --git a/bedboss/bedstat/r_demo.py b/bedboss/bedstat/r_demo.py new file mode 100644 index 0000000..1c386e8 --- /dev/null +++ b/bedboss/bedstat/r_demo.py @@ -0,0 +1,40 @@ + + +import subprocess +import socket +import signal +from subprocess import Popen, PIPE, STDOUT +import os + +def run_bed_through_R(path, host = "127.0.0.1", port = 8888): + s = socket.socket() + s.connect((host, port)) + s.send(path.encode()) + s.close() + +cmd = ["Rscript", "tools/r-service.R"] +p = subprocess.Popen(cmd, shell=False, preexec_fn=os.setsid) +print(f"Running R process with PID: {p.pid}") + +# Run any BED files through bedstat + +run_bed_through_R("bedstat/data/beds/bed1.bed") + +# End the R process +run_bed_through_R("done") + +# Should probably do something like p.communicate() or p.wait() or something to wait until it ends. +p.terminate() + + + + + +# Make sure it's terminated +try: + run_bed_through_R("test") + # Force kill it from Python because the done signal failed + os.killpg(os.getpgid(p.pid), signal.SIGTERM) +except ConnectionErrorRefused: + pass # it's was killed + diff --git a/bedboss/bedstat/tools/r-service.R b/bedboss/bedstat/tools/r-service.R new file mode 100644 index 0000000..41e5095 --- /dev/null +++ b/bedboss/bedstat/tools/r-service.R @@ -0,0 +1,20 @@ +processBED = function(path, client, port) { + message("Processing BED file: ", path) + if (path == "done") { # Secret shutdown signal + message("Received done signal") + assign("done", TRUE, envir=.GlobalEnv) + return(0) + } + if (!file.exists(path)) { + message("File not found: ", path) + return(1) + } + return(1) +} + +message("Starting R server") +svSocket::start_socket_server(procfun=processBED) +message ("R server started") +while (!exists("done")) Sys.sleep(1) + +message("Shutting down R service") From 180f14ae0f5c58f50e30df86122decdf722bb187 Mon Sep 17 00:00:00 2001 From: nsheff Date: Mon, 7 Oct 2024 13:44:52 +0000 Subject: [PATCH 3/5] write RServiceManager class --- bedboss/bedstat/r_demo.py | 87 ++++++++++++++++++++++++++++----------- 1 file changed, 63 insertions(+), 24 deletions(-) diff --git a/bedboss/bedstat/r_demo.py b/bedboss/bedstat/r_demo.py index 1c386e8..0a81aca 100644 --- a/bedboss/bedstat/r_demo.py +++ b/bedboss/bedstat/r_demo.py @@ -1,40 +1,79 @@ - import subprocess import socket import signal -from subprocess import Popen, PIPE, STDOUT -import os +import os -def run_bed_through_R(path, host = "127.0.0.1", port = 8888): - s = socket.socket() - s.connect((host, port)) - s.send(path.encode()) - s.close() +class RServiceManager: + """ + A class to manage the lifecycle of an R service, allowing files to be processed through the service. -cmd = ["Rscript", "tools/r-service.R"] -p = subprocess.Popen(cmd, shell=False, preexec_fn=os.setsid) -print(f"Running R process with PID: {p.pid}") + Attributes: + r_script_path (str): Path to the R script that starts the service. + host (str): Host address for the socket connection. + port (int): Port number for the socket connection. + process (subprocess.Popen): The process running the R service. + """ + def __init__(self, r_script_path="tools/r-service.R", host="127.0.0.1", port=8888): + """ + Initializes the RServiceManager with the given R script path, host, and port. -# Run any BED files through bedstat + Args: + r_script_path (str): Path to the R script that starts the service. + host (str): Host address for the socket connection. Default is "127.0.0.1". + port (int): Port number for the socket connection. Default is 8888. + """ + self.r_script_path = r_script_path + self.host = host + self.port = port + self.process = None + def start_service(self): + """ + Starts the R service by running the R script in a subprocess. + """ + cmd = ["Rscript", self.r_script_path] + self.process = subprocess.Popen(cmd, shell=False, preexec_fn=os.setsid) + print(f"Running R process with PID: {self.process.pid}") + def run_file(self, file_path): + """ + Sends a file path to the R service for processing. -run_bed_through_R("bedstat/data/beds/bed1.bed") + Args: + file_path (str): The path to the file to be processed by the R service. + """ + try: + s = socket.socket() + s.connect((self.host, self.port)) + s.send(file_path.encode()) + s.close() + except ConnectionRefusedError: + print("Connection refused. Make sure the R service is running.") + def terminate_service(self): + """ + Terminates the R service by sending a termination signal and ensuring the process is stopped. + """ + self.run_file("done") # send secrete "terminate" code + if self.process: + self.process.terminate() + try: + self.process.wait(timeout=5) + except subprocess.TimeoutExpired: + os.killpg(os.getpgid(self.process.pid), signal.SIGTERM) + print("R process terminated.") -# End the R process -run_bed_through_R("done") -# Should probably do something like p.communicate() or p.wait() or something to wait until it ends. -p.terminate() +# TODO: "tools/r-service.R" needs to be embedded in the package +# Start the R service at the beginning of the pipeline +rsm = RServiceManager("tools/r-service.R") +rsm.start_service() +# Run any BED files through bedstat +rsm.run_file("bedstat/data/beds/bed1.bed") +rsm.run_file("../../test/data/bed/simpleexamples/bed1.bed") -# Make sure it's terminated -try: - run_bed_through_R("test") - # Force kill it from Python because the done signal failed - os.killpg(os.getpgid(p.pid), signal.SIGTERM) -except ConnectionErrorRefused: - pass # it's was killed +# After the pipeline finishes, terminate the R service +rsm.terminate_service() From 12ea5575785f840da082246ac385d0e96695d71a Mon Sep 17 00:00:00 2001 From: nsheff Date: Mon, 7 Oct 2024 09:45:46 -0400 Subject: [PATCH 4/5] lint --- bedboss/bedstat/r_demo.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bedboss/bedstat/r_demo.py b/bedboss/bedstat/r_demo.py index 0a81aca..627743f 100644 --- a/bedboss/bedstat/r_demo.py +++ b/bedboss/bedstat/r_demo.py @@ -1,9 +1,9 @@ - import subprocess import socket import signal import os + class RServiceManager: """ A class to manage the lifecycle of an R service, allowing files to be processed through the service. @@ -14,6 +14,7 @@ class RServiceManager: port (int): Port number for the socket connection. process (subprocess.Popen): The process running the R service. """ + def __init__(self, r_script_path="tools/r-service.R", host="127.0.0.1", port=8888): """ Initializes the RServiceManager with the given R script path, host, and port. @@ -27,6 +28,7 @@ def __init__(self, r_script_path="tools/r-service.R", host="127.0.0.1", port=888 self.host = host self.port = port self.process = None + def start_service(self): """ Starts the R service by running the R script in a subprocess. @@ -34,6 +36,7 @@ def start_service(self): cmd = ["Rscript", self.r_script_path] self.process = subprocess.Popen(cmd, shell=False, preexec_fn=os.setsid) print(f"Running R process with PID: {self.process.pid}") + def run_file(self, file_path): """ Sends a file path to the R service for processing. @@ -48,6 +51,7 @@ def run_file(self, file_path): s.close() except ConnectionRefusedError: print("Connection refused. Make sure the R service is running.") + def terminate_service(self): """ Terminates the R service by sending a termination signal and ensuring the process is stopped. @@ -76,4 +80,3 @@ def terminate_service(self): # After the pipeline finishes, terminate the R service rsm.terminate_service() - From 622b94353b4807bc73362e9e12207d0f1b7b894e Mon Sep 17 00:00:00 2001 From: nsheff Date: Mon, 7 Oct 2024 09:46:14 -0400 Subject: [PATCH 5/5] add comment --- bedboss/bedstat/tools/r-service.R | 1 + 1 file changed, 1 insertion(+) diff --git a/bedboss/bedstat/tools/r-service.R b/bedboss/bedstat/tools/r-service.R index 41e5095..5d0caec 100644 --- a/bedboss/bedstat/tools/r-service.R +++ b/bedboss/bedstat/tools/r-service.R @@ -1,3 +1,4 @@ +# This function should run the process processBED = function(path, client, port) { message("Processing BED file: ", path) if (path == "done") { # Secret shutdown signal