{"action":"created","issue":{"url":"https://api.github.com/repos/dabeaz/curio/issues/70","repository_url":"https://api.github.com/repos/dabeaz/curio","labels_url":"https://api.github.com/repos/dabeaz/curio/issues/70/labels{/name}","comments_url":"https://api.github.com/repos/dabeaz/curio/issues/70/comments","events_url":"https://api.github.com/repos/dabeaz/curio/issues/70/events","html_url":"https://github.com/dabeaz/curio/issues/70","id":163767085,"number":70,"title":"I think Socket.close should be a regular method, not an async method","user":{"login":"njsmith","id":609896,"avatar_url":"https://avatars.githubusercontent.com/u/609896?v=3","gravatar_id":"","url":"https://api.github.com/users/njsmith","html_url":"https://github.com/njsmith","followers_url":"https://api.github.com/users/njsmith/followers","following_url":"https://api.github.com/users/njsmith/following{/other_user}","gists_url":"https://api.github.com/users/njsmith/gists{/gist_id}","starred_url":"https://api.github.com/users/njsmith/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/njsmith/subscriptions","organizations_url":"https://api.github.com/users/njsmith/orgs","repos_url":"https://api.github.com/users/njsmith/repos","events_url":"https://api.github.com/users/njsmith/events{/privacy}","received_events_url":"https://api.github.com/users/njsmith/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":12,"created_at":"2016-07-05T03:47:52Z","updated_at":"2016-11-20T04:45:50Z","closed_at":null,"body":"So obviously, you cannot call async methods from `__del__` methods. And because of this, `close` methods generally have to be non-async as well. And in particular, Python enforces that the generator protocol's `close` method is synchronous and cannot `yield`:\n\n``` python\nIn [5]: def f():\n   ...:     try:\n   ...:         yield 1\n   ...:     finally:\n   ...:         yield 2\n   ...:         \n\nIn [6]: gen = f()\n\nIn [7]: next(gen)\nOut[7]: 1\n\nIn [8]: gen.close()\nRuntimeError: generator ignored GeneratorExit\n```\n\nThis doesn't come up much in the context of coroutines, because coroutines are usually iterated to exhaustion. But as soon as you have [asynchronous generators](https://pypi.io/project/async_generator/) then it's a real problem.\n\nFor example, imagine that we have an asynchronous WSGI-like API that wants to use an `async for` loop to collect up the body of an HTTP response. And suppose that we have some code that builds that body by reading from a remote socket::\n\n``` python\nfrom async_generator import async_generator, yield_\n@async_generator\nasync def get_html_body_from_socket():\n    sock = await do_connect(...)\n    try:\n        while True:\n            got = await sock.recv(1024)\n            if not got:\n                return\n            await yield_(got)\n    finally:\n        await sock.close()\n```\n\nAnd suppose that this iterator is _not_ exhausted -- for example, because the HTTP client went away, so the WSGI-like server code stopped iterating over it. Then eventually either the server code or the Python runtime will call `.close()` on the `get_html_body_from_socket` coroutine, this will inject a `GeneratorExit` exception, and we are required to handle this exception _without_ yielding. So the above code is... kinda wrong. You'll get away with it in practice because Curio's `Socket.close()` doesn't actually yield, but the easy general rule is that coroutine `finally` blocks should never contain `await`, and this violates that. And this constraint means that Curio's `Socket.close()` must never change in the future to yield, so we might as well make it non-async to remind us of this and enforce it.\n\nFor the same reason, I guess `Socket` should implement the synchronous `__enter__`/`__exit__` protocol instead of `__aenter__`/`__aexit__`.\n\nPossibly there are other places in curio with the same issue.\n"},"comment":{"url":"https://api.github.com/repos/dabeaz/curio/issues/comments/261758816","html_url":"https://github.com/dabeaz/curio/issues/70#issuecomment-261758816","issue_url":"https://api.github.com/repos/dabeaz/curio/issues/70","id":261758816,"user":{"login":"imrn","id":2698246,"avatar_url":"https://avatars.githubusercontent.com/u/2698246?v=3","gravatar_id":"","url":"https://api.github.com/users/imrn","html_url":"https://github.com/imrn","followers_url":"https://api.github.com/users/imrn/followers","following_url":"https://api.github.com/users/imrn/following{/other_user}","gists_url":"https://api.github.com/users/imrn/gists{/gist_id}","starred_url":"https://api.github.com/users/imrn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/imrn/subscriptions","organizations_url":"https://api.github.com/users/imrn/orgs","repos_url":"https://api.github.com/users/imrn/repos","events_url":"https://api.github.com/users/imrn/events{/privacy}","received_events_url":"https://api.github.com/users/imrn/received_events","type":"User","site_admin":false},"created_at":"2016-11-20T04:45:50Z","updated_at":"2016-11-20T04:45:50Z","body":"Given that 'Nice to have' s turning quickly into harmful things\r\nmost the time, I'm now thinking in terms of 'Absolute Necessity':\r\n\r\n- What are the 'Really' hard the constraints for having async close()?\r\nBulleted one-liners please.."}}
{"actor":{"display_login":"imrn"}}