mirror of
https://github.com/Sneed-Group/Poodletooth-iLand
synced 2024-12-29 06:32:40 -06:00
322 lines
10 KiB
Python
322 lines
10 KiB
Python
from __future__ import absolute_import, with_statement
|
|
import sys
|
|
import os
|
|
from gevent.hub import get_hub
|
|
from gevent.socket import EBADF
|
|
from gevent.os import _read, _write, ignored_errors
|
|
from gevent.lock import Semaphore, DummySemaphore
|
|
|
|
|
|
try:
|
|
from fcntl import fcntl
|
|
except ImportError:
|
|
fcntl = None
|
|
|
|
|
|
__all__ = ['FileObjectPosix',
|
|
'FileObjectThread',
|
|
'FileObject']
|
|
|
|
|
|
if fcntl is None:
|
|
|
|
__all__.remove('FileObjectPosix')
|
|
|
|
else:
|
|
|
|
from gevent.socket import _fileobject, _get_memory
|
|
cancel_wait_ex = IOError(EBADF, 'File descriptor was closed in another greenlet')
|
|
from gevent.os import make_nonblocking
|
|
|
|
try:
|
|
from gevent._util import SocketAdapter__del__, noop
|
|
except ImportError:
|
|
SocketAdapter__del__ = None
|
|
noop = None
|
|
|
|
from types import UnboundMethodType
|
|
|
|
class NA(object):
|
|
|
|
def __repr__(self):
|
|
return 'N/A'
|
|
|
|
NA = NA()
|
|
|
|
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, (int, long)):
|
|
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)
|
|
|
|
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 close(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()
|
|
bytes_total = len(data)
|
|
bytes_written = 0
|
|
while True:
|
|
try:
|
|
bytes_written += _write(fileno, _get_memory(data, bytes_written))
|
|
except (IOError, OSError):
|
|
code = sys.exc_info()[1].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):
|
|
code = sys.exc_info()[1].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('\n'):
|
|
data = data[1:]
|
|
if not data:
|
|
return self.recv(size)
|
|
if data.endswith('\r'):
|
|
self._eat_newline = True
|
|
return self._translate_newlines(data)
|
|
self.hub.wait(self._read_event)
|
|
|
|
def _translate_newlines(self, data):
|
|
data = data.replace("\r\n", "\n")
|
|
data = data.replace("\r", "\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)
|
|
|
|
class FileObjectPosix(_fileobject):
|
|
|
|
def __init__(self, fobj, mode='rb', bufsize=-1, close=True):
|
|
if isinstance(fobj, (int, long)):
|
|
fileno = fobj
|
|
fobj = None
|
|
else:
|
|
fileno = fobj.fileno()
|
|
sock = SocketAdapter(fileno, mode, close=close)
|
|
self._fobj = fobj
|
|
self._closed = False
|
|
_fileobject.__init__(self, sock, mode=mode, bufsize=bufsize, close=close)
|
|
|
|
def __repr__(self):
|
|
if self._sock is None:
|
|
return '<%s closed>' % self.__class__.__name__
|
|
elif self._fobj is None:
|
|
return '<%s %s>' % (self.__class__.__name__, self._sock)
|
|
else:
|
|
return '<%s %s _fobj=%r>' % (self.__class__.__name__, self._sock, self._fobj)
|
|
|
|
def close(self):
|
|
if self._closed:
|
|
# make sure close() is only ran once when called concurrently
|
|
# cannot rely on self._sock for this because we need to keep that until flush() is done
|
|
return
|
|
self._closed = True
|
|
sock = self._sock
|
|
if sock is None:
|
|
return
|
|
try:
|
|
self.flush()
|
|
finally:
|
|
if self._fobj is not None or not self._close:
|
|
sock.detach()
|
|
self._sock = None
|
|
self._fobj = None
|
|
|
|
def __getattr__(self, item):
|
|
assert item != '_fobj'
|
|
if self._fobj is None:
|
|
raise FileObjectClosed
|
|
return getattr(self._fobj, item)
|
|
|
|
if not noop:
|
|
|
|
def __del__(self):
|
|
# disable _fileobject's __del__
|
|
pass
|
|
|
|
if noop:
|
|
FileObjectPosix.__del__ = UnboundMethodType(FileObjectPosix, None, noop)
|
|
|
|
|
|
class FileObjectThread(object):
|
|
|
|
def __init__(self, fobj, *args, **kwargs):
|
|
self._close = kwargs.pop('close', True)
|
|
self.threadpool = kwargs.pop('threadpool', None)
|
|
self.lock = kwargs.pop('lock', True)
|
|
if kwargs:
|
|
raise TypeError('Unexpected arguments: %r' % kwargs.keys())
|
|
if self.lock is True:
|
|
self.lock = Semaphore()
|
|
elif not self.lock:
|
|
self.lock = DummySemaphore()
|
|
if not hasattr(self.lock, '__enter__'):
|
|
raise TypeError('Expected a Semaphore or boolean, got %r' % type(self.lock))
|
|
if isinstance(fobj, (int, long)):
|
|
if not self._close:
|
|
# we cannot do this, since fdopen object will close the descriptor
|
|
raise TypeError('FileObjectThread does not support close=False')
|
|
fobj = os.fdopen(fobj, *args)
|
|
self._fobj = fobj
|
|
if self.threadpool is None:
|
|
self.threadpool = get_hub().threadpool
|
|
|
|
def _apply(self, func, args=None, kwargs=None):
|
|
with self.lock:
|
|
return self.threadpool.apply_e(BaseException, func, args, kwargs)
|
|
|
|
def close(self):
|
|
fobj = self._fobj
|
|
if fobj is None:
|
|
return
|
|
self._fobj = None
|
|
try:
|
|
self.flush(_fobj=fobj)
|
|
finally:
|
|
if self._close:
|
|
fobj.close()
|
|
|
|
def flush(self, _fobj=None):
|
|
if _fobj is not None:
|
|
fobj = _fobj
|
|
else:
|
|
fobj = self._fobj
|
|
if fobj is None:
|
|
raise FileObjectClosed
|
|
return self._apply(fobj.flush)
|
|
|
|
def __repr__(self):
|
|
return '<%s _fobj=%r threadpool=%r>' % (self.__class__.__name__, self._fobj, self.threadpool)
|
|
|
|
def __getattr__(self, item):
|
|
assert item != '_fobj'
|
|
if self._fobj is None:
|
|
raise FileObjectClosed
|
|
return getattr(self._fobj, item)
|
|
|
|
for method in ['read', 'readinto', 'readline', 'readlines', 'write', 'writelines', 'xreadlines']:
|
|
|
|
exec '''def %s(self, *args, **kwargs):
|
|
fobj = self._fobj
|
|
if fobj is None:
|
|
raise FileObjectClosed
|
|
return self._apply(fobj.%s, args, kwargs)
|
|
''' % (method, method)
|
|
|
|
def __iter__(self):
|
|
return self
|
|
|
|
def next(self):
|
|
line = self.readline()
|
|
if line:
|
|
return line
|
|
raise StopIteration
|
|
|
|
|
|
FileObjectClosed = IOError(EBADF, 'Bad file descriptor (FileObject was closed)')
|
|
|
|
|
|
try:
|
|
FileObject = FileObjectPosix
|
|
except NameError:
|
|
FileObject = FileObjectThread
|
|
|
|
|
|
class FileObjectBlock(object):
|
|
|
|
def __init__(self, fobj, *args, **kwargs):
|
|
self._close = kwargs.pop('close', True)
|
|
if kwargs:
|
|
raise TypeError('Unexpected arguments: %r' % kwargs.keys())
|
|
if isinstance(fobj, (int, long)):
|
|
if not self._close:
|
|
# we cannot do this, since fdopen object will close the descriptor
|
|
raise TypeError('FileObjectBlock does not support close=False')
|
|
fobj = os.fdopen(fobj, *args)
|
|
self._fobj = fobj
|
|
|
|
def __repr__(self):
|
|
return '<%s %r>' % (self._fobj, )
|
|
|
|
def __getattr__(self, item):
|
|
assert item != '_fobj'
|
|
if self._fobj is None:
|
|
raise FileObjectClosed
|
|
return getattr(self._fobj, item)
|
|
|
|
|
|
config = os.environ.get('GEVENT_FILE')
|
|
if config:
|
|
klass = {'thread': 'gevent.fileobject.FileObjectThread',
|
|
'posix': 'gevent.fileobject.FileObjectPosix',
|
|
'block': 'gevent.fileobject.FileObjectBlock'}.get(config, config)
|
|
if klass.startswith('gevent.fileobject.'):
|
|
FileObject = globals()[klass.split('.', 2)[-1]]
|
|
else:
|
|
from gevent.hub import _import
|
|
FileObject = _import(klass)
|
|
del klass
|