classic.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. import sys
  2. import os
  3. import inspect
  4. import cPickle as pickle
  5. import rpyc
  6. from rpyc import SlaveService
  7. from rpyc.utils import factory
  8. SERVER_FILE = os.path.join(os.path.dirname(rpyc.__file__), "scripts", "rpyc_classic.py")
  9. DEFAULT_SERVER_PORT = 18812
  10. DEFAULT_SERVER_SSL_PORT = 18821
  11. #===============================================================================
  12. # connecting
  13. #===============================================================================
  14. def connect_channel(channel):
  15. return factory.connect_channel(channel, SlaveService)
  16. def connect_stream(stream):
  17. return factory.connect_stream(stream, SlaveService)
  18. def connect_stdpipes():
  19. return factory.connect_stdpipes(SlaveService)
  20. def connect_pipes(input, output):
  21. return factory.connect_pipes(input, output, SlaveService)
  22. def connect(host, port = DEFAULT_SERVER_PORT):
  23. """creates a socket connection to the given host and port"""
  24. return factory.connect(host, port, SlaveService)
  25. def tlslite_connect(host, username, password, port = DEFAULT_SERVER_PORT):
  26. """creates a secure (TLS) socket connection to the given host and port,
  27. authenticating with the given username and password"""
  28. return factory.tlslite_connect(host, port, username, password, SlaveService)
  29. def ssl_connect(host, port = DEFAULT_SERVER_SSL_PORT, keyfile = None,
  30. certfile = None, ca_certs = None, ssl_version = None):
  31. """creates a secure (SSL) socket connection to the given host and port,
  32. authenticating with the given certfile and CA file"""
  33. return factory.ssl_connect(host, port, keyfile = keyfile, certfile = certfile,
  34. ssl_version = ssl_version, ca_certs = ca_certs, service = SlaveService)
  35. def connect_subproc():
  36. """runs an rpyc classic server as a subprocess and return an rpyc
  37. connection to it"""
  38. return factory.connect_subproc([sys.executable, "-u", SERVER_FILE, "-q", "-m", "stdio"],
  39. SlaveService)
  40. def connect_thread():
  41. """starts a SlaveService on a thread and connects to it"""
  42. return factory.connect_thread(SlaveService, remote_service = SlaveService)
  43. #===============================================================================
  44. # remoting utilities
  45. #===============================================================================
  46. def upload(conn, localpath, remotepath, filter = None, ignore_invalid = False, chunk_size = 16000):
  47. """uploads a file or a directory to the given remote path
  48. localpath - the local file or directory
  49. remotepath - the remote path
  50. filter - a predicate that accepts the filename and determines whether
  51. it should be uploaded; None means any file
  52. chunk_size - the IO chunk size
  53. """
  54. if os.path.isdir(localpath):
  55. upload_dir(conn, localpath, remotepath, filter, chunk_size)
  56. elif os.path.isfile(localpath):
  57. upload_file(conn, localpath, remotepath, chunk_size)
  58. else:
  59. if not ignore_invalid:
  60. raise ValueError("cannot upload %r" % (localpath,))
  61. def upload_file(conn, localpath, remotepath, chunk_size = 16000):
  62. lf = open(localpath, "rb")
  63. rf = conn.modules.__builtin__.open(remotepath, "wb")
  64. while True:
  65. buf = lf.read(chunk_size)
  66. if not buf:
  67. break
  68. rf.write(buf)
  69. lf.close()
  70. rf.close()
  71. def upload_dir(conn, localpath, remotepath, filter = None, chunk_size = 16000):
  72. if not conn.modules.os.path.isdir(remotepath):
  73. conn.modules.os.makedirs(remotepath)
  74. for fn in os.listdir(localpath):
  75. if not filter or filter(fn):
  76. lfn = os.path.join(localpath, fn)
  77. rfn = conn.modules.os.path.join(remotepath, fn)
  78. upload(conn, lfn, rfn, filter = filter, ignore_invalid = True, chunk_size = chunk_size)
  79. def download(conn, remotepath, localpath, filter = None, ignore_invalid = False, chunk_size = 16000):
  80. """download a file or a directory to the given remote path
  81. localpath - the local file or directory
  82. remotepath - the remote path
  83. filter - a predicate that accepts the filename and determines whether
  84. it should be downloaded; None means any file
  85. chunk_size - the IO chunk size
  86. """
  87. if conn.modules.os.path.isdir(remotepath):
  88. download_dir(conn, remotepath, localpath, filter)
  89. elif conn.modules.os.path.isfile(remotepath):
  90. download_file(conn, remotepath, localpath, chunk_size)
  91. else:
  92. if not ignore_invalid:
  93. raise ValueError("cannot download %r" % (remotepath,))
  94. def download_file(conn, remotepath, localpath, chunk_size = 16000):
  95. rf = conn.modules.__builtin__.open(remotepath, "rb")
  96. lf = open(localpath, "wb")
  97. while True:
  98. buf = rf.read(chunk_size)
  99. if not buf:
  100. break
  101. lf.write(buf)
  102. lf.close()
  103. rf.close()
  104. def download_dir(conn, remotepath, localpath, filter = None, chunk_size = 16000):
  105. if not os.path.isdir(localpath):
  106. os.makedirs(localpath)
  107. for fn in conn.modules.os.listdir(remotepath):
  108. if not filter or filter(fn):
  109. rfn = conn.modules.os.path.join(remotepath, fn)
  110. lfn = os.path.join(localpath, fn)
  111. download(conn, rfn, lfn, filter = filter, ignore_invalid = True)
  112. def upload_package(conn, module, remotepath = None, chunk_size = 16000):
  113. """uploads a module or a package to the remote party"""
  114. if remotepath is None:
  115. site = conn.modules["distutils.sysconfig"].get_python_lib()
  116. remotepath = conn.modules.os.path.join(site, module.__name__)
  117. localpath = os.path.dirname(inspect.getsourcefile(module))
  118. upload(conn, localpath, remotepath, chunk_size = chunk_size)
  119. upload_module = upload_package
  120. def update_module(conn, module, chunk_size = 16000):
  121. """replaces a module on the remote party"""
  122. rmodule = conn.modules[module.__name__]
  123. lf = inspect.getsourcefile(module)
  124. rf = conn.modules.inspect.getsourcefile(rmodule)
  125. upload_file(conn, lf, rf, chunk_size = chunk_size)
  126. conn.modules.__builtin__.reload(rmodule)
  127. def obtain(proxy):
  128. """obtains (recreates) a remote object proxy from the other party.
  129. the object is moved by *value*, so changes made to it will not reflect
  130. on the remote object"""
  131. return pickle.loads(pickle.dumps(proxy))
  132. def deliver(conn, localobj):
  133. """delivers (recreates) a local object on the other party. the object is
  134. moved by *value*, so changes made to it will not reflect on the local
  135. object. returns a proxy to the remote object"""
  136. return conn.modules.cPickle.loads(pickle.dumps(localobj))
  137. class redirected_stdio(object):
  138. """redirects the other party's stdin, stdout and stderr to those of the
  139. local party, so remote STDIO will occur locally"""
  140. def __init__(self, conn):
  141. self._restored = True
  142. self.conn = conn
  143. self.orig_stdin = self.conn.modules.sys.stdin
  144. self.orig_stdout = self.conn.modules.sys.stdout
  145. self.orig_stderr = self.conn.modules.sys.stderr
  146. self.conn.modules.sys.stdin = sys.stdin
  147. self.conn.modules.sys.stdout = sys.stdout
  148. self.conn.modules.sys.stderr = sys.stderr
  149. self._restored = False
  150. def __del__(self):
  151. self.restore()
  152. def restore(self):
  153. if self._restored:
  154. return
  155. self._restored = True
  156. self.conn.modules.sys.stdin = self.orig_stdin
  157. self.conn.modules.sys.stdout = self.orig_stdout
  158. self.conn.modules.sys.stderr = self.orig_stderr
  159. def __enter__(self):
  160. return self
  161. def __exit__(self, t, v, tb):
  162. self.restore()
  163. #== compatibility with python 2.4 ==
  164. #@contextmanager
  165. #def redirected_stdio(conn):
  166. # orig_stdin = conn.modules.sys.stdin
  167. # orig_stdout = conn.modules.sys.stdout
  168. # orig_stderr = conn.modules.sys.stderr
  169. # try:
  170. # conn.modules.sys.stdin = sys.stdin
  171. # conn.modules.sys.stdout = sys.stdout
  172. # conn.modules.sys.stderr = sys.stderr
  173. # yield
  174. # finally:
  175. # conn.modules.sys.stdin = orig_stdin
  176. # conn.modules.sys.stdout = orig_stdout
  177. # conn.modules.sys.stderr = orig_stderr
  178. def pm(conn):
  179. """pdb.pm on a remote exception"""
  180. #pdb.post_mortem(conn.root.getconn()._last_traceback)
  181. redir = redirected_stdio(conn)
  182. try:
  183. conn.modules.pdb.post_mortem(conn.root.getconn()._last_traceback)
  184. finally:
  185. redir.restore()
  186. def interact(conn, namespace = None):
  187. """remote interactive interpreter"""
  188. if namespace is None:
  189. namespace = {}
  190. namespace["conn"] = conn
  191. redir = redirected_stdio(conn)
  192. try:
  193. conn.execute("""def _rinteract(ns):
  194. import code
  195. code.interact(local = dict(ns))""")
  196. conn.namespace["_rinteract"](namespace)
  197. finally:
  198. redir.restore()