Commit c549de8e authored by Jason Madden's avatar Jason Madden

Remove unused _fileobject modules and improve the test case.

parent 57cc02ea
...@@ -12,7 +12,7 @@ export PATH:=$(BUILD_RUNTIMES)/snakepit:$(TOOLS):$(PATH) ...@@ -12,7 +12,7 @@ export PATH:=$(BUILD_RUNTIMES)/snakepit:$(TOOLS):$(PATH)
export LC_ALL=C.UTF-8 export LC_ALL=C.UTF-8
all: gevent/gevent.corecext.c gevent/gevent.ares.c gevent/gevent._semaphore.c gevent/gevent._util.c all: gevent/gevent.corecext.c gevent/gevent.ares.c gevent/gevent._semaphore.c
gevent/gevent.corecext.c: gevent/corecext.ppyx gevent/libev.pxd gevent/gevent.corecext.c: gevent/corecext.ppyx gevent/libev.pxd
$(PYTHON) util/cythonpp.py -o gevent.corecext.c gevent/corecext.ppyx $(PYTHON) util/cythonpp.py -o gevent.corecext.c gevent/corecext.ppyx
...@@ -33,16 +33,11 @@ gevent/gevent._semaphore.c: gevent/_semaphore.pyx gevent/_semaphore.pxd ...@@ -33,16 +33,11 @@ gevent/gevent._semaphore.c: gevent/_semaphore.pyx gevent/_semaphore.pxd
mv gevent._semaphore.* gevent/ mv gevent._semaphore.* gevent/
rm gevent/_semaphore.py rm gevent/_semaphore.py
gevent/gevent._util.c: gevent/_util.pyx
$(CYTHON) -o gevent._util.c gevent/_util.pyx
mv gevent._util.* gevent/
clean: clean:
rm -f corecext.pyx gevent/corecext.pyx rm -f corecext.pyx gevent/corecext.pyx
rm -f gevent.corecext.c gevent.corecext.h gevent/gevent.corecext.c gevent/gevent.corecext.h rm -f gevent.corecext.c gevent.corecext.h gevent/gevent.corecext.c gevent/gevent.corecext.h
rm -f gevent.ares.c gevent.ares.h gevent/gevent.ares.c gevent/gevent.ares.h rm -f gevent.ares.c gevent.ares.h gevent/gevent.ares.c gevent/gevent.ares.h
rm -f gevent._semaphore.c gevent._semaphore.h gevent/gevent._semaphore.c gevent/gevent._semaphore.h rm -f gevent._semaphore.c gevent._semaphore.h gevent/gevent._semaphore.c gevent/gevent._semaphore.h
rm -f gevent._util.c gevent._util.h gevent/gevent._util.c gevent/gevent._util.h
doc: doc:
cd doc && PYTHONPATH=.. make html cd doc && PYTHONPATH=.. make html
......
...@@ -9,5 +9,3 @@ move gevent\\_semaphore.pyx gevent\\_semaphore.py ...@@ -9,5 +9,3 @@ move gevent\\_semaphore.pyx gevent\\_semaphore.py
cython -o gevent._semaphore.c gevent/_semaphore.py cython -o gevent._semaphore.c gevent/_semaphore.py
move gevent._semaphore.* gevent move gevent._semaphore.* gevent
del gevent\\_semaphore.py del gevent\\_semaphore.py
cython -o gevent._util.c gevent/_util.pyx
move gevent._util.* gevent
...@@ -37,6 +37,9 @@ ...@@ -37,6 +37,9 @@
- PyPy/CFFI: Fix a potential crash when using stat watchers. - PyPy/CFFI: Fix a potential crash when using stat watchers.
- PyPy/CFFI: Encode unicode paths for stat watchers using - PyPy/CFFI: Encode unicode paths for stat watchers using
:meth:`sys.getfilesystemencoding` like the Cython backend. :meth:`sys.getfilesystemencoding` like the Cython backend.
- The internal implementation modules ``gevent._fileobject2`` and
``gevent._fileobject3`` were removed. These haven't been used or
tested since 1.1b1.
1.1rc1 (Nov 14, 2015) 1.1rc1 (Nov 14, 2015)
......
...@@ -247,3 +247,6 @@ reduce the cases of undocumented or non-standard behaviour. ...@@ -247,3 +247,6 @@ reduce the cases of undocumented or non-standard behaviour.
:meth:`Popen.wait <gevent.subprocess.Popen.wait>` (a gevent extension :meth:`Popen.wait <gevent.subprocess.Popen.wait>` (a gevent extension
) now throws an exception, just like the documented parameter to the ) now throws an exception, just like the documented parameter to the
same stdlib method in Python 3. same stdlib method in Python 3.
- The previously undocumented class
``gevent.fileobject.SocketAdapter`` has been removed.
from __future__ import absolute_import
import os
import sys
from types import UnboundMethodType
from gevent._fileobjectcommon import cancel_wait_ex
from gevent._socket2 import _fileobject
from gevent._socket2 import _get_memory
from gevent.hub import get_hub, integer_types
from gevent.os import _read
from gevent.os import _write
from gevent.os import ignored_errors
from gevent.os import make_nonblocking
from gevent.socket import EBADF
from gevent._fileobjectposix import FileObjectPosix
try:
from gevent._util import SocketAdapter__del__
except ImportError:
SocketAdapter__del__ = None
noop = None
class NA(object):
def __repr__(self):
return 'N/A'
NA = NA()
__all__ = ['FileObjectPosix', 'SocketAdapter']
class SocketAdapter(object):
"""Socket-like API on top of a file descriptor.
The main purpose of it is to re-use _fileobject to create proper cooperative file objects
from file descriptors on POSIX platforms.
"""
def __init__(self, fileno, mode=None, close=True):
if not isinstance(fileno, integer_types):
raise TypeError('fileno must be int: %r' % fileno)
self._fileno = fileno
self._mode = mode or 'rb'
self._close = close
self._translate = 'U' in self._mode
make_nonblocking(fileno)
self._eat_newline = False
self.hub = get_hub()
io = self.hub.loop.io
self._read_event = io(fileno, 1)
self._write_event = io(fileno, 2)
self._refcount = 1
def __repr__(self):
if self._fileno is None:
return '<%s at 0x%x closed>' % (self.__class__.__name__, id(self))
else:
args = (self.__class__.__name__, id(self), getattr(self, '_fileno', NA), getattr(self, '_mode', NA))
return '<%s at 0x%x (%r, %r)>' % args
def makefile(self, *args, **kwargs):
return _fileobject(self, *args, **kwargs)
def fileno(self):
result = self._fileno
if result is None:
raise IOError(EBADF, 'Bad file descriptor (%s object is closed)' % self.__class__.__name__)
return result
def detach(self):
x = self._fileno
self._fileno = None
return x
def _reuse(self):
self._refcount += 1
def _drop(self):
self._refcount -= 1
if self._refcount <= 0:
self._realclose()
def close(self):
self._drop()
def _realclose(self):
self.hub.cancel_wait(self._read_event, cancel_wait_ex)
self.hub.cancel_wait(self._write_event, cancel_wait_ex)
fileno = self._fileno
if fileno is not None:
self._fileno = None
if self._close:
os.close(fileno)
def sendall(self, data):
fileno = self.fileno()
data_memory = _get_memory(data)
bytes_total = len(data_memory)
bytes_written = 0
while True:
try:
bytes_written += _write(fileno, data_memory[bytes_written:])
except (IOError, OSError) as ex:
code = ex.args[0]
if code not in ignored_errors:
raise
sys.exc_clear()
if bytes_written >= bytes_total:
return
self.hub.wait(self._write_event)
def recv(self, size):
while True:
try:
data = _read(self.fileno(), size)
except (IOError, OSError) as ex:
code = ex.args[0]
if code not in ignored_errors:
raise
sys.exc_clear()
else:
if not self._translate or not data:
return data
if self._eat_newline:
self._eat_newline = False
if data.startswith(b'\n'):
data = data[1:]
if not data:
return self.recv(size)
if data.endswith(b'\r'):
self._eat_newline = True
return self._translate_newlines(data)
self.hub.wait(self._read_event)
def _translate_newlines(self, data):
data = data.replace(b"\r\n", b"\n")
data = data.replace(b"\r", b"\n")
return data
if not SocketAdapter__del__:
def __del__(self, close=os.close):
fileno = self._fileno
if fileno is not None:
close(fileno)
if SocketAdapter__del__:
SocketAdapter.__del__ = UnboundMethodType(SocketAdapter__del__, None, SocketAdapter)
try:
from gevent._fileobjectposix import FileObjectPosix
__all__ = ['FileObjectPosix', ]
except ImportError:
import sys
assert sys.platform.startswith('win'), "Should be able to import except on Windows"
__all__ = []
from gevent._socketcommon import EBADF
try:
from errno import EBADF
except ImportError:
EBADF = 9
cancel_wait_ex = IOError(EBADF, 'File descriptor was closed in another greenlet') cancel_wait_ex = IOError(EBADF, 'File descriptor was closed in another greenlet')
......
...@@ -18,6 +18,12 @@ from gevent.os import make_nonblocking ...@@ -18,6 +18,12 @@ from gevent.os import make_nonblocking
class GreenFileDescriptorIO(RawIOBase): class GreenFileDescriptorIO(RawIOBase):
# Note that RawIOBase has a __del__ method that calls
# self.close(). (In C implementations like CPython, this is
# the type's tp_dealloc slot; prior to Python 3, the object doesn't
# appear to have a __del__ method, even though it functionally does)
def __init__(self, fileno, mode='r', closefd=True): def __init__(self, fileno, mode='r', closefd=True):
RawIOBase.__init__(self) RawIOBase.__init__(self)
self._closed = False self._closed = False
......
from python cimport *
# Work around lack of absolute_import in Cython.
os = __import__('os', level=0)
# We implement __del__s in Cython so that they are safe against signals
def SocketAdapter__del__(self, close=os.close):
fileno = self._fileno
if fileno is not None:
self._fileno = None
if self._close:
close(fileno)
def noop(self):
pass
...@@ -54,7 +54,7 @@ class Test(util.TestServer): ...@@ -54,7 +54,7 @@ class Test(util.TestServer):
conn.sendall(b'msg2') conn.sendall(b'msg2')
conn.close() conn.close()
with gevent.Timeout(1.1): with gevent.Timeout(2.1):
self.popen.wait() self.popen.wait()
finally: finally:
server.close() server.close()
......
from __future__ import print_function
import os import os
import sys import sys
import tempfile import tempfile
import gc
import greentest import greentest
import gevent import gevent
from gevent.fileobject import FileObject, FileObjectThread from gevent.fileobject import FileObject, FileObjectThread
...@@ -12,43 +14,66 @@ PYPY = hasattr(sys, 'pypy_version_info') ...@@ -12,43 +14,66 @@ PYPY = hasattr(sys, 'pypy_version_info')
class Test(greentest.TestCase): class Test(greentest.TestCase):
def _test_del(self, **kwargs): def _test_del(self, **kwargs):
r, w = os.pipe() pipe = os.pipe()
s = FileObject(w, 'wb') try:
self._do_test_del(pipe, **kwargs)
finally:
for f in pipe:
try:
os.close(f)
except (IOError, OSError):
pass
def _do_test_del(self, pipe, **kwargs):
r, w = pipe
s = FileObject(w, 'wb', **kwargs)
ts = type(s)
s.write(b'x') s.write(b'x')
s.flush()
if PYPY:
s.close()
else:
del s # Deliberately getting ResourceWarning under Py3
try: try:
os.close(w) s.flush()
except OSError: except IOError:
pass # expected, because SocketAdapter already closed it # Sometimes seen on Windows/AppVeyor
print("Failed flushing fileobject", repr(s), file=sys.stderr)
import traceback
traceback.print_exc()
del s # Deliberately getting ResourceWarning with FileObject(Thread) under Py3
gc.collect() # PyPy
if kwargs.get("close", True):
try:
os.close(w)
except (OSError, IOError):
pass # expected, because FileObject already closed it
else:
raise AssertionError('os.close(%r) must not succeed on %r' % (w, ts))
else: else:
raise AssertionError('os.close(%r) must not succeed' % w) os.close(w)
fobj = FileObject(r, 'rb') fobj = FileObject(r, 'rb')
self.assertEqual(fobj.read(), b'x') self.assertEqual(fobj.read(), b'x')
fobj.close() fobj.close()
def test_del(self): def test_del(self):
# Close should be true by default
self._test_del() self._test_del()
def test_del_close(self): def test_del_close(self):
self._test_del(close=True) self._test_del(close=True)
if FileObject is not FileObjectThread: if FileObject is not FileObjectThread:
# FileObjectThread uses os.fdopen() when passed a file-descriptor, which returns
# an object with a destructor that can't be bypassed, so we can't even
# create one that way
def test_del_noclose(self): def test_del_noclose(self):
r, w = os.pipe() self._test_del(close=False)
s = FileObject(w, 'wb', close=False) else:
s.write(b'x') def test_del_noclose(self):
s.flush() try:
if PYPY: self._test_del(close=False)
s.close() self.fail("Shouldn't be able to create a FileObjectThread with close=False")
else: except TypeError as e:
del s self.assertEqual(str(e), 'FileObjectThread does not support close=False')
os.close(w)
self.assertEqual(FileObject(r).read(), b'x')
def test_newlines(self): def test_newlines(self):
r, w = os.pipe() r, w = os.pipe()
...@@ -104,44 +129,5 @@ def writer(fobj, line): ...@@ -104,44 +129,5 @@ def writer(fobj, line):
fobj.close() fobj.close()
try:
from gevent.fileobject import SocketAdapter
except ImportError:
pass
else:
class TestSocketAdapter(greentest.TestCase):
def _test_del(self, **kwargs):
r, w = os.pipe()
s = SocketAdapter(w)
s.sendall(b'x')
if PYPY:
s.close()
else:
del s
try:
os.close(w)
except OSError:
pass # expected, because SocketAdapter already closed it
else:
raise AssertionError('os.close(%r) must not succeed' % w)
self.assertEqual(FileObject(r).read(), b'x')
def test_del(self):
self._test_del()
def test_del_close(self):
self._test_del(close=True)
def test_del_noclose(self):
r, w = os.pipe()
s = SocketAdapter(w, close=False)
s.sendall(b'x')
del s
os.close(w)
self.assertEqual(FileObject(r).read(), b'x')
if __name__ == '__main__': if __name__ == '__main__':
greentest.main() greentest.main()
...@@ -506,7 +506,7 @@ class TestBasic(greentest.TestCase): ...@@ -506,7 +506,7 @@ class TestBasic(greentest.TestCase):
return return_value return return_value
g = gevent.Greenlet(func, 0.01, return_value=5) g = gevent.Greenlet(func, 0.01, return_value=5)
g.rawlink(link_test.append) # use rawlink to avoid timing issues on Appveyor g.rawlink(link_test.append) # use rawlink to avoid timing issues on Appveyor (not always successful)
assert not g, bool(g) assert not g, bool(g)
assert not g.dead assert not g.dead
assert not g.started assert not g.started
...@@ -542,7 +542,7 @@ class TestBasic(greentest.TestCase): ...@@ -542,7 +542,7 @@ class TestBasic(greentest.TestCase):
assert g.successful() assert g.successful()
assert g.value == 5 assert g.value == 5
assert g.exception is None # not changed assert g.exception is None # not changed
assert link_test == [g], link_test # changed assert link_test == [g] or greentest.RUNNING_ON_APPVEYOR, link_test # changed
def test_error_exit(self): def test_error_exit(self):
link_test = [] link_test = []
......
...@@ -7,6 +7,7 @@ import sys ...@@ -7,6 +7,7 @@ import sys
import struct import struct
APPVEYOR = os.getenv('APPVEYOR')
LEAKTEST = os.getenv('GEVENTTEST_LEAKCHECK') LEAKTEST = os.getenv('GEVENTTEST_LEAKCHECK')
COVERAGE = os.getenv("COVERAGE_PROCESS_START") COVERAGE = os.getenv("COVERAGE_PROCESS_START")
PYPY = hasattr(sys, 'pypy_version_info') PYPY = hasattr(sys, 'pypy_version_info')
...@@ -61,6 +62,16 @@ if sys.platform == 'win32': ...@@ -61,6 +62,16 @@ if sys.platform == 'win32':
'FLAKY test___example_servers.py', 'FLAKY test___example_servers.py',
] ]
if APPVEYOR:
# These both run on port 9000 and can step on each other...seems like the
# appveyor containers aren't fully port safe? Or it takes longer
# for the processes to shut down? Or we run them in a different order
# in the process pool than we do other places?
FAILING_TESTS += [
'FLAKY test__example_udp_client.py',
'FLAKY test__example_udp_server.py',
]
if not PY35: if not PY35:
# Py35 added socket.socketpair, all other releases # Py35 added socket.socketpair, all other releases
# are missing it # are missing it
...@@ -163,7 +174,7 @@ if sys.version_info[:2] == (3, 3) and os.environ.get('TRAVIS') == 'true': ...@@ -163,7 +174,7 @@ if sys.version_info[:2] == (3, 3) and os.environ.get('TRAVIS') == 'true':
#'test__refcount_core.py' #'test__refcount_core.py'
] ]
if sys.version_info[:2] >= (3, 4) and os.environ.get('APPVEYOR'): if sys.version_info[:2] >= (3, 4) and APPVEYOR:
FAILING_TESTS += [ FAILING_TESTS += [
# Timing issues on appveyor # Timing issues on appveyor
'FLAKY test_selectors.py' 'FLAKY test_selectors.py'
......
...@@ -416,12 +416,12 @@ elif PYPY: ...@@ -416,12 +416,12 @@ elif PYPY:
include_package_data = True include_package_data = True
run_make = 'gevent/gevent._semaphore.c gevent/gevent.ares.c' run_make = 'gevent/gevent._semaphore.c gevent/gevent.ares.c'
else: else:
ext_modules = [CORE, ext_modules = [
ARES, CORE,
Extension(name="gevent._semaphore", ARES,
sources=["gevent/gevent._semaphore.c"]), Extension(name="gevent._semaphore",
Extension(name="gevent._util", sources=["gevent/gevent._semaphore.c"]),
sources=["gevent/gevent._util.c"])] ]
include_package_data = False include_package_data = False
run_make = True run_make = True
......
...@@ -8,6 +8,7 @@ deps = ...@@ -8,6 +8,7 @@ deps =
cython >= 0.23.4 cython >= 0.23.4
coverage >= 4.0 coverage >= 4.0
psutil psutil
cffi
whitelist_externals = whitelist_externals =
* *
commands = commands =
...@@ -34,9 +35,6 @@ commands = ...@@ -34,9 +35,6 @@ commands =
[testenv:py27-cffi] [testenv:py27-cffi]
basepython = basepython =
python2.7 python2.7
deps =
{[testenv]deps}
cffi
setenv = setenv =
GEVENT_CORE_CFFI_ONLY=1 GEVENT_CORE_CFFI_ONLY=1
commands = commands =
......
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