def main():
parser = argparse.ArgumentParser()
parser.add_argument('torrent',
help='the .torrent to download')
parser.add_argument('-v', '--verbose', action='store_true',
help='enable verbose output')
args = parser.parse_args()
if args.verbose:
logging.basicConfig(level=logging.INFO)
loop = asyncio.get_event_loop()
client = TorrentClient(Torrent(args.torrent))
task = loop.create_task(client.start())
def signal_handler(*_):
logging.info('Exiting, please wait until everything is shutdown...')
client.stop()
task.cancel()
signal.signal(signal.SIGINT, signal_handler)
try:
loop.run_until_complete(task)
except CancelledError:
logging.warning('Event loop was canceled')
python类CancelledError()的实例源码
def __anext__(self):
# Read data from the socket. When we have enough data to parse, parse
# it and return the message. Until then keep reading from stream
while True:
try:
data = await self.reader.read(PeerStreamIterator.CHUNK_SIZE)
if data:
self.buffer += data
message = self.parse()
if message:
return message
else:
logging.debug('No data read from stream')
if self.buffer:
message = self.parse()
if message:
return message
raise StopAsyncIteration()
except ConnectionResetError:
logging.debug('Connection closed by peer')
raise StopAsyncIteration()
except CancelledError:
raise StopAsyncIteration()
except StopAsyncIteration as e:
# Cath to stop logging
raise e
except Exception:
logging.exception('Error when iterating over stream!')
raise StopAsyncIteration()
raise StopAsyncIteration()
def cancel(self):
"""Request that this task cancel itself.
This arranges for a CancelledError to be thrown into the
wrapped coroutine on the next cycle through the event loop.
The coroutine then has a chance to clean up or even deny
the request using try/except/finally.
Unlike Future.cancel, this does not guarantee that the
task will be cancelled: the exception might be caught and
acted upon, delaying cancellation of the task or preventing
cancellation completely. The task may also return a value or
raise a different exception.
Immediately after this method is called, Task.cancelled() will
not return True (unless the task was already cancelled). A
task will be marked as cancelled when the wrapped coroutine
terminates with a CancelledError exception (even if cancel()
was not called).
"""
if self.done():
return False
if self._fut_waiter is not None:
if self._fut_waiter.cancel():
# Leave self._fut_waiter; it may be a Task that
# catches and ignores the cancellation so we may have
# to cancel it again later.
return True
# It must be the case that self._step is already scheduled.
self._must_cancel = True
return True
def start_task(self, task: Task) -> None:
'''Initialize the task, queue it for execution, add the done callback,
and keep track of it for when tasks need to be stopped.'''
try:
Log.debug('task %s starting', task.name)
before = time.time()
task.counters['last_run'] = before
task.running = True
self.running_tasks.add(task)
await task.run_task()
Log.debug('task %s completed', task.name)
except CancelledError:
Log.debug('task %s cancelled', task.name)
except Exception:
Log.exception('unhandled exception in task %s', task.name)
finally:
self.running_tasks.discard(task)
task.running = False
task.task = None
after = time.time()
total = after - before
task.counters['last_completed'] = after
task.counters['duration'] = total
def monitor_tasks(self, interval: float=1.0) -> None:
'''Monitor all known tasks for run state. Ensure that enabled tasks
are running, and that disabled tasks are stopped.'''
Log.debug('monitor running')
while True:
try:
await asyncio.sleep(interval)
for name, task in self.all_tasks.items():
if self.terminate_on_finish:
if task in self.running_tasks and task.running:
await task.stop()
elif task.enabled:
if task not in self.running_tasks:
Log.debug('task %s enabled, restarting', task.name)
await self.insert(task)
else:
if task in self.running_tasks:
Log.debug('task %s disabled, stopping', task.name)
await task.stop()
if self.terminate_on_finish and not self.running_tasks:
Log.debug('all tasks completed, terminating')
break
except CancelledError:
Log.debug('monitor cancelled')
break
except Exception:
Log.exception('monitoring exception')
self.monitor = None
self.loop.call_later(0, self.terminate)
def run_task(self) -> None:
'''Initialize the queue and spawn extra worker tasks if this if the
first task. Then wait for work items to enter the task queue, and
execute the `run()` method with the current work item.'''
while self.running:
try:
item = self.QUEUE.get_nowait()
Log.debug('%s processing work item', self.name)
await self.run(item)
Log.debug('%s completed work item', self.name)
self.QUEUE.task_done()
except asyncio.QueueEmpty:
if self.OPEN:
await self.sleep(0.05)
else:
Log.debug('%s queue closed and empty, stopping', self.name)
return
except CancelledError:
Log.debug('%s cancelled, dropping work item')
self.QUEUE.task_done()
raise
except Exception:
Log.exception('%s failed work item', self.name)
self.QUEUE.task_done()
def run_task(self) -> None:
'''Execute the task inside the asyncio event loop after `DELAY`
seconds. Track the time it takes to run, and log when it starts/stops.
If/when `reset()` is called, reset the wait time to `DELAY` seconds.'''
self.last_run = 0.0
self.target = self.time() + self.DELAY
while self.running:
try:
now = self.time()
if now < self.target:
sleep = self.target - now
await self.sleep(sleep)
elif self.last_run < self.target:
Log.debug('executing timer task %s', self.name)
self.last_run = self.time()
await self.run()
total = self.time() - self.last_run
Log.debug('finished timer task %s in %.1f seconds',
self.name, total)
else:
sleep = min(5.0, self.DELAY)
await self.sleep(sleep)
except CancelledError:
Log.debug('cancelled timer task %s', self.name)
raise
except Exception:
Log.exception('exception in timer task %s', self.name)
def cancel(self):
"""Request that this task cancel itself.
This arranges for a CancelledError to be thrown into the
wrapped coroutine on the next cycle through the event loop.
The coroutine then has a chance to clean up or even deny
the request using try/except/finally.
Unlike Future.cancel, this does not guarantee that the
task will be cancelled: the exception might be caught and
acted upon, delaying cancellation of the task or preventing
cancellation completely. The task may also return a value or
raise a different exception.
Immediately after this method is called, Task.cancelled() will
not return True (unless the task was already cancelled). A
task will be marked as cancelled when the wrapped coroutine
terminates with a CancelledError exception (even if cancel()
was not called).
"""
if self.done():
return False
if self._fut_waiter is not None:
if self._fut_waiter.cancel():
# Leave self._fut_waiter; it may be a Task that
# catches and ignores the cancellation so we may have
# to cancel it again later.
return True
# It must be the case that self._step is already scheduled.
self._must_cancel = True
return True
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is futures.CancelledError and self._cancelled:
self._cancel_handler = None
self._task = None
raise futures.TimeoutError
self._cancel_handler.cancel()
self._cancel_handler = None
self._task = None
def test_result_with_timeout(self):
self.assertRaises(futures.TimeoutError,
PENDING_FUTURE.result, timeout=0)
self.assertRaises(futures.TimeoutError,
RUNNING_FUTURE.result, timeout=0)
self.assertRaises(futures.CancelledError,
CANCELLED_FUTURE.result, timeout=0)
self.assertRaises(futures.CancelledError,
CANCELLED_AND_NOTIFIED_FUTURE.result, timeout=0)
self.assertRaises(OSError, EXCEPTION_FUTURE.result, timeout=0)
self.assertEqual(SUCCESSFUL_FUTURE.result(timeout=0), 42)
def test_exception_with_timeout(self):
self.assertRaises(futures.TimeoutError,
PENDING_FUTURE.exception, timeout=0)
self.assertRaises(futures.TimeoutError,
RUNNING_FUTURE.exception, timeout=0)
self.assertRaises(futures.CancelledError,
CANCELLED_FUTURE.exception, timeout=0)
self.assertRaises(futures.CancelledError,
CANCELLED_AND_NOTIFIED_FUTURE.exception, timeout=0)
self.assertTrue(isinstance(EXCEPTION_FUTURE.exception(timeout=0),
OSError))
self.assertEqual(SUCCESSFUL_FUTURE.exception(timeout=0), None)
def read_response(self):
if not self._stream:
raise ConnectionError("Socket closed on remote end")
# _next_response might be cached from a can_read() call
if self._next_response is not False:
response = self._next_response
self._next_response = False
return response
response = self._reader.gets()
while response is False:
try:
buffer = await self._stream.read(self._read_size)
# CancelledError will be caught by client so that command won't be retried again
# For more detailed discussion please see https://github.com/NoneGG/aredis/issues/56
except CancelledError:
raise
except Exception:
e = sys.exc_info()[1]
raise ConnectionError("Error {} while reading from stream: {}".format(type(e), e.args))
if not buffer:
raise ConnectionError("Socket closed on remote end")
self._reader.feed(buffer)
response = self._reader.gets()
if isinstance(response, ResponseError):
response = self.parse_error(response.args[0])
return response
def cancel(self):
"""Request this task to cancel itself.
This arranges for a CancelledError to be thrown into the
wrapped coroutine on the next cycle through the event loop.
The coroutine then has a chance to clean up or even deny
the request using try/except/finally.
Contrary to Future.cancel(), this does not guarantee that the
task will be cancelled: the exception might be caught and
acted upon, delaying cancellation of the task or preventing it
completely. The task may also return a value or raise a
different exception.
Immediately after this method is called, Task.cancelled() will
not return True (unless the task was already cancelled). A
task will be marked as cancelled when the wrapped coroutine
terminates with a CancelledError exception (even if cancel()
was not called).
"""
if self.done():
return False
if self._fut_waiter is not None:
if self._fut_waiter.cancel():
# Leave self._fut_waiter; it may be a Task that
# catches and ignores the cancellation so we may have
# to cancel it again later.
return True
# It must be the case that self._step is already scheduled.
self._must_cancel = True
return True
def test_result_with_timeout(self):
self.assertRaises(futures.TimeoutError,
PENDING_FUTURE.result, timeout=0)
self.assertRaises(futures.TimeoutError,
RUNNING_FUTURE.result, timeout=0)
self.assertRaises(futures.CancelledError,
CANCELLED_FUTURE.result, timeout=0)
self.assertRaises(futures.CancelledError,
CANCELLED_AND_NOTIFIED_FUTURE.result, timeout=0)
self.assertRaises(OSError, EXCEPTION_FUTURE.result, timeout=0)
self.assertEqual(SUCCESSFUL_FUTURE.result(timeout=0), 42)
def test_exception_with_timeout(self):
self.assertRaises(futures.TimeoutError,
PENDING_FUTURE.exception, timeout=0)
self.assertRaises(futures.TimeoutError,
RUNNING_FUTURE.exception, timeout=0)
self.assertRaises(futures.CancelledError,
CANCELLED_FUTURE.exception, timeout=0)
self.assertRaises(futures.CancelledError,
CANCELLED_AND_NOTIFIED_FUTURE.exception, timeout=0)
self.assertTrue(isinstance(EXCEPTION_FUTURE.exception(timeout=0),
OSError))
self.assertEqual(SUCCESSFUL_FUTURE.exception(timeout=0), None)
def handle_request(self, request: Request) -> Response:
"""
coroutine: This method is called by Transport
implementation to handle the actual request.
It returns a webtype.Response object.
"""
# Get handler
try:
try:
handler = self.router.get_handler_for_request(request)
request.app = self
response = await handler(request)
except ResponseError as r:
response = r.response
if r.log:
exc_info = sys.exc_info()
self.logger.log_exception(request, exc_info, level='warning')
# invoke serialization (json) to make sure it works
_ = response.data
except CancelledError:
# This error can happen if a client closes the connection
# The response shouldnt really ever be used
return None
except Exception:
exc_info = sys.exc_info()
self.logger.log_exception(request, exc_info)
response = Response(status=500,
body={'message': 'Server Error'})
if not response.correlation_id:
response.correlation_id = request.correlation_id
if self._cors_handler is not None:
self._cors_handler.add_cors_headers(request, response)
# add default headers
response.headers = {**self.default_headers, **response.headers}
return response