comparison lib/r2py.py @ 0:1d1b9e1b2e2f draft

Uploaded
author petr-novak
date Thu, 19 Dec 2019 10:24:45 -0500
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:1d1b9e1b2e2f
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")