Pythonic способ изменить способ отмены асинхронной задачи, откуда вызывается метод cancel () - PullRequest
0 голосов
/ 14 февраля 2019

Как изменить поведение отмены задачи с того места, где она отменяется?

О чем я мечтаю:

task = ensure_future(foo())

def foo_done(task)
    try:
        return task.get_result()
    except CancelError as e:
        when, why = e.args
        if when == "now"
            # do something...
        elif when == "asap":
            # do something else...
        else:
            # do default
        print(f"task cancelled because {why}")

task.add_done_callback(foo_done)

[...]

task.cancel("now", "This is an order!")

Я мог бы прикрепить объект к задаче раньшевызов task.cancel() и проверка его позже.

task = ensure_future(foo())

def foo_done(task)
    try:
        return task.get_result()
    except CancelError as e:
        when = getattr(task, "_when", "")
        why = getattr(task, "_why", "")
        if when == "now"
            # do something...
        elif when == "asap":
            # do something else...
        else:
            # do default
        print(f"task cancelled because {why}")

task.add_done_callback(foo_done)

[...]
task._when = "now"
task._why = "This is an order!"
task.cancel()

Но в некоторых ситуациях это выглядит неуклюже, когда я хочу захватить CancelError в рамках выполняемой задачи, например:

async def foo():
   # some stuff
   try:
       # some other stuff
   except CancellError as e:
       # here I have easily access to the error, but not the task :(
   [...]

Я ищу более Pythonic способ сделать это.

1 Ответ

0 голосов
/ 15 февраля 2019

Ваше решение украсить Task данными, относящимися к вашему исключению, на самом деле является хорошим.В рамках задачи вы можете получить доступ к задаче, которая обрабатывается с помощью asyncio.Task.current_task().

. Вы также можете достичь синтаксиса, о котором мечтаете, используя следующий декоратор (не проверено):

def propagate_when(fn):
    async def wrapped(*args, **kwds):
        try:
            return await fn(*args, **kwds)
        except CancelledError as e:
            e.when = getattr(asyncio.Task.current_task(), '_when', None)
            raise
    return wrapped

Украшение сопрограммы@propagate_when позволяет коду из foo_done получить доступ к e.when при обработке CancelledError.Недостатком является то, что e.when будет не быть доступным внутри задачи - там вам все равно придется использовать current_task().Из-за этого несоответствия я бы порекомендовал придерживаться чтения из объекта задачи.

Несколько связанных рекомендаций:

  • Поместите код отмены в служебную функцию, которая хранит объект, который выпередать его и затем звонит task.cancel().Этот тонкий слой инкапсуляции должен устранить «неуклюжее» ощущение в текущем коде.

  • Использовать имена атрибутов с префиксами - короткие и общие, такие как _when, могут вызвать конфликт в будущем выпуске,(Я понимаю, что это был только пример, но незафиксированные имена всегда могут столкнуться).

  • Украсьте задачу одним объектом, поместив фактические данные в его атрибуты.Это делает поиск проще и чище, а также дает возможность реализовать методы для хранимого объекта.

...