PK!!eNNnewio/__init__.pyfrom .api import * # noqa:F401,F403 from . import api __all__ = api.__all__ PK!+newio/_socket.pyfrom contextlib import contextmanager from socket import SOL_SOCKET, SO_ERROR from .api import wait_read, wait_write try: from ssl import SSLWantReadError, SSLWantWriteError WantRead = (BlockingIOError, SSLWantReadError) WantWrite = (BlockingIOError, SSLWantWriteError) except ImportError: WantRead = (BlockingIOError,) WantWrite = (BlockingIOError,) class Socket: ''' Non-blocking wrapper around a socket object. The original socket is put into a non-blocking mode when it's wrapped. ''' def __init__(self, sock): self._socket = sock self._socket.setblocking(False) self._fd = sock.fileno() # Commonly used bound methods self._socket_send = sock.send self._socket_recv = sock.recv def __repr__(self): return f'' def __getattr__(self, name): return getattr(self._socket, name) def fileno(self): return self._fd def settimeout(self, seconds): raise RuntimeError('Use newio.timeout() to set a timeout') def gettimeout(self): return None def dup(self): return type(self)(self._socket.dup()) @property def socket(self): return self._socket @contextmanager def blocking(self): ''' Allow temporary access to the underlying socket in blocking mode ''' try: self._socket.setblocking(True) yield self._socket finally: self._socket.setblocking(False) async def recv(self, maxsize, flags=0): while True: try: return self._socket_recv(maxsize, flags) except WantRead: await wait_read(self._fd) except WantWrite: await wait_write(self._fd) async def recv_into(self, buffer, nbytes=0, flags=0): while True: try: return self._socket.recv_into(buffer, nbytes, flags) except WantRead: await wait_read(self._fd) except WantWrite: await wait_write(self._fd) async def send(self, data, flags=0): while True: try: return self._socket_send(data, flags) except WantWrite: await wait_write(self._fd) except WantRead: await wait_read(self._fd) async def sendall(self, data, flags=0): buffer = memoryview(data).cast('b') while buffer: try: nsent = self._socket_send(buffer, flags) buffer = buffer[nsent:] except WantWrite: await wait_write(self._fd) except WantRead: await wait_read(self._fd) async def accept(self): while True: try: client, addr = self._socket.accept() return type(self)(client), addr except WantRead: await wait_read(self._fd) async def connect_ex(self, address): try: await self.connect(address) return 0 except OSError as e: return e.errno async def connect(self, address): try: result = self._socket.connect(address) if getattr(self, 'do_handshake_on_connect', False): await self.do_handshake() return result except WantWrite: await wait_write(self._fd) err = self._socket.getsockopt(SOL_SOCKET, SO_ERROR) if err != 0: raise OSError(err, 'Connect call failed %s' % (address,)) if getattr(self, 'do_handshake_on_connect', False): await self.do_handshake() async def recvfrom(self, buffersize, flags=0): while True: try: return self._socket.recvfrom(buffersize, flags) except WantRead: await wait_read(self._fd) except WantWrite: await wait_write(self._fd) async def recvfrom_into(self, buffer, bytes=0, flags=0): while True: try: return self._socket.recvfrom_into(buffer, bytes, flags) except WantRead: await wait_read(self._fd) except WantWrite: await wait_write(self._fd) async def sendto(self, bytes, flags_or_address, address=None): if address: flags = flags_or_address else: address = flags_or_address flags = 0 while True: try: return self._socket.sendto(bytes, flags, address) except WantWrite: await wait_write(self._fd) except WantRead: await wait_read(self._fd) async def recvmsg(self, bufsize, ancbufsize=0, flags=0): while True: try: return self._socket.recvmsg(bufsize, ancbufsize, flags) except WantRead: await wait_read(self._fd) async def recvmsg_into(self, buffers, ancbufsize=0, flags=0): while True: try: return self._socket.recvmsg_into(buffers, ancbufsize, flags) except WantRead: await wait_read(self._fd) async def sendmsg(self, buffers, ancdata=(), flags=0, address=None): while True: try: return self._socket.sendmsg(buffers, ancdata, flags, address) except WantRead: await wait_write(self._fd) # Special functions for SSL async def do_handshake(self): while True: try: return self._socket.do_handshake() except WantRead: await wait_read(self._fd) except WantWrite: await wait_write(self._fd) # Design discussion. Why make close() async? Partly it's to make the # programming interface highly uniform with the other methods (all of which # involve an await). It's also to provide consistency with the Stream # API below which requires an asynchronous close to properly flush I/O # buffers. async def close(self): if self._socket: self._socket.close() self._socket = None # This is declared as async for the same reason as close() async def shutdown(self, how): if self._socket: self._socket.shutdown(how) async def __aenter__(self): self._socket.__enter__() return self async def __aexit__(self, *args): if self._socket: self._socket.__exit__(*args) self._socket = None PK!bk newio/api.py'''Newio common API for users''' from . import syscall from .syscall import TaskCanceled, TaskTimeout, Timer, Task, Lounge __all__ = ( 'TaskCanceled', 'TaskTimeout', 'Task', 'Timer', 'Lounge', 'wait_read', 'wait_write', 'sleep', 'spawn', 'current_task', 'run_in_thread', 'run_in_process', 'run_in_asyncio', 'timeout_after', 'open_nursery', ) wait_read = syscall.nio_wait_read wait_write = syscall.nio_wait_write run_in_thread = syscall.nio_run_in_thread run_in_process = syscall.nio_run_in_process run_in_asyncio = syscall.nio_run_in_asyncio sleep = syscall.nio_sleep spawn = syscall.nio_spawn current_task = syscall.nio_current_task class timeout_after: '''Async context manager for task timeout Usage: async with timeout_after(3) as is_timeout: await sleep(5) if is_timeout: # task timeout ''' def __init__(self, seconds: float): self._seconds = seconds self._timer = None def __bool__(self): return self._timer is not None and self._timer.is_expired async def __aenter__(self): self._timer = await syscall.nio_timeout_after(self._seconds) return self async def __aexit__(self, exc_type, exc_value, traceback): await syscall.nio_unset_timer(self._timer) should_suppress = ( exc_value is not None and isinstance(exc_value, TaskTimeout) and exc_value.timer is self._timer ) return should_suppress def open_nursery(): return _Nursery() class _Nursery: '''Nursery is manager of tasks, it will take care of it spawned tasks. All tasks spawned by the nursery are ensure stoped after nursery exited. When nursery exiting, it will join spawned tasks, if join failed it will cancel spawned tasks. ''' def __init__(self): self._tasks = [] self._is_closed = False async def spawn(self, coro): '''Spawn task in the nursery''' if self._is_closed: raise RuntimeError('nursery already closed') task = await spawn(coro) self._tasks.append(task) return task async def _join(self): for task in self._tasks: if task.is_alive: await task.join() async def _cancel(self): for task in self._tasks: if task.is_alive: await task.cancel() async def __aenter__(self): if self._is_closed: raise RuntimeError('nursery already closed') return self async def __aexit__(self, *exc_info): self._is_closed = True try: await self._join() finally: await self._cancel() await self._join() PK!|Knewio/channel/__init__.pyfrom .error import ChannelClosed from .asyncio_channel import AsyncioChannel from .thread_channel import ThreadChannel __all__ = ('ThreadChannel', 'AsyncioChannel', 'ChannelClosed') PK!ĈZZnewio/channel/asyncio_broker.pyimport logging import asyncio from queue import Empty, Full from threading import Lock as ThreadLock from socket import socketpair from newio.api import spawn, run_in_asyncio from .error import ChannelClosed LOG = logging.getLogger(__name__) async def cond_wait(cond): await cond.acquire() try: await cond.wait() finally: cond.release() async def cond_notify(cond): await cond.acquire() try: cond.notify() finally: cond.release() async def cond_notify_all(cond): await cond.acquire() try: cond.notify_all() finally: cond.release() class AsyncioBroker: def __init__(self, queue): self._queue = queue self._notify_send, self._wakeup_send = socketpair() self._notify_recv, self._wakeup_recv = socketpair() self._notify_send.setblocking(False) self._wakeup_send.setblocking(False) self._notify_recv.setblocking(False) self._wakeup_recv.setblocking(False) self._send_cond = None self._recv_cond = None self._is_closed = False self._notify_lock = ThreadLock() self._consumer_broker_task = None self._producer_broker_task = None async def _init_cond(self): async def _init(): self._send_cond = asyncio.Condition() self._recv_cond = asyncio.Condition() await run_in_asyncio(_init()) async def _notify_all_cond(self): async def _notify_all(): await cond_notify_all(self._send_cond) await cond_notify_all(self._recv_cond) await run_in_asyncio(_notify_all()) async def start(self): await self._init_cond() self._consumer_broker_task = await spawn(self._consumer_broker()) self._producer_broker_task = await spawn(self._producer_broker()) async def shutdown(self, wait=True): LOG.debug('broker exiting') self._is_closed = True with self._notify_lock: self._notify_send.sendall(b'x') self._notify_recv.sendall(b'x') self._notify_send.close() self._notify_recv.close() await self._notify_all_cond() if wait: await self._consumer_broker_task.join() await self._producer_broker_task.join() self._wakeup_send.close() self._wakeup_recv.close() def notify_send(self): if self._is_closed: return with self._notify_lock: self._notify_send.sendall(b'1') def notify_recv(self): if self._is_closed: return with self._notify_lock: self._notify_recv.sendall(b'1') async def send(self, item): if self._is_closed: raise ChannelClosed() while True: try: self._queue.put_nowait(item) except Full: await cond_wait(self._send_cond) if self._is_closed: raise ChannelClosed() from None else: break await cond_notify(self._recv_cond) async def recv(self): while True: try: item = self._queue.get_nowait() except Empty: if self._is_closed: raise ChannelClosed() from None await cond_wait(self._recv_cond) else: break await cond_notify(self._send_cond) return item async def _consumer_broker(self): async def _broker(): LOG.debug('consumer broker started') loop = asyncio.get_event_loop() while True: nbytes = await loop.sock_recv(self._wakeup_recv, 1) if not nbytes or nbytes == b'x': LOG.debug('consumer broker exiting') break await cond_notify(self._recv_cond) await run_in_asyncio(_broker()) async def _producer_broker(self): async def _broker(): LOG.debug('producer broker started') loop = asyncio.get_event_loop() while True: nbytes = await loop.sock_recv(self._wakeup_send, 1) if not nbytes or nbytes == b'x': LOG.debug('producer broker exiting') break await cond_notify(self._send_cond) await run_in_asyncio(_broker()) PK!}II newio/channel/asyncio_channel.pyimport logging from queue import Queue as ThreadQueue from .error import ChannelClosed from .asyncio_broker import AsyncioBroker from .newio_broker import NewioBroker LOG = logging.getLogger(__name__) class AsyncioChannel: '''Message channel for communicating between asyncio tasks and newio tasks''' def __init__(self, maxsize=0): self._queue = ThreadQueue(maxsize) self._asyncio_broker = AsyncioBroker(self._queue) self._newio_broker = NewioBroker(self._queue) self._is_closed = False def __repr__(self): return f'' async def __aenter__(self): if self._is_closed: raise RuntimeError('Channel already closed') await self._asyncio_broker.start() await self._newio_broker.start() return self async def __aexit__(self, *exc_info): self._is_closed = True await self._asyncio_broker.shutdown() await self._newio_broker.shutdown() async def __aiter__(self): while True: try: yield (await self.recv()) except ChannelClosed: break async def asyncio_iter(self): while True: try: yield (await self.asyncio_recv()) except ChannelClosed: break async def asyncio_send(self, item): '''send in asyncio''' if self._is_closed: raise ChannelClosed() await self._asyncio_broker.send(item) self._newio_broker.notify_recv() async def asyncio_recv(self): '''recv in asyncio''' item = await self._asyncio_broker.recv() self._newio_broker.notify_send() return item async def send(self, item): '''send in newio task''' if self._is_closed: raise ChannelClosed() await self._newio_broker.send(item) self._asyncio_broker.notify_recv() async def recv(self): '''recv in newio task''' item = await self._newio_broker.recv() self._asyncio_broker.notify_send() return item PK!XNI__newio/channel/error.pyclass ChannelClosed(Exception): '''Exception raised when send or recv on closed channel''' PK!K newio/channel/newio_broker.pyimport logging from queue import Full, Empty from threading import Lock as ThreadLock from newio import spawn from newio.sync import Condition from newio.socket import socketpair from .error import ChannelClosed LOG = logging.getLogger(__name__) class NewioBroker: def __init__(self, queue): self._queue = queue self._notify_send, self._wakeup_send = socketpair() self._notify_recv, self._wakeup_recv = socketpair() self._send_cond = Condition() self._recv_cond = Condition() self._is_closed = False self._notify_lock = ThreadLock() self._consumer_broker_task = None self._producer_broker_task = None async def start(self): self._consumer_broker_task = await spawn(self._consumer_broker()) self._producer_broker_task = await spawn(self._producer_broker()) async def shutdown(self, wait=True): LOG.debug('broker exiting') self._is_closed = True with self._notify_lock: self._notify_send.socket.sendall(b'x') self._notify_recv.socket.sendall(b'x') await self._notify_send.close() await self._notify_recv.close() await self._send_cond.notify_all() await self._recv_cond.notify_all() if wait: await self._consumer_broker_task.join() await self._producer_broker_task.join() await self._wakeup_send.close() await self._wakeup_recv.close() def notify_send(self): if self._is_closed: return with self._notify_lock: self._notify_send.socket.sendall(b'1') def notify_recv(self): if self._is_closed: return with self._notify_lock: self._notify_recv.socket.sendall(b'1') async def send(self, item): if self._is_closed: raise ChannelClosed() while True: try: self._queue.put_nowait(item) except Full: await self._send_cond.wait() if self._is_closed: raise ChannelClosed() from None else: break await self._recv_cond.notify() async def recv(self): while True: try: item = self._queue.get_nowait() except Empty: if self._is_closed: raise ChannelClosed() from None await self._recv_cond.wait() else: break await self._send_cond.notify() return item async def _consumer_broker(self): LOG.debug('consumer broker started') while True: nbytes = await self._wakeup_recv.recv(1) if not nbytes or nbytes == b'x': LOG.debug('consumer broker exiting') break await self._recv_cond.notify() async def _producer_broker(self): LOG.debug('producer broker started') while True: nbytes = await self._wakeup_send.recv(1) if not nbytes or nbytes == b'x': LOG.debug('producer broker exiting') break await self._send_cond.notify() PK!ŽGGnewio/channel/thread_broker.pyimport logging from queue import Empty, Full from .error import ChannelClosed LOG = logging.getLogger(__name__) class ThreadBroker: def __init__(self, queue): self._queue = queue self._blocking_timeout = 0.1 self._is_closed = False async def start(self): '''do nothing''' async def shutdown(self, wait=True): LOG.debug('broker exiting') self._is_closed = True def notify_send(self): '''do nothing''' def notify_recv(self): '''do nothing''' def send(self, item): if self._is_closed: raise ChannelClosed() while True: try: self._queue.put(item, timeout=self._blocking_timeout) except Full: if self._is_closed: raise ChannelClosed() from None else: break def recv(self): if self._is_closed: try: return self._queue.get_nowait() except Empty: raise ChannelClosed() from None while True: try: item = self._queue.get(timeout=self._blocking_timeout) except Empty: if self._is_closed: raise ChannelClosed() from None else: break return item PK!:d  newio/channel/thread_channel.pyimport logging from queue import Queue as ThreadQueue from .error import ChannelClosed from .newio_broker import NewioBroker from .thread_broker import ThreadBroker LOG = logging.getLogger(__name__) class ThreadChannel: '''Message channel for communicating between threads and newio tasks''' def __init__(self, maxsize=0): self._queue = ThreadQueue(maxsize) self._thread_broker = ThreadBroker(self._queue) self._newio_broker = NewioBroker(self._queue) self._is_closed = False def __repr__(self): return f'' async def __aenter__(self): if self._is_closed: raise RuntimeError('Channel already closed') await self._thread_broker.start() await self._newio_broker.start() return self async def __aexit__(self, *exc_info): self._is_closed = True await self._thread_broker.shutdown() await self._newio_broker.shutdown() async def __aiter__(self): while True: try: yield (await self.recv()) except ChannelClosed: break def thread_iter(self): while True: try: yield self.thread_recv() except ChannelClosed: break def thread_send(self, item): '''send in thread''' if self._is_closed: raise ChannelClosed() self._thread_broker.send(item) self._newio_broker.notify_recv() def thread_recv(self): '''recv in thread''' item = self._thread_broker.recv() self._newio_broker.notify_send() return item async def send(self, item): '''send in newio task''' if self._is_closed: raise ChannelClosed() await self._newio_broker.send(item) self._thread_broker.notify_recv() async def recv(self): '''recv in newio task''' item = await self._newio_broker.recv() self._thread_broker.notify_send() return item PK!c newio/queue.pyfrom queue import Empty, Full from collections import deque from heapq import heappush, heappop from .sync import Condition __all__ = ('Queue', 'PriorityQueue', 'LifoQueue') class Queue: ''' A queue for communicating between tasks. ''' def __init__(self, maxsize=0): self.maxsize = maxsize self._get_waiting = Condition() self._put_waiting = Condition() self._join_waiting = Condition() self._task_count = 0 self._queue = self._init_internal_queue() def __repr__(self): name = type(self).__name__ return f'<{name}, size={self.qsize()}>' def _init_internal_queue(self): return deque() def qsize(self): return len(self._queue) def empty(self): return not self._queue def full(self): if self.maxsize is None: return False return self.qsize() >= self.maxsize async def get(self): while self.empty(): await self._get_waiting.wait() return await self.get_nowait() async def get_nowait(self): if self.empty(): raise Empty() result = self._get() await self._put_waiting.notify() return result def _get(self): return self._queue.popleft() async def join(self): if self._task_count > 0: await self._join_waiting.wait() async def put(self, item): while self.full(): await self._put_waiting.wait() await self.put_nowait(item) async def put_nowait(self, item): if self.full(): raise Full() self._put(item) self._task_count += 1 await self._get_waiting.notify() def _put(self, item): self._queue.append(item) async def task_done(self): self._task_count -= 1 if self._task_count <= 0: await self._join_waiting.notify_all() class PriorityQueue(Queue): ''' A Queue that outputs an item with the lowest priority first Items have to be orderable objects ''' def _init_internal_queue(self): return [] def _put(self, item): heappush(self._queue, item) def _get(self): return heappop(self._queue) class LifoQueue(Queue): ''' Last In First Out queue Retrieves most recently added items first ''' def _init_internal_queue(self): return [] def _put(self, item): self._queue.append(item) def _get(self): return self._queue.pop() PK!newio/socket.py''' A async version for the standard socket library. The entire contents of stdlib socket are made available here. The socket class is replaced by an async compatible version. ''' import socket as std __all__ = tuple(std.__all__) + ('Socket',) from socket import * # noqa: F401,F403 from functools import wraps from ._socket import Socket from .api import run_in_thread @wraps(std.socket) def socket(*args, **kwargs): return Socket(std.socket(*args, **kwargs)) @wraps(std.socketpair) def socketpair(*args, **kwargs): s1, s2 = std.socketpair(*args, **kwargs) return Socket(s1), Socket(s2) @wraps(std.fromfd) def fromfd(*args, **kwargs): return Socket(std.fromfd(*args, **kwargs)) @wraps(std.create_connection) async def create_connection(*args, **kwargs): sock = await run_in_thread(std.create_connection, *args, **kwargs) return Socket(sock) @wraps(std.getaddrinfo) async def getaddrinfo(*args, **kwargs): return await run_in_thread(std.getaddrinfo, *args, **kwargs) @wraps(std.getfqdn) async def getfqdn(*args, **kwargs): return await run_in_thread(std.getfqdn, *args, **kwargs) @wraps(std.gethostbyname) async def gethostbyname(*args, **kwargs): return await run_in_thread(std.gethostbyname, *args, **kwargs) @wraps(std.gethostbyname_ex) async def gethostbyname_ex(*args, **kwargs): return await run_in_thread(std.gethostbyname_ex, *args, **kwargs) @wraps(std.gethostname) async def gethostname(*args, **kwargs): return await run_in_thread(std.gethostname, *args, **kwargs) @wraps(std.gethostbyaddr) async def gethostbyaddr(*args, **kwargs): return await run_in_thread(std.gethostbyaddr, *args, **kwargs) @wraps(std.getnameinfo) async def getnameinfo(*args, **kwargs): return await run_in_thread(std.getnameinfo, *args, **kwargs) PK!-0[% % newio/ssl.py'''Wrapper around built-in SSL module''' __all__ = () from functools import wraps try: import ssl as _ssl from ssl import * # noqa: F401,F403 except ImportError: _ssl = None # We need these exceptions defined, even if ssl is not available. class SSLWantReadError(Exception): pass class SSLWantWriteError(Exception): pass from .api import run_in_thread from ._socket import Socket if _ssl: @wraps(_ssl.wrap_socket) async def wrap_socket(sock, *args, do_handshake_on_connect=True, **kwargs): if isinstance(sock, Socket): sock = sock._socket ssl_sock = _ssl.wrap_socket(sock, *args, do_handshake_on_connect=False, **kwargs) cssl_sock = Socket(ssl_sock) cssl_sock.do_handshake_on_connect = do_handshake_on_connect if do_handshake_on_connect and ssl_sock._connected: await cssl_sock.do_handshake() return cssl_sock @wraps(_ssl.get_server_certificate) async def get_server_certificate(*args, **kwargs): return await run_in_thread(_ssl.get_server_certificate, *args, **kwargs) # Small wrapper class to make sure the wrap_socket() method returns the right type class NewioSSLContext(object): def __init__(self, context): self._context = context def __getattr__(self, name): return getattr(self._context, name) async def wrap_socket(self, sock, *args, do_handshake_on_connect=True, **kwargs): sock = self._context.wrap_socket( sock._socket, *args, do_handshake_on_connect=False, **kwargs) csock = Socket(sock) csock.do_handshake_on_connect = do_handshake_on_connect if do_handshake_on_connect and sock._connected: await csock.do_handshake() return csock def __setattr__(self, name, value): if name == '_context': super().__setattr__(name, value) else: setattr(self._context, name, value) # Name alias def SSLContext(protocol): return NewioSSLContext(_ssl.SSLContext(protocol)) @wraps(_ssl.create_default_context) def create_default_context(*args, **kwargs): context = _ssl.create_default_context(*args, **kwargs) return NewioSSLContext(context) PK!M; newio/sync.py'''synchronization primitives''' from .syscall import Lounge class BrokenBarrierError(RuntimeError): '''Exception raised if the barrier is broken''' class Lock: def __init__(self): self._is_locked = False self._lounge = Lounge() async def __aenter__(self): await self.acquire() async def __aexit__(self, *exc_info): await self.release() async def acquire(self): if self._is_locked: await self._lounge.wait() self._is_locked = True async def release(self): if not self._is_locked: raise RuntimeError('release unlocked lock') self._is_locked = False await self._lounge.wake(1) def locked(self): return self._is_locked class Condition: def __init__(self): self._lounge = Lounge() async def wait(self): await self._lounge.wait() async def notify(self, n=1): await self._lounge.wake(n) async def notify_all(self): return await self.notify(Lounge.WAKE_ALL) class Semaphore: def __init__(self, value=1): if value < 0: raise ValueError('semaphore initial value must be >= 0') self._value = value self._lounge = Lounge() async def __aenter__(self): await self.acquire() async def __aexit__(self, *exc_info): await self.release() async def acquire(self): if self._value <= 0: await self._lounge.wait() self._value -= 1 async def release(self): self._value += 1 if self._value == 1: await self._lounge.wake(1) class BoundedSemaphore: def __init__(self, value=1): if value < 0: raise ValueError('semaphore initial value must be >= 0') self._value = value self._init_value = value self._lounge = Lounge() async def __aenter__(self): await self.acquire() async def __aexit__(self, *exc_info): await self.release() async def acquire(self): if self._value <= 0: await self._lounge.wait() self._value -= 1 async def release(self): if self._value >= self._init_value: raise RuntimeError('semaphore released too many times') self._value += 1 if self._value == 1: await self._lounge.wake(1) class Event: def __init__(self): self._lounge = Lounge() self._is_set = False def is_set(self): return self._is_set async def set(self): if self._is_set: return self._is_set = True await self._lounge.wake(Lounge.WAKE_ALL) def clear(self): self._is_set = False async def wait(self): if self._is_set: return await self._lounge.wait() class Barrier: def __init__(self, parties, action=None): self._lounge = Lounge() self._parties = parties self._action = action self._count = 0 self._is_broken = False self._is_filled = False async def wait(self): if self._is_broken: raise BrokenBarrierError() if self._is_filled: return 0 index = self._count self._count += 1 try: if self._count >= self._parties: self._is_filled = True await self._lounge.wake(Lounge.WAKE_ALL) if self._action is not None: self._action() else: await self._lounge.wait() except BaseException: self._is_broken = True raise finally: self._count -= 1 if self._is_broken: raise BrokenBarrierError() return index # TODO: implement barrier reset # async def reset(self): # pass async def abort(self): self._is_broken = True if self._count > 0: await self._lounge.wake(Lounge.WAKE_ALL) PK!v:YƘnewio/syscall.py'''All syscalls are defined here for communicating with kernel Implementation Note: the **_nio_ref_** attributes is used to bind user object and kernel object, so as to efficiently exchange between user space and kernel space. ''' import time from types import coroutine class TaskCanceled(BaseException): ''' Exception raised when task canceled. It's used to unwind coroutine stack and cleanup resources, the exception **MUST NOT** be catch without reraise. The exception directly inherits from BaseException instead of Exception so as to not be accidentally caught by code that catches Exception. ''' class TaskTimeout(Exception): '''Exception raised when task timeout.''' def __init__(self, timer): self.timer = timer class Timer: '''A wrapper for kernel timer object''' def __init__(self, kernel_timer): self._nio_ref_ = kernel_timer kernel_timer._nio_ref_ = self @property def seconds(self) -> float: '''timer expire duration in seconds''' return self._nio_ref_.seconds @property def deadline(self) -> float: '''timer deadline, based on time.monotonic()''' return self._nio_ref_.deadline @property def is_expired(self) -> bool: '''is timer expired''' return self._nio_ref_.is_expired @property def is_canceled(self) -> bool: '''is timer canceled''' return self._nio_ref_.is_canceled async def cancel(self): await nio_unset_timer(self) def __repr__(self): if self.is_expired: status = 'expired' elif self.is_canceled: status = 'canceled' else: remain = max(0, self.deadline - time.monotonic()) status = f'remain={remain:.3f}s' return f'' class Lounge: ''' Lounge - waiting room for tasks, borrowing from Linux kernel(Futex). It is a sychronization primitive used to coordinate tasks. ''' WAKE_ALL = -1 # A symbol for wake all tasks def __init__(self): self._nio_ref_ = None def __repr__(self): return f'' @property def ident(self) -> int: return id(self) async def wait(self): '''Wait on the lounge''' await nio_lounge_wait(self) async def wake(self, n: int): '''Wake up at most n tasks waiting on the lounge''' await nio_lounge_wake(self, n) class Task: '''A wrapper for kernel task''' def __init__(self, kernel_task): self._nio_ref_ = kernel_task kernel_task._nio_ref_ = self @property def ident(self) -> int: '''task id''' return self._nio_ref_.ident @property def name(self) -> str: '''task name''' return self._nio_ref_.name @property def is_alive(self) -> bool: '''is task alive''' return self._nio_ref_.is_alive @property def result(self): '''Get task result after task stoped''' if self.is_alive: raise RuntimeError('task is alive, can not get result') return self._nio_ref_.result @property def error(self): '''Get task error after task stoped''' if self.is_alive: raise RuntimeError('task is alive, can not get error') return self._nio_ref_.error async def join(self) -> None: '''Join the task''' await nio_join(self) async def cancel(self) -> None: '''Cancel the task''' await nio_cancel(self) def __repr__(self): if self.is_alive: state = 'alive' elif self.error: state = 'error' else: state = 'stoped' return f'' @coroutine def nio_wait_read(fd: int) -> None: '''Wait until fd readable Raises: TaskTimeout: task timeout TaskCanceled: task canceled ''' yield (nio_wait_read, fd) @coroutine def nio_wait_write(fd: int) -> None: '''Wait until fd writeable Raises: TaskTimeout: task timeout TaskCanceled: task canceled ''' yield (nio_wait_write, fd) @coroutine def nio_spawn(coro, cancel_after: float=None) -> Task: '''Spawn a task If cancel_after is not None, cancel the task after seconds Raises: TaskCanceled: task canceled ''' return (yield (nio_spawn, coro, cancel_after)) @coroutine def nio_current_task() -> Task: '''Get current task Raises: TaskCanceled: task canceled ''' return (yield (nio_current_task,)) @coroutine def nio_cancel(task: Task) -> None: '''Cancel a task This call will return immediately, before task stoped. This call will not raise TaskCanceled, so it's safe to cancel multiple tasks one by one. ''' yield (nio_cancel, task) @coroutine def nio_join(task: Task) -> None: '''Join a task, wait until the task stoped Raises: TaskTimeout: task timeout TaskCanceled: task canceled ''' yield (nio_join, task) @coroutine def nio_sleep(seconds: float=0) -> None: '''Sleep at least seconds Raises: TaskTimeout: task timeout TaskCanceled: task canceled ''' if seconds < 0: raise ValueError('can not sleep a negative seconds') yield (nio_sleep, seconds) @coroutine def nio_timeout_after(seconds: float) -> Timer: '''Set a timer for timeout current task after seconds Raises: TaskCanceled: task canceled ''' if seconds < 0: raise ValueError('can not set negative seconds timer') return (yield (nio_timeout_after, seconds)) @coroutine def nio_unset_timer(timer: Timer) -> None: '''Unset a timer for current task Raises: TaskCanceled: task canceled ''' yield (nio_unset_timer, timer) @coroutine def nio_lounge_wait(lounge: Lounge) -> None: '''Wait on lounge Raises: TaskTimeout: task timeout TaskCanceled: task canceled ''' yield (nio_lounge_wait, lounge) @coroutine def nio_lounge_wake(lounge: Lounge, n: int) -> None: '''Wake up at most n tasks waiting on lounge Raises: TaskCanceled: task canceled ''' yield (nio_lounge_wake, lounge, n) @coroutine def nio_run_in_thread(fn, *args, **kwargs): '''Run fn in thread executor Raises: TaskTimeout: task timeout TaskCanceled: task canceled ''' return (yield (nio_run_in_thread, fn, args, kwargs)) @coroutine def nio_run_in_process(fn, *args, **kwargs): '''Run fn in process executor Raises: TaskTimeout: task timeout TaskCanceled: task canceled ''' return (yield (nio_run_in_process, fn, args, kwargs)) @coroutine def nio_run_in_asyncio(coro): '''Run fn in asyncio executor Raises: TaskTimeout: task timeout TaskCanceled: task canceled ''' return (yield (nio_run_in_asyncio, coro)) PK!HǎASSnewio-0.3.0.dist-info/WHEEL A н#J@Z|Jmqvh&#hڭw fi4WZ^EgM_-]0(q7PK!H,ɭ>newio-0.3.0.dist-info/METADATANK 0@CP!(nk,tMbMgݼ߼nBrF5Nلȩ/,c)9uAXdV/BS:_U_UeӰMP}Rv;sx]0*VF qb|tbFPK!HT8Tnewio-0.3.0.dist-info/RECORDuɒX}= d1sYB&7ȕy黺02g8s-QѯȘ̨ `OZrN5:4n1l!I72Z~P!,vZM1InYC8]gul_i/|`ð &8m݂r'˵xFT\F]íj`)\ma`G,am,8:. (κlFY/`CْDzkyZF6ԯ; |Jko..YaK!gw2X4a:H38w2(Z1jh{x9^=̣!nY!I}꟫{SrD0%T}2nF[DzR& x8=>G]6r&z+w{%h޲,CP>b|E{~$&˦ܕ T8Vi8'pǣd;U Ͽ&i'NOUڵPM^#tD1(W]M<?$X ?PK!!eNNnewio/__init__.pyPK!+}newio/_socket.pyPK!bk newio/api.pyPK!|K%newio/channel/__init__.pyPK!ĈZZ&newio/channel/asyncio_broker.pyPK!}II %8newio/channel/asyncio_channel.pyPK!XNI__@newio/channel/error.pyPK!K ?Anewio/channel/newio_broker.pyPK!ŽGG Nnewio/channel/thread_broker.pyPK!:d  Snewio/channel/thread_channel.pyPK!c [newio/queue.pyPK!enewio/socket.pyPK!-0[% % !mnewio/ssl.pyPK!M; pvnewio/sync.pyPK!v:YƘ.newio/syscall.pyPK!HǎASSnewio-0.3.0.dist-info/WHEELPK!H,ɭ>newio-0.3.0.dist-info/METADATAPK!HT8Tinewio-0.3.0.dist-info/RECORDPKۦ