Skip to content

Commit

Permalink
Merge branch 'log-traceback'
Browse files Browse the repository at this point in the history
Add `log_traceback` option to print traceback using
`logger.error(..., exc_info=...)`.
  • Loading branch information
tkf committed Mar 17, 2013
2 parents ccf6dc5 + 53c0d0e commit a6cb4ea
Show file tree
Hide file tree
Showing 13 changed files with 90 additions and 33 deletions.
5 changes: 3 additions & 2 deletions epc/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,11 @@ class EPCClient(EPCCore):

thread_daemon = True

def __init__(self, socket_or_address=None, debugger=None):
def __init__(self, socket_or_address=None,
debugger=None, log_traceback=False):
if socket_or_address is not None:
self.connect(socket_or_address)
EPCCore.__init__(self, debugger)
EPCCore.__init__(self, debugger, log_traceback)

def connect(self, socket_or_address):
"""
Expand Down
3 changes: 2 additions & 1 deletion epc/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,10 @@ class EPCCore(EPCDispatcher):

logger = _logger

def __init__(self, debugger):
def __init__(self, debugger, log_traceback):
EPCDispatcher.__init__(self)
self.set_debugger(debugger)
self.log_traceback = log_traceback

def set_debugger(self, debugger):
"""
Expand Down
6 changes: 4 additions & 2 deletions epc/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,9 +245,11 @@ def _handle(self, sexp):
except Exception as err:
if self.handle_error(err):
return
if self.server.debugger or self.server.log_traceback:
exc_info = sys.exc_info()
self.logger.error('Unexpected error', exc_info=exc_info)
if self.server.debugger:
traceback = sys.exc_info()[2]
self.server.debugger.post_mortem(traceback)
self.server.debugger.post_mortem(exc_info[2])
name = 'epc-error' if uid is undefined else 'return-error'
self._send(name, uid, repr(err))

Expand Down
16 changes: 13 additions & 3 deletions epc/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,15 +112,15 @@ class EPCServer(SocketServer.TCPServer, EPCClientManager,
def __init__(self, server_address,
RequestHandlerClass=EPCHandler,
bind_and_activate=True,
debugger=None):
debugger=None, log_traceback=False):
# `BaseServer` (super class of `SocketServer`) will set
# `RequestHandlerClass` to the attribute `self.RequestHandlerClass`.
# This class is initialize in `BaseServer.finish_request` by
# `self.RequestHandlerClass(request, client_address, self)`.
SocketServer.TCPServer.__init__(
self, server_address, RequestHandlerClass, bind_and_activate)
EPCClientManager.__init__(self)
EPCCore.__init__(self, debugger)
EPCCore.__init__(self, debugger, log_traceback)
self.logger.debug('-' * 75)
self.logger.debug(
"EPCServer is initialized: server_address = %r",
Expand Down Expand Up @@ -201,9 +201,19 @@ def main(args=None):
help='server port. 0 means to pick up random port.')
parser.add_argument(
'--allow-dotted-names', default=False, action='store_true')
parser.add_argument(
'--pdb', dest='debugger', const='pdb', action='store_const',
help='start pdb when error occurs.')
parser.add_argument(
'--ipdb', dest='debugger', const='ipdb', action='store_const',
help='start ipdb when error occurs.')
parser.add_argument(
'--log-traceback', action='store_true', default=False)
ns = parser.parse_args(args)

