Skip to content

Commit

Permalink
drop python 2.4 support; add MockClassicConnection
Browse files Browse the repository at this point in the history
  • Loading branch information
tomerfiliba committed Aug 9, 2013
1 parent fa2753e commit bfedb34
Show file tree
Hide file tree
Showing 17 changed files with 127 additions and 357 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ python:
- "3.2"
- "3.3"

install:
install:
- pip install six
- pip install -e git+git://github.com/tomerfiliba/plumbum.git#egg=plumbum

Expand Down
8 changes: 6 additions & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
connected to it, in just one line of code. All you need is SSH access and a Python interpreter
installed on the remote machine.

* Dropping python 2.4 support. RPyC now requires python 2.5 - 3.3.
* Dropping Python 2.4 support. RPyC now requires Python 2.5 - 3.3.

* rpycd - a well-behaved daemon for ``rpyc_classic.py``, based on
`python-daemon <http://pypi.python.org/pypi/python-daemon/>`_
Expand All @@ -21,12 +21,16 @@

* ``scripts`` directory renamed ``bin``

* Introducing ``Splitbrain Python`` - running code on remote machines transparently
* Introducing ``Splitbrain Python`` - running code on remote machines transparently. Although tested,
it is still considered experimental.

* Removing the ``BgServerThread`` and all polling/timeout hacks in favor of a "global background
reactor thread" that handles all incoming transport from all connections. This should solve
all threading issues once and for all.

* Added ``MockClassicConnection`` - a mock RPyC "connection" that allows you to write code that runs
either locally or remotely without modification


3.2.3
-----
Expand Down
8 changes: 4 additions & 4 deletions bin/rpycd.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
#!/usr/bin/env python
from __future__ import with_statement
from rpyc.utils.server import ThreadedServer, ForkingServer
from rpyc.core.service import SlaveService
from rpyc.lib import setup_logger
from six.moves import configparser
import daemon
import lockfile
import sys
import signal
from rpyc.utils.server import ThreadedServer, ForkingServer
from rpyc.core.service import SlaveService
from rpyc.lib import setup_logger
from six.moves import configparser


server = None
Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -147,5 +147,6 @@ Contents
screencasts
docs
api
whatsnew33


3 changes: 3 additions & 0 deletions docs/whatsnew33.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
What's New in RPyC 3.3
======================

2 changes: 1 addition & 1 deletion rpyc/core/brine.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
>>> x == z
True
"""
from rpyc.lib.compat import Struct, BytesIO, all, is_py3k, BYTES_LITERAL
from rpyc.lib.compat import Struct, BytesIO, is_py3k, BYTES_LITERAL


# singletons
Expand Down
43 changes: 20 additions & 23 deletions rpyc/core/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,13 +185,12 @@ def close(self, _catchall = True):
return
self._closed = True
try:
try:
self._async_request(consts.HANDLE_CLOSE)
except EOFError:
pass
except Exception:
if not _catchall:
raise
self._async_request(consts.HANDLE_CLOSE)
except EOFError:
pass
except Exception:
if not _catchall:
raise
finally:
self._cleanup(_anyway = True)

Expand Down Expand Up @@ -336,14 +335,13 @@ def _recv(self, timeout, wait_for_lock):
if not self._recvlock.acquire(wait_for_lock):
return None
try:
try:
if self._channel.poll(timeout):
data = self._channel.recv()
else:
data = None
except EOFError:
self.close()
raise
if self._channel.poll(timeout):
data = self._channel.recv()
else:
data = None
except EOFError:
self.close()
raise
finally:
self._recvlock.release()
return data
Expand Down Expand Up @@ -390,14 +388,13 @@ def serve_all(self):
"""Serves all requests and replies for as long as the connection is
alive."""
try:
try:
while True:
self.serve(0.1)
except (socket.error, select_error, IOError):
if not self.closed:
raise
except EOFError:
pass
while True:
self.serve(0.1)
except (socket.error, select_error, IOError):
if not self.closed:
raise
except EOFError:
pass
finally:
self.close()

Expand Down
21 changes: 5 additions & 16 deletions rpyc/lib/colls.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from __future__ import with_statement
import weakref
from threading import Lock

Expand Down Expand Up @@ -61,38 +62,26 @@ def __init__(self):
def __repr__(self):
return repr(self._dict)
def add(self, obj):
self._lock.acquire()
try:
with self._lock:
key = id(obj)
slot = self._dict.get(key, None)
if slot is None:
slot = [obj, 0]
else:
slot[1] += 1
self._dict[key] = slot
finally:
self._lock.release()
def clear(self):
self._lock.acquire()
try:
with self._lock:
self._dict.clear()
finally:
self._lock.release()
def decref(self, key):
self._lock.acquire()
try:
with self._lock:
slot = self._dict[key]
if slot[1] < 1:
del self._dict[key]
else:
slot[1] -= 1
self._dict[key] = slot
finally:
self._lock.release()
def __getitem__(self, key):
self._lock.acquire()
try:
with self._lock:
return self._dict[key][0]
finally:
self._lock.release()

9 changes: 0 additions & 9 deletions rpyc/lib/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,6 @@ def unpack(self, data):
def next(iterator):
return iterator.next()

try:
all = all
except NameError:
def all(seq):
for elem in seq:
if not elem:
return False
return True

try:
import cPickle as pickle
except ImportError:
Expand Down
119 changes: 50 additions & 69 deletions rpyc/utils/classic.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from __future__ import with_statement
import sys
import os
import inspect
from rpyc.lib.compat import pickle
from rpyc.lib.compat import pickle, execute, is_py3k
from rpyc import SlaveService
from rpyc.utils import factory
from rpyc.core.service import ModuleNamespace
from contextlib import contextmanager


DEFAULT_SERVER_PORT = 18812
Expand Down Expand Up @@ -275,83 +278,39 @@ def deliver(conn, localobj):
"""
return conn.modules["rpyc.lib.compat"].pickle.loads(pickle.dumps(localobj))

