Wow. Good catch! I had four different theories about this, and none of them held up. Two things are suspect: the logic in TemporaryDirectory to ensure cleanup (there are two separate mechanisms), and the fact that KeyboardError is not a subclass of Exception (only of BaseException).
The latter means that many try/except clauses in asyncio don't get triggered, because (intentionally) it uses "except Exception:" instead of a bare except clause. This probably causes the main() generator to be abandoned by the asyncio scheduler, so the context manager's __exit__ isn't called, and instead the cleanup path via the weak reference removes the directory during some GC step. But then at a later GC step the main() generator is finalized, which throws GeneratorExit into the generator, which causes __exit__ to be called -- and it attempts to clean up a second time. I think. I suspect that the real bug is that TemporaryDirectory assumes that the __exit__ clause is always reached before the finalizer cleans up, and apparently that is violated here, during end-of-program GC. You can put some print calls in to debug this further. Please do file this as a bug on bugs.python.org! --Guido On Sat, Sep 6, 2014 at 5:49 PM, Jack O'Connor <[email protected]> wrote: > I have the following test script: > > import asyncio > import tempfile > > @asyncio.coroutine > def error(): > raise KeyboardInterrupt() > > @asyncio.coroutine > def main(): > with tempfile.TemporaryDirectory(): > yield from asyncio.gather(error()) # gather() is important here > > loop = asyncio.get_event_loop() > loop.run_until_complete(main()) > > > When I run that, I get several tracebacks in a row, and one of them is > inexplicable to me: > > Exception ignored in: <generator object main at 0x7fccf7a48ee8> > Traceback (most recent call last): > File "test.py", line 11, in main > File "/usr/lib/python3.4/tempfile.py", line 691, in __exit__ > File "/usr/lib/python3.4/tempfile.py", line 697, in cleanup > File "/usr/lib/python3.4/shutil.py", line 454, in rmtree > File "/usr/lib/python3.4/shutil.py", line 452, in rmtree > FileNotFoundError: [Errno 2] No such file or directory: '/tmp/tmpl2z0vwdy' > > > When the TemporaryDirectory context manager exits, it tries to clean up > the directory it created. In this case, something appears to have already > deleted that directory. There's no other rmdir() or anything like that in > the script, so I'm confused. This part of the traceback goes away if I > remove the gather() in there and instead yield directly from my error() > coro. This also doesn't repro if I throw a RuntimeError instead of a > KeyboardInterrupt. > > Could gather() be removing a directory? Or maybe making my context manager > exit twice? > -- --Guido van Rossum (python.org/~guido)