server = EPCServer((ns.address, ns.port))
server = EPCServer((ns.address, ns.port),
debugger=ns.debugger,
log_traceback=ns.log_traceback)
server.register_instance(
__import__(ns.module),
allow_dotted_names=ns.allow_dotted_names)
Expand Down
6 changes: 3 additions & 3 deletions epc/tests/test_py2py.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,16 @@ class ThreadingPy2Py(object):
"""

def setup_connection(self):
self.server = ThreadingEPCServer(('localhost', 0))
def setup_connection(self, **kwds):
self.server = ThreadingEPCServer(('localhost', 0), **kwds)
self.server.daemon_threads = True
self.server_thread = newthread(self, target=self.server.serve_forever)
self.server_thread.start()

self.client_queue = q = Queue.Queue()
self.server.handle_client_connect = q.put

self.client = EPCClient(self.server.server_address)
self.client = EPCClient(self.server.server_address, **kwds)

def teardown_connection(self):
self.client.close()
Expand Down
19 changes: 12 additions & 7 deletions epc/tests/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.


import io
import socket

from sexpdata import Symbol, loads
Expand All @@ -26,8 +25,9 @@
from ..handler import encode_string, encode_object, BlockingCallback, \
ReturnError, EPCError, ReturnErrorCallerUnknown, EPCErrorCallerUnknown, \
CallerUnknown
from ..py3compat import PY3, utf8, Queue, nested
from .utils import mockedattr, logging_to_stdout, BaseTestCase
from ..py3compat import utf8, Queue, nested
from .utils import mockedattr, logging_to_stdout, CaptureStdIO, BaseTestCase, \
streamio


class TestEPCServerMisc(BaseTestCase):
Expand All @@ -48,10 +48,7 @@ def tearDown(self):
self.server.server_close()

def test_print_port(self):
if PY3:
stream = io.StringIO()
else:
stream = io.BytesIO()
stream = streamio()
self.server.print_port(stream)
self.assertEqual(stream.getvalue(),
'{0}\n'.format(self.server.server_address[1]))
Expand Down Expand Up @@ -216,6 +213,14 @@ def test_invalid_call_too_many_arguments(self):
def test_invalid_methods_too_many_arguments(self):
self.check_invalid_call('(methods {0} "extra value")'.format)

def test_log_traceback(self):
stdio = CaptureStdIO()
with nested(stdio, mockedattr(self.server, 'log_traceback', True)):
self.test_error_in_method()
log = stdio.read_stdout()
self.assertIn('ValueError: This is a bad method!', log)
self.assertIn('raise self.error_to_throw', log)


class TestEPCServerCallClient(BaseEPCServerTestCase):

Expand Down
53 changes: 43 additions & 10 deletions epc/tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@
import os
import sys
import functools
import io

import unittest
try:
import unittest
unittest.TestCase.assertIs
except AttributeError:
import unittest2 as unittest
from contextlib import contextmanager

from ..py3compat import Queue
from ..py3compat import Queue, PY3
from ..utils import newthread


Expand All @@ -43,6 +48,42 @@ def logging_to_stdout(logger):
return mockedattr(logger.handlers[0], 'stream', sys.stdout)


def streamio():
"""
Return `io.StringIO` for Python 3, otherwise `io.BytesIO`.
"""
if PY3:
return io.StringIO()
else:
return io.BytesIO()


class CaptureStdIO(object):

def __enter__(self):
self._orig_stdin = sys.stdin
self._orig_stdout = sys.stdout
self._orig_stderr = sys.stderr

self.stdin = sys.stdin = streamio()
self.stdout = sys.stdout = streamio()
self.stderr = sys.stderr = streamio()
return self

def __exit__(self, exc_type, exc_value, traceback):
sys.stdin = self._orig_stdin
sys.stdout = self._orig_stdout
sys.stderr = self._orig_stderr

def read_stdout(self):
self.stdout.seek(0)
return self.stdout.read()

def read_stderr(self):
self.stderr.seek(0)
return self.stderr.read()


class BaseTestCase(unittest.TestCase):

TRAVIS = os.getenv('TRAVIS')
Expand All @@ -52,14 +93,6 @@ class BaseTestCase(unittest.TestCase):
else:
timeout = 1

if not hasattr(unittest.TestCase, 'assertIs'):
def assertIs(self, expr1, expr2, msg=None):
self.assertTrue(expr1 is expr2, msg)

if not hasattr(unittest.TestCase, 'assertIsInstance'):
def assertIsInstance(self, obj, cls, msg=None):
self.assertTrue(isinstance(obj, cls), msg),


def skip(reason):
from nose import SkipTest
Expand Down
2 changes: 1 addition & 1 deletion examples/echo/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


def echo_server(address='localhost', port=0, logfilename='python-epc.log'):
server = EPCServer((address, port))
server = EPCServer((address, port), log_traceback=True)
server.logger.setLevel(logging.DEBUG)

ch = logging.FileHandler(filename=logfilename, mode='w')
Expand Down
2 changes: 1 addition & 1 deletion examples/epcs/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


def run_client(address, port, log, log_level):
client = EPCClient((address, port))
client = EPCClient((address, port), log_traceback=True)

if log:
level = getattr(logging, log_level.upper())
Expand Down
2 changes: 1 addition & 1 deletion examples/gtk/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def setup_gtk(self):
self.window.show()

def setup_epc(self):
self.server = ThreadingEPCServer(('localhost', 0))
self.server = ThreadingEPCServer(('localhost', 0), log_traceback=True)

# Setup logger
self.server.logger.setLevel(logging.DEBUG)
Expand Down
2 changes: 1 addition & 1 deletion examples/inprocess/echo.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

def echo_long_string(length, repeat):
p2p = ThreadingPy2Py()
p2p.setup_connection()
p2p.setup_connection(log_traceback=True)

@p2p.server.register_function
def echo(x):
Expand Down
3 changes: 2 additions & 1 deletion examples/quick-launcher/client.el
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

(defvar my-epc (epc:start-epc
(or (getenv "PYTHON") "python")
'("-m" "epc.server" "--allow-dotted-names" "os")))
'("-m" "epc.server" "--log-traceback"
"--allow-dotted-names" "os")))

(message "Start request")

Expand Down
4 changes: 4 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ deps =
argparse
commands = nosetests --with-doctest epc []
changedir = {envtmpdir}
[testenv:py26]
deps =
unittest2
{[testenv]deps}

0 comments on commit a6cb4ea

Please sign in to comment.