class redirected_stdio(object):
"""
@contextmanager
def redirected_stdio(conn):
r"""
Redirects the other party's ``stdin``, ``stdout`` and ``stderr`` to
those of the local party, so remote IO will occur locally. It was
originally written as a ``contextmanager``, but was turned into a class
for compatibility with python 2.4
Here's the context-manager::
@contextmanager
def redirected_stdio(conn):
orig_stdin = conn.modules.sys.stdin
orig_stdout = conn.modules.sys.stdout
orig_stderr = conn.modules.sys.stderr
try:
conn.modules.sys.stdin = sys.stdin
conn.modules.sys.stdout = sys.stdout
conn.modules.sys.stderr = sys.stderr
yield
finally:
conn.modules.sys.stdin = orig_stdin
conn.modules.sys.stdout = orig_stdout
conn.modules.sys.stderr = orig_stderr
those of the local party, so remote IO will occur locally.
Example usage::
with redirected_stdio(conn):
# remote IO will occur locally
or ::
redir = redirected_stdio(conn)
try:
# remote IO will occur locally
finally:
redir.restore()
conn.modules.sys.stdout.write("hello\n") # will be printed locally
"""
def __init__(self, conn):
"""
:param conn: the RPyC connection whose stdio will be redirected
"""
self._restored = True
self.conn = conn
self.orig_stdin = self.conn.modules.sys.stdin
self.orig_stdout = self.conn.modules.sys.stdout
self.orig_stderr = self.conn.modules.sys.stderr
self.conn.modules.sys.stdin = sys.stdin
self.conn.modules.sys.stdout = sys.stdout
self.conn.modules.sys.stderr = sys.stderr
self._restored = False
def __del__(self):
self.restore()
def restore(self):
"""Restores the redirection"""
if self._restored:
return
self._restored = True
self.conn.modules.sys.stdin = self.orig_stdin
self.conn.modules.sys.stdout = self.orig_stdout
self.conn.modules.sys.stderr = self.orig_stderr
def __enter__(self):
return self
def __exit__(self, t, v, tb):
self.restore()

orig_stdin = conn.modules.sys.stdin
orig_stdout = conn.modules.sys.stdout
orig_stderr = conn.modules.sys.stderr
try:
conn.modules.sys.stdin = sys.stdin
conn.modules.sys.stdout = sys.stdout
conn.modules.sys.stderr = sys.stderr
yield
finally:
conn.modules.sys.stdin = orig_stdin
conn.modules.sys.stdout = orig_stdout
conn.modules.sys.stderr = orig_stderr

def pm(conn):
"""same as ``pdb.pm()`` but on a remote exception
:param conn: the RPyC connection
"""
#pdb.post_mortem(conn.root.getconn()._last_traceback)
redir = redirected_stdio(conn)
try:
with redirected_stdio(conn):
conn.modules.pdb.post_mortem(conn.root.getconn()._last_traceback)
finally:
redir.restore()

def interact(conn, namespace = None):
"""remote interactive interpreter
Expand All @@ -362,12 +321,34 @@ def interact(conn, namespace = None):
if namespace is None:
namespace = {}
namespace["conn"] = conn
redir = redirected_stdio(conn)
try:
with redirected_stdio(conn):
conn.execute("""def _rinteract(ns):
import code
code.interact(local = dict(ns))""")
conn.namespace["_rinteract"](namespace)
finally:
redir.restore()

class MockClassicConnection(object):
"""Mock classic RPyC connection object. Useful when you want the same code to run remotely or locally.
"""
def __init__(self):
self._conn = None
self.namespace = {}
self.modules = ModuleNamespace(self.getmodule)
if is_py3k:
self.builtin = self.modules.builtins
else:
self.builtin = self.modules.__builtin__
self.builtins = self.builtin

def execute(self, text):
execute(text, self.namespace)
def eval(self, text):
return eval(text, self.namespace)
def getmodule(self, name):
return __import__(name, None, None, "*")
def getconn(self):
return None



16 changes: 7 additions & 9 deletions rpyc/utils/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,10 @@ def start(self):
raise ValueError("object disposed")
self.logger.debug("server started on %s:%s", *self.sock.getsockname()[:2])
try:
try:
self.active = True
self._work()
except KeyboardInterrupt:
self.logger.warn("User interrupt!")
self.active = True
self._work()
except KeyboardInterrupt:
self.logger.warn("User interrupt!")
finally:
self.active = False
self.logger.debug("server closed")
Expand Down Expand Up @@ -230,10 +229,9 @@ def _recv(self):
def _send(self, data, addrinfo):
sock2 = self._connected_sockets.pop(addrinfo)
try:
try:
sock2.send(data)
except (socket.error, socket.timeout):
pass
sock2.send(data)
except (socket.error, socket.timeout):
pass
finally:
sock2.close()

Expand Down
Loading

0 comments on commit bfedb34

Please sign in to comment.