Mercurial > repos > petr-novak > repeatrxplorer
diff lib/r2py.py @ 0:1d1b9e1b2e2f draft
Uploaded
author | petr-novak |
---|---|
date | Thu, 19 Dec 2019 10:24:45 -0500 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/r2py.py Thu Dec 19 10:24:45 2019 -0500 @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +import os +import atexit +import socket +import time +import config +import pyRserve + +def shutdown(port): + try: + conn = pyRserve.connect(port=port) + print("Shutting down Rserv...", end="") + conn.shutdown() + print("Done") + except pyRserve.rexceptions.RConnectionRefused: + print("connection to Rserve refused, server is probably already down") + +def get_open_port(): + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.bind(("", 0)) + s.listen(1) + port = s.getsockname()[1] + s.close() + return port + + # find free port +def create_connection(): + '''Start R Rserver and test connection, port is + stored in config.RSERVE_PORT + ''' + config.RSERVE_PORT = get_open_port() + print('Trying to start Rserve...',) + os.system( + "R CMD Rserve --RS-port {} -q --no-save ".format(config.RSERVE_PORT)) + # wait for server to start accepting connections + time.sleep(1) + try: + conn = pyRserve.connect(port=config.RSERVE_PORT) + print("connection OK") + conn.close() + atexit.register(shutdown, config.RSERVE_PORT) + return config.RSERVE_PORT + except: + print("Connection with Rserve was not established!") + raise + + +class setFunctionName(): + # decorator + + def __init__(self, f, name): + self.f = f + self.name = name + + def __call__(self, *args, **kwargs): + return self.f(self.name, *args, **kwargs) + + +def convert_types(fn): + ''' decorator function to convert type for r2py''' + allowed_classes = [str, int, float, list, bool, type(None)] + # everything else is converted to str + + def fn_wrapper(*args, **kwargs): + new_args = list(args) + new_kwargs = kwargs + for i, value in enumerate(args): + if any(type(value) is i for i in allowed_classes): + new_args[i] = value + else: + new_args[i] = str(value) + for i, value in kwargs.items(): + if any(type(value) is i for i in allowed_classes): + new_kwargs[i] = value + else: + new_kwargs[i] = str(value) + return fn(*new_args, **new_kwargs) + + return fn_wrapper + + +class R(): + + def __init__(self, source, verbose=False): + ''' Code in file should defined R functions which will be linked to python function + purpose of this is to make it memory efficient - rserve connection will be closed + after every exetion so memory is released. + warning - Source code is executed each time function is used so it should only + contain function definition!! + + ''' + self.source = os.path.realpath(source) + conn = pyRserve.connect(port=config.RSERVE_PORT) + conn.voidEval("source('{}', chdir=TRUE)".format(self.source)) + # if single object is define then fn return str! conversion neccessary + object_names = list(conn.r.ls()) + if verbose: + print("R function loaded:", end=" ") + for i in object_names: + ## skip these objects - not compatible with older version of rserve + ## and not needed to be accessed from python + if i in ['DT_OPTIONS', 'HTMLHEADER', 'WD', 'options', + 'htmlheader', 'options', 'xcolor_code', 'TANDEM_RANKS', 'RANKS_TANDEM',]: + continue + try: + obj = getattr(conn.r, i) + if isinstance(obj, pyRserve.rconn.RFuncProxy): + if verbose: + print(i, end=" ") + @convert_types + def rwrapper(fname, *args, **kwargs): + c = pyRserve.connect(port=config.RSERVE_PORT) + c.r.setwd(os.getcwd()) + c.voidEval("source('{}',chdir=TRUE)".format(self.source)) + fn = getattr(c.r, fname) + out = fn(*args, **kwargs) + c.close() + return out + rwrapper = setFunctionName(rwrapper, i) + setattr(self, i, rwrapper) + del(rwrapper) + except: + print("skipping :", i) + pass + if verbose: + print("\r")