itamarst / eliot

Eliot: the logging system that tells you *why* it happened
https://eliot.readthedocs.io
Apache License 2.0
1.1k stars 66 forks source link

"module 'asyncio' has no attribute '_get_running_loop'" on python 3.5.2 #418

Closed richvdh closed 5 years ago

richvdh commented 5 years ago

Python 3.5.2 is, regrettably, still a relatively common thing, thanks to Ubuntu Xenial, which still has another couple of years of support.

On such a python:

Python 3.5.2 (default, Nov 23 2017, 16:37:01) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import eliot
>>> eliot.current_action()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/synapse/env3/lib/python3.5/site-packages/eliot/_action.py", line 53, in current_action
    return _ACTION_CONTEXT.get(None)
  File "/home/synapse/env3/lib/python3.5/site-packages/contextvars/__init__.py", line 94, in get
    ctx = _get_context()
  File "/home/synapse/env3/lib/python3.5/site-packages/aiocontextvars.py", line 19, in _get_context
    state = _get_state()
  File "/home/synapse/env3/lib/python3.5/site-packages/aiocontextvars.py", line 33, in _get_state
    loop = asyncio._get_running_loop()
AttributeError: module 'asyncio' has no attribute '_get_running_loop'

The problem is really in aiocontextvars, which uses the undocumented method asyncio._get_running_loop, which was added to the standard library in python 3.5.3.

How about throwing 3.5.2 into the same bucket as python 2.7 and declaring it to be unsupported as of eliot 1.8 - and updating setup.py accordingly?

itamarst commented 5 years ago

Thanks for the bug report.

Also :sob:

I could do the setup.py thing, but then people on 3.5.2 would still get 1.8 from PyPI, so would still suffer from the bug. I wonder if adding _get_running_loop to asyncio is possible... let me go check.

itamarst commented 5 years ago

OK, so as a short term fix, before importing eliot do the following:

import asyncio
from asyncio import events
asyncio._get_running_loop = events._get_running_loop
asyncio._set_running_loop = events._set_running_loop

I will file issue with aiocontextvars and then fix this in Eliot.

itamarst commented 5 years ago

The above is failing in my branch, trying to figure out why...

itamarst commented 5 years ago

Looks like it's more than just exposing private stuff, that code doesn't exist in 3.5.2. Ugggggh.

itamarst commented 5 years ago

It looks like this would involve monkeypatching in the following: https://github.com/python/cpython/commit/600a349781bfa0a8239e1cb95fac29c7c4a3302e#diff-26d371a128a335a2c69b3f38613631fd

The problem is that there's a bunch of intra-package imports, so mere monkeypatching of asyncio.events and asyncio won't suffice.

richvdh commented 5 years ago

people on 3.5.2 would still get 1.8 from PyPI

could you remove 1.8 from pypi? Not entirely sure how well that works, tbh

itamarst commented 5 years ago

I could also have have Eliot 1.9 throw an exception saying "Python 3.5.2 and earlier are unsupported, you can use deadsnakes PPA to get newer Python 3.5 on Xenial" or something, instead of using setup.py metadata.

itamarst commented 5 years ago

Deadsnakes PPA doesn't package 3.5 for Xenial :crying_cat_face:

itamarst commented 5 years ago

I'm just going to go with "give nicer error message" in the next release (I could probably make a monkeypatched fix work, but it would be pretty intrusive so would need a bunch of testing, and I feel uncomfortable replacing the 1.8 release files).

If this is a big deal for Synapse I can probably offer you a support contract, but as a workaround I suggest pinning to 1.7 on Python 3.5, which I will also suggest to txacme.

richvdh commented 5 years ago

Ok, thanks very much for looking into this. As you say, I guess we'll just pin to eliot 1.7 for older python 3.5.