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)
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
$(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
mv gevent._semaphore.* gevent/
rm gevent/_semaphore.py
gevent/gevent._util.c: gevent/_util.pyx
$(CYTHON) -o gevent._util.c gevent/_util.pyx
mv gevent._util.* gevent/
clean:
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.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._util.c gevent._util.h gevent/gevent._util.c gevent/gevent._util.h
doc:
cd doc && PYTHONPATH=.. make html
......
......@@ -9,5 +9,3 @@ move gevent\\_semaphore.pyx gevent\\_semaphore.py
cython -o gevent._semaphore.c gevent/_semaphore.py
move gevent._semaphore.* gevent
del gevent\\_semaphore.py
cython -o gevent._util.c gevent/_util.pyx
move gevent._util.* gevent
......@@ -37,6 +37,9 @@
- PyPy/CFFI: Fix a potential crash when using stat watchers.
- PyPy/CFFI: Encode unicode paths for stat watchers using
: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)
......
......@@ -247,3 +247,6 @@ reduce the cases of undocumented or non-standard behaviour.
:meth:`Popen.wait <gevent.subprocess.Popen.wait>` (a gevent extension
) now throws an exception, just like the documented parameter to the
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')
......
......@@ -18,6 +18,12 @@ from gevent.os import make_nonblocking
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):
RawIOBase.__init__(self)
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):
conn.sendall(b'msg2')
conn.close()
with gevent.Timeout(1.1):
with gevent.Timeout(2.1):
self.popen.wait()
finally:
server.close()
......
from __future__ import print_function
import os
import sys
import tempfile
import gc
import greentest
import gevent
from gevent.fileobject import FileObject, FileObjectThread
......@@ -12,43 +14,66 @@ PYPY = hasattr(sys, 'pypy_version_info')
class Test(greentest.TestCase):
def _test_del(self, **kwargs):
r, w = os.pipe()
s = FileObject(w, 'wb')
pipe = os.pipe()
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.flush()
if PYPY:
s.close()
else:
del s # Deliberately getting ResourceWarning under Py3
try:
os.close(w)
except OSError:
pass # expected, because SocketAdapter already closed it
s.flush()
except IOError:
# 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:
raise AssertionError('os.close(%r) must not succeed' % w)
os.close(w)
fobj = FileObject(r, 'rb')
self.assertEqual(fobj.read(), b'x')
fobj.close()
def test_del(self):
# Close should be true by default
self._test_del()
def test_del_close(self):
self._test_del(close=True)
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):
r, w = os.pipe()
s = FileObject(w, 'wb', close=False)
s.write(b'x')
s.flush()
if PYPY:
s.close()
else:
del s
os.close(w)
self.assertEqual(FileObject(r).read(), b'x')
self._test_del(close=False)
else:
def test_del_noclose(self):
try:
self._test_del(close=False)
self.fail("Shouldn't be able to create a FileObjectThread with close=False")
except TypeError as e:
self.assertEqual(str(e), 'FileObjectThread does not support close=False')
def test_newlines(self):
r, w = os.pipe()
......@@ -104,44 +129,5 @@ def writer(fobj, line):
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__':
greentest.main()
......@@ -506,7 +506,7 @@ class TestBasic(greentest.TestCase):
return return_value
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.dead
assert not g.started
......@@ -542,7 +542,7 @@ class TestBasic(greentest.TestCase):
assert g.successful()
assert g.value == 5
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):
link_test = []
......
......@@ -7,6 +7,7 @@ import sys
import struct
APPVEYOR = os.getenv('APPVEYOR')
LEAKTEST = os.getenv('GEVENTTEST_LEAKCHECK')
COVERAGE = os.getenv("COVERAGE_PROCESS_START")
PYPY = hasattr(sys, 'pypy_version_info')
......@@ -61,6 +62,16 @@ if sys.platform == 'win32':
'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:
# Py35 added socket.socketpair, all other releases
# are missing it
......@@ -163,7 +174,7 @@ if sys.version_info[:2] == (3, 3) and os.environ.get('TRAVIS') == 'true':
#'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 += [
# Timing issues on appveyor
'FLAKY test_selectors.py'
......
......@@ -416,12 +416,12 @@ elif PYPY:
include_package_data = True
run_make = 'gevent/gevent._semaphore.c gevent/gevent.ares.c'
else:
ext_modules = [CORE,
ARES,
Extension(name="gevent._semaphore",
sources=["gevent/gevent._semaphore.c"]),
Extension(name="gevent._util",
sources=["gevent/gevent._util.c"])]
ext_modules = [
CORE,
ARES,
Extension(name="gevent._semaphore",
sources=["gevent/gevent._semaphore.c"]),
]
include_package_data = False
run_make = True
......
......@@ -8,6 +8,7 @@ deps =
cython >= 0.23.4
coverage >= 4.0
psutil
cffi
whitelist_externals =
*
commands =
......@@ -34,9 +35,6 @@ commands =
[testenv:py27-cffi]
basepython =
python2.7
deps =
{[testenv]deps}
cffi
setenv =
GEVENT_CORE_CFFI_ONLY=1
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