diff --git a/asyncio/compat.py b/asyncio/compat.py new file mode 100644 index 0000000..660b7e7 --- /dev/null +++ b/asyncio/compat.py @@ -0,0 +1,17 @@ +"""Compatibility helpers for the different Python versions.""" + +import sys + +PY34 = sys.version_info >= (3, 4) +PY35 = sys.version_info >= (3, 5) + + +def flatten_list_bytes(list_of_data): + """Concatenate a sequence of bytes-like objects.""" + if not PY34: + # On Python 3.3 and older, bytes.join() doesn't handle + # memoryview. + list_of_data = ( + bytes(data) if isinstance(data, memoryview) else data + for data in list_of_data) + return b''.join(list_of_data) diff --git a/asyncio/coroutines.py b/asyncio/coroutines.py index 15475f2..e11b21b 100644 --- a/asyncio/coroutines.py +++ b/asyncio/coroutines.py @@ -9,14 +9,12 @@ import sys import traceback import types +from . import compat from . import events from . import futures from .log import logger -_PY35 = sys.version_info >= (3, 5) - - # Opcode of "yield from" instruction _YIELD_FROM = opcode.opmap['YIELD_FROM'] @@ -140,7 +138,7 @@ class CoroWrapper: def gi_code(self): return self.gen.gi_code - if _PY35: + if compat.PY35: __await__ = __iter__ # make compatible with 'await' expression diff --git a/asyncio/events.py b/asyncio/events.py index 496075b..d5f0d45 100644 --- a/asyncio/events.py +++ b/asyncio/events.py @@ -17,12 +17,11 @@ import sys import threading import traceback - -_PY34 = sys.version_info >= (3, 4) +from asyncio import compat def _get_function_source(func): - if _PY34: + if compat.PY34: func = inspect.unwrap(func) elif hasattr(func, '__wrapped__'): func = func.__wrapped__ @@ -31,7 +30,7 @@ def _get_function_source(func): return (code.co_filename, code.co_firstlineno) if isinstance(func, functools.partial): return _get_function_source(func.func) - if _PY34 and isinstance(func, functools.partialmethod): + if compat.PY34 and isinstance(func, functools.partialmethod): return _get_function_source(func.func) return None diff --git a/asyncio/futures.py b/asyncio/futures.py index d06828a..dbe06c4 100644 --- a/asyncio/futures.py +++ b/asyncio/futures.py @@ -11,6 +11,7 @@ import reprlib import sys import traceback +from . import compat from . import events # States for Future. @@ -18,9 +19,6 @@ _PENDING = 'PENDING' _CANCELLED = 'CANCELLED' _FINISHED = 'FINISHED' -_PY34 = sys.version_info >= (3, 4) -_PY35 = sys.version_info >= (3, 5) - Error = concurrent.futures._base.Error CancelledError = concurrent.futures.CancelledError TimeoutError = concurrent.futures.TimeoutError @@ -199,7 +197,7 @@ class Future: # On Python 3.3 and older, objects with a destructor part of a reference # cycle are never destroyed. It's not more the case on Python 3.4 thanks # to the PEP 442. - if _PY34: + if compat.PY34: def __del__(self): if not self._log_traceback: # set_exception() was not called, or result() or exception() @@ -352,7 +350,7 @@ class Future: self._exception = exception self._state = _FINISHED self._schedule_callbacks() - if _PY34: + if compat.PY34: self._log_traceback = True else: self._tb_logger = _TracebackLogger(self, exception) @@ -388,7 +386,7 @@ class Future: assert self.done(), "yield from wasn't used with future" return self.result() # May raise too. - if _PY35: + if compat.PY35: __await__ = __iter__ # make compatible with 'await' expression diff --git a/asyncio/locks.py b/asyncio/locks.py index b2e516b..cc6f2bf 100644 --- a/asyncio/locks.py +++ b/asyncio/locks.py @@ -5,14 +5,12 @@ __all__ = ['Lock', 'Event', 'Condition', 'Semaphore', 'BoundedSemaphore'] import collections import sys +from . import compat from . import events from . import futures from .coroutines import coroutine -_PY35 = sys.version_info >= (3, 5) - - class _ContextManager: """Context manager. @@ -70,7 +68,7 @@ class _ContextManagerMixin: yield from self.acquire() return _ContextManager(self) - if _PY35: + if compat.PY35: def __await__(self): # To make "with await lock" work. diff --git a/asyncio/streams.py b/asyncio/streams.py index 176c65e..6cd60c4 100644 --- a/asyncio/streams.py +++ b/asyncio/streams.py @@ -12,6 +12,7 @@ if hasattr(socket, 'AF_UNIX'): __all__.extend(['open_unix_connection', 'start_unix_server']) from . import coroutines +from . import compat from . import events from . import futures from . import protocols @@ -20,7 +21,6 @@ from .log import logger _DEFAULT_LIMIT = 2**16 -_PY35 = sys.version_info >= (3, 5) class IncompleteReadError(EOFError): @@ -488,7 +488,7 @@ class StreamReader: return b''.join(blocks) - if _PY35: + if compat.PY35: @coroutine def __aiter__(self): return self diff --git a/asyncio/tasks.py b/asyncio/tasks.py index d8193ba..1d5f865 100644 --- a/asyncio/tasks.py +++ b/asyncio/tasks.py @@ -16,13 +16,12 @@ import traceback import warnings import weakref +from . import compat from . import coroutines from . import events from . import futures from .coroutines import coroutine -_PY34 = (sys.version_info >= (3, 4)) - class Task(futures.Future): """A coroutine wrapped in a Future.""" @@ -83,7 +82,7 @@ class Task(futures.Future): # On Python 3.3 or older, objects with a destructor that are part of a # reference cycle are never destroyed. That's not the case any more on # Python 3.4 thanks to the PEP 442. - if _PY34: + if compat.PY34: def __del__(self): if self._state == futures._PENDING and self._log_destroy_pending: context = { diff --git a/asyncio/transports.py b/asyncio/transports.py index 22df3c7..7a28d90 100644 --- a/asyncio/transports.py +++ b/asyncio/transports.py @@ -2,7 +2,7 @@ import sys -_PY34 = sys.version_info >= (3, 4) +from asyncio import compat __all__ = ['BaseTransport', 'ReadTransport', 'WriteTransport', 'Transport', 'DatagramTransport', 'SubprocessTransport', @@ -94,12 +94,8 @@ class WriteTransport(BaseTransport): The default implementation concatenates the arguments and calls write() on the result. """ - if not _PY34: - # In Python 3.3, bytes.join() doesn't handle memoryview. - list_of_data = ( - bytes(data) if isinstance(data, memoryview) else data - for data in list_of_data) - self.write(b''.join(list_of_data)) + data = compat.flatten_list_bytes(list_of_data) + self.write(data) def write_eof(self): """Close the write end after flushing buffered data.