That reads good, just a small typo in the third paragraph: Contrary to 
Future.cancel(), this does not guarantee that the task will cancelled
which should have an additional "be": "... will be cancelled" (or just 
"will cancel").

On Monday, March 31, 2014 7:29:41 PM UTC+2, Guido van Rossum wrote:
>
> Thanks! I rewrote it to the following docstring:
>
>         """Request that a task to cancel 
> itself.                                                                       
>                          
>
>                                                                               
>                                                                   
>
>         This arranges for a CancellationError 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 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).                                                                      
>                                                   
>
>         """
>
> I'm going to think about how to work this into the CPython Doc tree (which 
> is structured very differently).
>
>
>
> On Sun, Mar 30, 2014 at 5:22 PM, Florian Rüchel 
> <[email protected]<javascript:>
> > wrote:
>
>> Hi Guido,
>>
>> First of all, thank you for your suggestion. I still have to wrap my mind 
>> around async programming contrary to multithreaded programming. I have 
>> rethought my code and could actually clean it up a great deal.
>>
>> Regarding the documentation: I'd probably put a docstring on Task.cancel 
>> that reads similar to this:
>> ===
>> Cancel a task by raising a CancellationError inside the wrapped 
>> coroutine. Contrary to Future.cancel this does not guarantee that the task 
>> is cancelled as the exception might be caught and acted upon, delaying or 
>> preventing cancellation of the task. Additonally, this function only marks 
>> the task for cancellation but will not directly cancel it (thus 
>> Task.cancelled will not return True right after calling Task.cancel).
>> ===
>>
>> You could add the "Additionally, ..." part as an rst ..note block, which 
>> I think would be prettier.
>>
>> Regards,
>> Florian
>>
>>
>> On Friday, March 28, 2014 10:58:03 PM UTC+1, Guido van Rossum wrote:
>>
>>> Hi Florian,
>>>
>>> I'm sorry this wasn't documented in the official docs. (It's described 
>>> in PEP 3156 in the two sections on  Futures and Tasks.)
>>>
>>> The behavior is indeed as intended, and came from a long discussion and 
>>> deep thinking about cancellation. When a Task (as opposed to a Future) is 
>>> cancelled, the coroutine that the Task wraps will receive a 
>>> asyncio.CancelledError, which will be handled by any except/finally clauses 
>>> active at that point. But this will only run when the event loop goes 
>>> through its next cycle. It is possible (though not recommended) for a 
>>> coroutine to completely ignore this exception, and it is also possible for 
>>> it to yield, waiting for additional blocking events, before finally 
>>> exiting. The cancelled() flag is only set once the coroutine has completed.
>>>
>>> We should really document this -- do you have a suggestion for a 
>>> sentence or paragraph that we should add to the docs?
>>>
>>> As a practical matter, you shouldn't write code that depends on checking 
>>> whether a task has a pending cancellation or not (since it may not take 
>>> effect at all). In your particular app, if you really need to keep track of 
>>> which tasks you have already tried to cancel, I recommend a separate 
>>> variable containing a set of tasks which you have cancelled for that 
>>> purpose.
>>>
>>> Finally, your description of how you are using this sounds like you are 
>>> really thinking of your collection of tasks as a collection of callbacks 
>>> that you are managing manually. Things might (or might not) become easier 
>>> if you let go of this perspective -- e.g. instead of looping over all tasks 
>>> and cancelling slow ones, wrap each task in a wait_for() call that enforces 
>>> a timeout.
>>>
>>>
>>>
>>> On Fri, Mar 28, 2014 at 2:22 PM, Florian Rüchel <[email protected]>wrote:
>>>
>>>> I had an issue with my code where I called Task.cancel on an instance 
>>>> and it would stay in state pending (cancelled returning False). I did some 
>>>> digging and found in the source that Task overwrites the cancel method and 
>>>> does not directly set the new state. Going back to the documentation, I 
>>>> did 
>>>> not find anything that documents this behavior.
>>>>
>>>> The problematic use case was a situation like the following: I iterate 
>>>> over a list of tasks, check various properties (like how long they are 
>>>> running etc.) and based on that decide to either give it more time or 
>>>> cancel it or check its results if its done. After having handled all 
>>>> running tasks, I do other work and later return to check them again. When 
>>>> I 
>>>> checked again, I explicitly checked the case where I already had cancelled 
>>>> a task. In this next iteration I want to see if it is done yet or if it 
>>>> needs some more time cleaning up. However, to my surprise, 
>>>> task.cancelled() 
>>>> returned false, which led me to execute task.cancel() again, which in turn 
>>>> raised a CancellationError inside the finally-block doing cleanup. I then 
>>>> added something like this:
>>>>
>>>> task.cancel()
>>>> assert task.canelled()
>>>>
>>>> A statement that, on a future, has to succeed (as I see it from the 
>>>> source) but on a task must fail. Is there a reason this behavior is not 
>>>> documented or did I miss it somewhere?
>>>>
>>>> Also since this seems to be desired behavior, is there a reliable way 
>>>> to perform this check on a task? In __repr__ you check against 
>>>> _must_cancel 
>>>> for the difference between displaying "PENDING" and "CANCELLING" (this 
>>>> actually led me to the point of looking at the Task source). However, this 
>>>> is an internal variable and so I probably shouldn't use it. The only other 
>>>> thing I can come up with is using the return value and store it somewhere 
>>>> else and then check that variable, however that seems rather "dirty", 
>>>> storing a value that should belong to the task outside of it.
>>>>
>>>
>>>
>>>
>>> -- 
>>> --Guido van Rossum (python.org/~guido) 
>>>
>>
>
>
> -- 
> --Guido van Rossum (python.org/~guido) 
>

Reply via email to