0
|
1 #!/usr/bin/env python3
|
|
2 import os
|
|
3 import atexit
|
|
4 import socket
|
|
5 import time
|
|
6 import config
|
|
7 import pyRserve
|
|
8
|
|
9 def shutdown(port):
|
|
10 try:
|
|
11 conn = pyRserve.connect(port=port)
|
|
12 print("Shutting down Rserv...", end="")
|
|
13 conn.shutdown()
|
|
14 print("Done")
|
|
15 except pyRserve.rexceptions.RConnectionRefused:
|
|
16 print("connection to Rserve refused, server is probably already down")
|
|
17
|
|
18 def get_open_port():
|
|
19 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
20 s.bind(("", 0))
|
|
21 s.listen(1)
|
|
22 port = s.getsockname()[1]
|
|
23 s.close()
|
|
24 return port
|
|
25
|
|
26 # find free port
|
|
27 def create_connection():
|
|
28 '''Start R Rserver and test connection, port is
|
|
29 stored in config.RSERVE_PORT
|
|
30 '''
|
|
31 config.RSERVE_PORT = get_open_port()
|
|
32 print('Trying to start Rserve...',)
|
|
33 os.system(
|
|
34 "R CMD Rserve --RS-port {} -q --no-save ".format(config.RSERVE_PORT))
|
|
35 # wait for server to start accepting connections
|
|
36 time.sleep(1)
|
|
37 try:
|
|
38 conn = pyRserve.connect(port=config.RSERVE_PORT)
|
|
39 print("connection OK")
|
|
40 conn.close()
|
|
41 atexit.register(shutdown, config.RSERVE_PORT)
|
|
42 return config.RSERVE_PORT
|
|
43 except:
|
|
44 print("Connection with Rserve was not established!")
|
|
45 raise
|
|
46
|
|
47
|
|
48 class setFunctionName():
|
|
49 # decorator
|
|
50
|
|
51 def __init__(self, f, name):
|
|
52 self.f = f
|
|
53 self.name = name
|
|
54
|
|
55 def __call__(self, *args, **kwargs):
|
|
56 return self.f(self.name, *args, **kwargs)
|
|
57
|
|
58
|
|
59 def convert_types(fn):
|
|
60 ''' decorator function to convert type for r2py'''
|
|
61 allowed_classes = [str, int, float, list, bool, type(None)]
|
|
62 # everything else is converted to str
|
|
63
|
|
64 def fn_wrapper(*args, **kwargs):
|
|
65 new_args = list(args)
|
|
66 new_kwargs = kwargs
|
|
67 for i, value in enumerate(args):
|
|
68 if any(type(value) is i for i in allowed_classes):
|
|
69 new_args[i] = value
|
|
70 else:
|
|
71 new_args[i] = str(value)
|
|
72 for i, value in kwargs.items():
|
|
73 if any(type(value) is i for i in allowed_classes):
|
|
74 new_kwargs[i] = value
|
|
75 else:
|
|
76 new_kwargs[i] = str(value)
|
|
77 return fn(*new_args, **new_kwargs)
|
|
78
|
|
79 return fn_wrapper
|
|
80
|
|
81
|
|
82 class R():
|
|
83
|
|
84 def __init__(self, source, verbose=False):
|
|
85 ''' Code in file should defined R functions which will be linked to python function
|
|
86 purpose of this is to make it memory efficient - rserve connection will be closed
|
|
87 after every exetion so memory is released.
|
|
88 warning - Source code is executed each time function is used so it should only
|
|
89 contain function definition!!
|
|
90
|
|
91 '''
|
|
92 self.source = os.path.realpath(source)
|
|
93 conn = pyRserve.connect(port=config.RSERVE_PORT)
|
|
94 conn.voidEval("source('{}', chdir=TRUE)".format(self.source))
|
|
95 # if single object is define then fn return str! conversion neccessary
|
|
96 object_names = list(conn.r.ls())
|
|
97 if verbose:
|
|
98 print("R function loaded:", end=" ")
|
|
99 for i in object_names:
|
|
100 ## skip these objects - not compatible with older version of rserve
|
|
101 ## and not needed to be accessed from python
|
|
102 if i in ['DT_OPTIONS', 'HTMLHEADER', 'WD', 'options',
|
|
103 'htmlheader', 'options', 'xcolor_code', 'TANDEM_RANKS', 'RANKS_TANDEM',]:
|
|
104 continue
|
|
105 try:
|
|
106 obj = getattr(conn.r, i)
|
|
107 if isinstance(obj, pyRserve.rconn.RFuncProxy):
|
|
108 if verbose:
|
|
109 print(i, end=" ")
|
|
110 @convert_types
|
|
111 def rwrapper(fname, *args, **kwargs):
|
|
112 c = pyRserve.connect(port=config.RSERVE_PORT)
|
|
113 c.r.setwd(os.getcwd())
|
|
114 c.voidEval("source('{}',chdir=TRUE)".format(self.source))
|
|
115 fn = getattr(c.r, fname)
|
|
116 out = fn(*args, **kwargs)
|
|
117 c.close()
|
|
118 return out
|
|
119 rwrapper = setFunctionName(rwrapper, i)
|
|
120 setattr(self, i, rwrapper)
|
|
121 del(rwrapper)
|
|
122 except:
|
|
123 print("skipping :", i)
|
|
124 pass
|
|
125 if verbose:
|
|
126 print("\r")
|