Commit 8bd70641 authored by Jason Madden's avatar Jason Madden

Python 2: Correct errors from async closed sockets.

Previously we would create a AttributeError, instead of the preferred socket.error(EBADF).

Python 3 already did the right thing.

Discovered because of bad test coverage in test__hub.py that wasn't doing what we thought.

Incremental work on increasing test coverage.
parent 305b60f7
...@@ -37,9 +37,13 @@ exclude_lines = ...@@ -37,9 +37,13 @@ exclude_lines =
if sys.platform == 'win32': if sys.platform == 'win32':
if mswindows: if mswindows:
if is_windows: if is_windows:
if WIN:
self.fail
omit = omit =
# local.so sometimes gets included, and it can't be parsed # local.so sometimes gets included, and it can't be parsed
# as source, so it fails the whole process. # as source, so it fails the whole process.
# coverage 4.5 needs this specified here, 4.4.2 needed it in [run] # coverage 4.5 needs this specified here, 4.4.2 needed it in [run]
*.so *.so
/tmp/test_* /tmp/test_*
# Third-party vendored code
src/gevent/_tblib.py
...@@ -58,6 +58,11 @@ ...@@ -58,6 +58,11 @@
- Make `gevent.util.assert_switches` produce more informative messages - Make `gevent.util.assert_switches` produce more informative messages
when the assertion fails. when the assertion fails.
- Python 2: If a `gevent.socket` was closed asynchronously (in a
different greenlet or a hub callback), `AttributeError` could result
if the socket was already in use. Now the correct socket.error
should be raised.
1.3.7 (2018-10-12) 1.3.7 (2018-10-12)
================== ==================
......
...@@ -159,8 +159,10 @@ del sys ...@@ -159,8 +159,10 @@ del sys
# the following makes hidden imports visible to freezing tools like # the following makes hidden imports visible to freezing tools like
# py2exe. see https://github.com/gevent/gevent/issues/181 # py2exe. see https://github.com/gevent/gevent/issues/181
# This is not well maintained or tested, though, so it likely becomes
# outdated on each major release.
def __dependencies_for_freezing(): def __dependencies_for_freezing(): # pragma: no cover
# pylint:disable=unused-variable # pylint:disable=unused-variable
from gevent import core from gevent import core
from gevent import resolver_thread from gevent import resolver_thread
......
...@@ -181,7 +181,9 @@ class AbstractLinkable(object): ...@@ -181,7 +181,9 @@ class AbstractLinkable(object):
def _wait_return_value(self, waited, wait_success): def _wait_return_value(self, waited, wait_success):
# pylint:disable=unused-argument # pylint:disable=unused-argument
return None # Subclasses should override this to return a value from _wait.
# By default we return None.
return None # pragma: no cover all extent subclasses override
def _wait(self, timeout=None): def _wait(self, timeout=None):
if self.ready(): if self.ready():
......
...@@ -282,6 +282,10 @@ def _primitive_wait(watcher, timeout, timeout_exc, hub): ...@@ -282,6 +282,10 @@ def _primitive_wait(watcher, timeout, timeout_exc, hub):
# Suitable to be bound as an instance method # Suitable to be bound as an instance method
def wait_on_socket(socket, watcher, timeout_exc=None): def wait_on_socket(socket, watcher, timeout_exc=None):
if socket is None or watcher is None:
# test__hub TestCloseSocketWhilePolling, on Python 2; Python 3
# catches the EBADF differently.
raise ConcurrentObjectUseError("The socket has already been closed by another greenlet")
_primitive_wait(watcher, socket.timeout, _primitive_wait(watcher, socket.timeout,
timeout_exc if timeout_exc is not None else _NONE, timeout_exc if timeout_exc is not None else _NONE,
socket.hub) socket.hub)
......
...@@ -185,10 +185,9 @@ class socket(object): ...@@ -185,10 +185,9 @@ class socket(object):
_wait = _wait_on_socket _wait = _wait_on_socket
def accept(self): def accept(self):
sock = self._sock while 1:
while True:
try: try:
client_socket, address = sock.accept() client_socket, address = self._sock.accept()
break break
except error as ex: except error as ex:
if ex.args[0] != EWOULDBLOCK or self.timeout == 0.0: if ex.args[0] != EWOULDBLOCK or self.timeout == 0.0:
...@@ -212,10 +211,17 @@ class socket(object): ...@@ -212,10 +211,17 @@ class socket(object):
def close(self, _closedsocket=_closedsocket): def close(self, _closedsocket=_closedsocket):
# This function should not reference any globals. See Python issue #808164. # This function should not reference any globals. See Python issue #808164.
# Also break any reference to the loop.io objects. Our fileno, which they were # Also break any reference to the loop.io objects. Our fileno,
# tied to, is now free to be reused, so these objects are no longer functional. # which they were tied to, is now free to be reused, so these
# objects are no longer functional.
self._drop_events() self._drop_events()
s = self._sock s = self._sock
# Note that we change self._sock at this point. Methods *must not*
# cache `self._sock` separately from self._write_event/self._read_event,
# or they will be out of sync and we may get inappropriate errors.
# (See test__hub:TestCloseSocketWhilePolling for an example).
self._sock = _closedsocket() self._sock = _closedsocket()
if PYPY: if PYPY:
s._drop() s._drop()
...@@ -227,16 +233,16 @@ class socket(object): ...@@ -227,16 +233,16 @@ class socket(object):
def connect(self, address): def connect(self, address):
if self.timeout == 0.0: if self.timeout == 0.0:
return self._sock.connect(address) return self._sock.connect(address)
sock = self._sock
address = _socketcommon._resolve_addr(sock, address) address = _socketcommon._resolve_addr(self._sock, address)
timer = Timeout._start_new_or_dummy(self.timeout, timeout('timed out')) timer = Timeout._start_new_or_dummy(self.timeout, timeout('timed out'))
try: try:
while True: while 1:
err = sock.getsockopt(SOL_SOCKET, SO_ERROR) err = self._sock.getsockopt(SOL_SOCKET, SO_ERROR)
if err: if err:
raise error(err, strerror(err)) raise error(err, strerror(err))
result = sock.connect_ex(address) result = self._sock.connect_ex(address)
if not result or result == EISCONN: if not result or result == EISCONN:
break break
elif (result in (EWOULDBLOCK, EINPROGRESS, EALREADY)) or (result == EINVAL and is_windows): elif (result in (EWOULDBLOCK, EINPROGRESS, EALREADY)) or (result == EINVAL and is_windows):
...@@ -283,10 +289,9 @@ class socket(object): ...@@ -283,10 +289,9 @@ class socket(object):
return fobj return fobj
def recv(self, *args): def recv(self, *args):
sock = self._sock # keeping the reference so that fd is not closed during waiting while 1:
while True:
try: try:
return sock.recv(*args) return self._sock.recv(*args)
except error as ex: except error as ex:
if ex.args[0] != EWOULDBLOCK or self.timeout == 0.0: if ex.args[0] != EWOULDBLOCK or self.timeout == 0.0:
raise raise
...@@ -295,10 +300,9 @@ class socket(object): ...@@ -295,10 +300,9 @@ class socket(object):
self._wait(self._read_event) self._wait(self._read_event)
def recvfrom(self, *args): def recvfrom(self, *args):
sock = self._sock while 1:
while True:
try: try:
return sock.recvfrom(*args) return self._sock.recvfrom(*args)
except error as ex: except error as ex:
if ex.args[0] != EWOULDBLOCK or self.timeout == 0.0: if ex.args[0] != EWOULDBLOCK or self.timeout == 0.0:
raise raise
...@@ -306,10 +310,9 @@ class socket(object): ...@@ -306,10 +310,9 @@ class socket(object):
self._wait(self._read_event) self._wait(self._read_event)
def recvfrom_into(self, *args): def recvfrom_into(self, *args):
sock = self._sock while 1:
while True:
try: try:
return sock.recvfrom_into(*args) return self._sock.recvfrom_into(*args)
except error as ex: except error as ex:
if ex.args[0] != EWOULDBLOCK or self.timeout == 0.0: if ex.args[0] != EWOULDBLOCK or self.timeout == 0.0:
raise raise
...@@ -317,10 +320,9 @@ class socket(object): ...@@ -317,10 +320,9 @@ class socket(object):
self._wait(self._read_event) self._wait(self._read_event)
def recv_into(self, *args): def recv_into(self, *args):
sock = self._sock while 1:
while True:
try: try:
return sock.recv_into(*args) return self._sock.recv_into(*args)
except error as ex: except error as ex:
if ex.args[0] != EWOULDBLOCK or self.timeout == 0.0: if ex.args[0] != EWOULDBLOCK or self.timeout == 0.0:
raise raise
...@@ -328,18 +330,17 @@ class socket(object): ...@@ -328,18 +330,17 @@ class socket(object):
self._wait(self._read_event) self._wait(self._read_event)
def send(self, data, flags=0, timeout=timeout_default): def send(self, data, flags=0, timeout=timeout_default):
sock = self._sock
if timeout is timeout_default: if timeout is timeout_default:
timeout = self.timeout timeout = self.timeout
try: try:
return sock.send(data, flags) return self._sock.send(data, flags)
except error as ex: except error as ex:
if ex.args[0] not in _socketcommon.GSENDAGAIN or timeout == 0.0: if ex.args[0] not in _socketcommon.GSENDAGAIN or timeout == 0.0:
raise raise
sys.exc_clear() sys.exc_clear()
self._wait(self._write_event) self._wait(self._write_event)
try: try:
return sock.send(data, flags) return self._sock.send(data, flags)
except error as ex2: except error as ex2:
if ex2.args[0] == EWOULDBLOCK: if ex2.args[0] == EWOULDBLOCK:
return 0 return 0
...@@ -354,16 +355,15 @@ class socket(object): ...@@ -354,16 +355,15 @@ class socket(object):
return _socketcommon._sendall(self, data_memory, flags) return _socketcommon._sendall(self, data_memory, flags)
def sendto(self, *args): def sendto(self, *args):
sock = self._sock
try: try:
return sock.sendto(*args) return self._sock.sendto(*args)
except error as ex: except error as ex:
if ex.args[0] != EWOULDBLOCK or self.timeout == 0.0: if ex.args[0] != EWOULDBLOCK or self.timeout == 0.0:
raise raise
sys.exc_clear() sys.exc_clear()
self._wait(self._write_event) self._wait(self._write_event)
try: try:
return sock.sendto(*args) return self._sock.sendto(*args)
except error as ex2: except error as ex2:
if ex2.args[0] == EWOULDBLOCK: if ex2.args[0] == EWOULDBLOCK:
return 0 return 0
......
...@@ -122,6 +122,12 @@ from .flaky import reraiseFlakyTestRaceCondition ...@@ -122,6 +122,12 @@ from .flaky import reraiseFlakyTestRaceCondition
from .flaky import reraises_flaky_timeout from .flaky import reraises_flaky_timeout
from .flaky import reraises_flaky_race_condition from .flaky import reraises_flaky_race_condition
def gc_collect_if_needed():
"Collect garbage if necessary for destructors to run"
import gc
if PYPY: # pragma: no cover
gc.collect()
try: try:
from unittest import mock from unittest import mock
except ImportError: # Python 2 except ImportError: # Python 2
......
...@@ -466,6 +466,7 @@ if LIBUV: ...@@ -466,6 +466,7 @@ if LIBUV:
'test_socket.BufferIOTest.testRecvFromIntoBytearray', 'test_socket.BufferIOTest.testRecvFromIntoBytearray',
'test_socket.BufferIOTest.testRecvFromIntoArray', 'test_socket.BufferIOTest.testRecvFromIntoArray',
'test_socket.BufferIOTest.testRecvFromIntoEmptyBuffer', 'test_socket.BufferIOTest.testRecvFromIntoEmptyBuffer',
'test_socket.BufferIOTest.testRecvFromIntoMemoryview',
] ]
if PY3: if PY3:
......
...@@ -106,7 +106,7 @@ class TestPeriodicMonitoringThread(_AbstractTestPeriodicMonitoringThread, ...@@ -106,7 +106,7 @@ class TestPeriodicMonitoringThread(_AbstractTestPeriodicMonitoringThread,
self.assertRaises(ValueError, self.pmt.add_monitoring_function, lambda: None, -1) self.assertRaises(ValueError, self.pmt.add_monitoring_function, lambda: None, -1)
def f(): def f():
pass "Does nothing"
# Add # Add
self.pmt.add_monitoring_function(f, 1) self.pmt.add_monitoring_function(f, 1)
......
...@@ -8,7 +8,7 @@ if sys.argv[1:] == []: ...@@ -8,7 +8,7 @@ if sys.argv[1:] == []:
os.environ['GEVENT_BACKEND'] = 'select' os.environ['GEVENT_BACKEND'] = 'select'
popen = subprocess.Popen([sys.executable, __file__, '1']) popen = subprocess.Popen([sys.executable, __file__, '1'])
assert popen.wait() == 0, popen.poll() assert popen.wait() == 0, popen.poll()
else: else: # pragma: no cover
hub = gevent.get_hub() hub = gevent.get_hub()
if 'select' in gevent.core.supported_backends(): if 'select' in gevent.core.supported_backends():
assert hub.loop.backend == 'select', hub.loop.backend assert hub.loop.backend == 'select', hub.loop.backend
......
...@@ -35,8 +35,8 @@ class Test(greentest.TestCase): ...@@ -35,8 +35,8 @@ class Test(greentest.TestCase):
g = gevent.spawn(hello, expected_error) g = gevent.spawn(hello, expected_error)
g.join() g.join()
self.assert_error(ExpectedError, expected_error) self.assert_error(ExpectedError, expected_error)
if not isinstance(g.exception, ExpectedError): self.assertIsInstance(g.exception, ExpectedError)
raise g.exception
try: try:
raise raise
except: # pylint:disable=bare-except except: # pylint:disable=bare-except
......
...@@ -165,9 +165,6 @@ def return25(): ...@@ -165,9 +165,6 @@ def return25():
return 25 return 25
def sleep0():
return sleep(0)
class TestReturn_link(LinksTestCase): class TestReturn_link(LinksTestCase):
link_method = 'link' link_method = 'link'
...@@ -748,7 +745,7 @@ class TestBasic(greentest.TestCase): ...@@ -748,7 +745,7 @@ class TestBasic(greentest.TestCase):
raise ValueError("call stack is not deep enough") raise ValueError("call stack is not deep enough")
try: try:
ogf = greenlet.sys_getframe ogf = greenlet.sys_getframe
except AttributeError: except AttributeError: # pragma: no cover
# Must be running cython compiled # Must be running cython compiled
raise unittest.SkipTest("Cannot mock when Cython compiled") raise unittest.SkipTest("Cannot mock when Cython compiled")
greenlet.sys_getframe = get greenlet.sys_getframe = get
...@@ -806,7 +803,7 @@ class TestRef(greentest.TestCase): ...@@ -806,7 +803,7 @@ class TestRef(greentest.TestCase):
@greentest.skipOnPurePython("Needs C extension") @greentest.skipOnPurePython("Needs C extension")
class TestCExt(greentest.TestCase): class TestCExt(greentest.TestCase): # pragma: no cover (we only do coverage on pure-Python)
def test_c_extension(self): def test_c_extension(self):
self.assertEqual(greenlet.Greenlet.__module__, self.assertEqual(greenlet.Greenlet.__module__,
......
...@@ -149,13 +149,11 @@ class Test(greentest.TestCase): ...@@ -149,13 +149,11 @@ class Test(greentest.TestCase):
s = set() s = set()
s.add(p1) s.add(p1)
s.add(p2) s.add(p2)
try: with self.assertRaises(Timeout):
gevent.killall(s, timeout=0.5) gevent.killall(s, timeout=0.5)
except Timeout:
for g in s: for g in s:
assert not g.dead self.assertFalse(g.dead, g)
else:
self.fail("Should raise timeout")
class GreenletSubclass(gevent.Greenlet): class GreenletSubclass(gevent.Greenlet):
......
...@@ -36,10 +36,13 @@ DELAY = 0.1 ...@@ -36,10 +36,13 @@ DELAY = 0.1
class TestCloseSocketWhilePolling(greentest.TestCase): class TestCloseSocketWhilePolling(greentest.TestCase):
def test(self): def test(self):
with self.assertRaises(Exception): from gevent.exceptions import ConcurrentObjectUseError
sock = socket.socket()
self._close_on_teardown(sock) sock = socket.socket()
t = get_hub().loop.timer(0, sock.close) self._close_on_teardown(sock)
t = get_hub().loop.timer(0)
t.start(sock.close)
with self.assertRaises(socket.error):
try: try:
sock.connect(('python.org', 81)) sock.connect(('python.org', 81))
finally: finally:
......
...@@ -14,7 +14,7 @@ class TestSwitch(greentest.TestCase): ...@@ -14,7 +14,7 @@ class TestSwitch(greentest.TestCase):
self.switched_to = [False, False] self.switched_to = [False, False]
self.caught = None self.caught = None
def runner(self, i): def should_never_run(self, i): # pragma: no cover
self.switched_to[i] = True self.switched_to[i] = True
def check(self, g, g2): def check(self, g, g2):
...@@ -33,8 +33,8 @@ class TestSwitch(greentest.TestCase): ...@@ -33,8 +33,8 @@ class TestSwitch(greentest.TestCase):
def test_gevent_kill(self): def test_gevent_kill(self):
g = gevent.spawn(self.runner, 0) # create but do not switch to g = gevent.spawn(self.should_never_run, 0) # create but do not switch to
g2 = gevent.spawn(self.runner, 1) # create but do not switch to g2 = gevent.spawn(self.should_never_run, 1) # create but do not switch to
# Using gevent.kill # Using gevent.kill
gevent.kill(g) gevent.kill(g)
gevent.kill(g2) gevent.kill(g2)
...@@ -42,16 +42,16 @@ class TestSwitch(greentest.TestCase): ...@@ -42,16 +42,16 @@ class TestSwitch(greentest.TestCase):
def test_greenlet_kill(self): def test_greenlet_kill(self):
# killing directly # killing directly
g = gevent.spawn(self.runner, 0) g = gevent.spawn(self.should_never_run, 0)
g2 = gevent.spawn(self.runner, 1) g2 = gevent.spawn(self.should_never_run, 1)
g.kill() g.kill()
g2.kill() g2.kill()
self.check(g, g2) self.check(g, g2)
def test_throw(self): def test_throw(self):
# throwing # throwing
g = gevent.spawn(self.runner, 0) g = gevent.spawn(self.should_never_run, 0)
g2 = gevent.spawn(self.runner, 1) g2 = gevent.spawn(self.should_never_run, 1)
g.throw(gevent.GreenletExit) g.throw(gevent.GreenletExit)
g2.throw(gevent.GreenletExit) g2.throw(gevent.GreenletExit)
self.check(g, g2) self.check(g, g2)
......
...@@ -16,7 +16,7 @@ if not sys.argv[1:]: ...@@ -16,7 +16,7 @@ if not sys.argv[1:]:
# or __package__, falling back on __name__ and __path__\n return f(*args, **kwds)\n' # or __package__, falling back on __name__ and __path__\n return f(*args, **kwds)\n'
assert err == b'' or b'sys.excepthook' in err or b'Warning' in err, (out, err, code) assert err == b'' or b'sys.excepthook' in err or b'Warning' in err, (out, err, code)
elif sys.argv[1:] == ['subprocess']: elif sys.argv[1:] == ['subprocess']: # pragma: no cover
import gevent import gevent
import gevent.monkey import gevent.monkey
gevent.monkey.patch_all(sys=True) gevent.monkey.patch_all(sys=True)
...@@ -30,5 +30,5 @@ elif sys.argv[1:] == ['subprocess']: ...@@ -30,5 +30,5 @@ elif sys.argv[1:] == ['subprocess']:
gevent.spawn(printline).join() gevent.spawn(printline).join()
else: else: # pragma: no cover
sys.exit('Invalid arguments: %r' % (sys.argv, )) sys.exit('Invalid arguments: %r' % (sys.argv, ))
...@@ -8,7 +8,7 @@ handling of KeyboardInterrupt. ...@@ -8,7 +8,7 @@ handling of KeyboardInterrupt.
import sys import sys
if sys.argv[1:] == ['subprocess']: if sys.argv[1:] == ['subprocess']: # pragma: no cover
import gevent import gevent
def task(): def task():
......
...@@ -277,9 +277,8 @@ class TestGeventLocal(greentest.TestCase): ...@@ -277,9 +277,8 @@ class TestGeventLocal(greentest.TestCase):
my_local = MyLocal() my_local = MyLocal()
my_local.sentinel = None my_local.sentinel = None
if greentest.PYPY: greentest.gc_collect_if_needed()
import gc
gc.collect()
del created_sentinels[:] del created_sentinels[:]
del deleted_sentinels[:] del deleted_sentinels[:]
...@@ -298,8 +297,7 @@ class TestGeventLocal(greentest.TestCase): ...@@ -298,8 +297,7 @@ class TestGeventLocal(greentest.TestCase):
for g in greenlets: for g in greenlets:
assert not g.is_alive() assert not g.is_alive()
gevent.sleep() # let the callbacks run gevent.sleep() # let the callbacks run
if greentest.PYPY: greentest.gc_collect_if_needed()
gc.collect()
# The sentinels should be gone too # The sentinels should be gone too
self.assertEqual(len(deleted_sentinels), len(greenlets)) self.assertEqual(len(deleted_sentinels), len(greenlets))
...@@ -412,7 +410,7 @@ class TestLocalInterface(greentest.TestCase): ...@@ -412,7 +410,7 @@ class TestLocalInterface(greentest.TestCase):
@greentest.skipOnPurePython("Needs C extension") @greentest.skipOnPurePython("Needs C extension")
class TestCExt(greentest.TestCase): class TestCExt(greentest.TestCase): # pragma: no cover
def test_c_extension(self): def test_c_extension(self):
self.assertEqual(local.__module__, self.assertEqual(local.__module__,
......
...@@ -9,7 +9,7 @@ from gevent.timeout import Timeout ...@@ -9,7 +9,7 @@ from gevent.timeout import Timeout
hasattr(sys, 'gettotalrefcount'), hasattr(sys, 'gettotalrefcount'),
"Needs debug build" "Needs debug build"
) )
class TestQueue(TestCase): class TestQueue(TestCase): # pragma: no cover
# pylint:disable=bare-except,no-member # pylint:disable=bare-except,no-member
def test(self): def test(self):
......
...@@ -160,7 +160,7 @@ if hasattr(os, 'fork_and_watch'): ...@@ -160,7 +160,7 @@ if hasattr(os, 'fork_and_watch'):
# Can't assert on what the pid actually was, # Can't assert on what the pid actually was,
# our testrunner may have spawned multiple children. # our testrunner may have spawned multiple children.
os._reap_children(0) # make the leakchecker happy os._reap_children(0) # make the leakchecker happy
else: else: # pragma: no cover
gevent.sleep(2) gevent.sleep(2)
os._exit(0) os._exit(0)
......
...@@ -140,7 +140,7 @@ class TestQueue(TestCase): ...@@ -140,7 +140,7 @@ class TestQueue(TestCase):
with gevent.Timeout(0, RuntimeError()): with gevent.Timeout(0, RuntimeError()):
try: try:
result = q.get() result = q.get()
evt.set(result) evt.set(result) # pragma: no cover (should have raised)
except RuntimeError: except RuntimeError:
evt.set('timed out') evt.set('timed out')
...@@ -169,7 +169,7 @@ class TestQueue(TestCase): ...@@ -169,7 +169,7 @@ class TestQueue(TestCase):
with gevent.Timeout(0, RuntimeError()): with gevent.Timeout(0, RuntimeError()):
try: try:
result = q.get() result = q.get()
evt.set(result) evt.set(result) # pragma: no cover (should have raised)
except RuntimeError: except RuntimeError:
evt.set('timed out') evt.set('timed out')
...@@ -188,7 +188,7 @@ class TestQueue(TestCase): ...@@ -188,7 +188,7 @@ class TestQueue(TestCase):
with gevent.Timeout(0, RuntimeError()): with gevent.Timeout(0, RuntimeError()):
try: try:
result = q.get() result = q.get()
evt.set(result) evt.set(result) # pragma: no cover (should have raised)
except RuntimeError: except RuntimeError:
evt.set('timed out') evt.set('timed out')
......
...@@ -14,7 +14,7 @@ try: ...@@ -14,7 +14,7 @@ try:
s.close() s.close()
del s del s
assert r() is None assert r() is None
except AssertionError: except AssertionError: # pragma: no cover
import sys import sys
if hasattr(sys, 'pypy_version_info'): if hasattr(sys, 'pypy_version_info'):
# PyPy uses a non refcounted GC which may defer # PyPy uses a non refcounted GC which may defer
......
...@@ -8,7 +8,7 @@ import gevent.testing as greentest ...@@ -8,7 +8,7 @@ import gevent.testing as greentest
def readall(sock, _): def readall(sock, _):
while sock.recv(1024): while sock.recv(1024):
pass pass # pragma: no cover we never actually send the data
sock.close() sock.close()
......
import sys import sys
if 'runtestcase' in sys.argv[1:]: if 'runtestcase' in sys.argv[1:]: # pragma: no cover
import gevent import gevent
import gevent.subprocess import gevent.subprocess
gevent.spawn(sys.exit, 'bye') gevent.spawn(sys.exit, 'bye')
...@@ -15,6 +15,6 @@ else: ...@@ -15,6 +15,6 @@ else:
__file__, 'runtestcase'], __file__, 'runtestcase'],
stderr=subprocess.PIPE).communicate() stderr=subprocess.PIPE).communicate()
if b'refs' in err: # Something to do with debug mode python builds? if b'refs' in err: # Something to do with debug mode python builds?
assert err.startswith(b'bye'), repr(err) assert err.startswith(b'bye'), repr(err) # pragma: no cover
else: else:
assert err.strip() == b'bye', repr(err) assert err.strip() == b'bye', repr(err)
...@@ -45,7 +45,7 @@ class _FakeTimer(object): ...@@ -45,7 +45,7 @@ class _FakeTimer(object):
@property @property
def seconds(self): def seconds(self):
return None "Always returns None"
timer = exception = seconds timer = exception = seconds
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment