Repository: minrk/asyncio-atexit (language: python)

Issue:
uvloop compatibility
Hi, I noticed this library seems to fail with uvloop

```
File "/my_file.py", line 71
    asyncio_atexit.register(shutdown)
  File "/my_path/lib/python3.9/site-packages/asyncio_atexit.py", line 48, in register
    entry = _get_entry(loop)
  File "/my_path/lib/python3.9/site-packages/asyncio_atexit.py", line 73, in _get_entry
    _register_loop(loop)
  File "/my_path/lib/python3.9/site-packages/asyncio_atexit.py", line 82, in _register_loop
    _registry[loop] = _RegistryEntry(loop)
  File "/my_path/lib/python3.9/site-packages/asyncio_atexit.py", line 25, in __init__
    self._close_ref = weakref.WeakMethod(loop.close)
  File "/my_path/lib/python3.9/weakref.py", line 52, in __new__
    raise TypeError("argument should be a bound method, not {}"
TypeError: argument should be a bound method, not <class 'builtin_function_or_method'>
```


Discussion / hints:
interesting! I didn't know there were methods you can't weakref.

Fix patch:
diff --git a/asyncio_atexit.py b/asyncio_atexit.py
index 4849b79..e2cf2de 100644
--- a/asyncio_atexit.py
+++ b/asyncio_atexit.py
@@ -21,7 +21,13 @@
 
 class _RegistryEntry:
     def __init__(self, loop):
-        self._close_ref = weakref.WeakMethod(loop.close)
+        try:
+            self._close_ref = weakref.WeakMethod(loop.close)
+        except TypeError:
+            # not everything can be weakref'd (Extensions such as uvloop).
+            # Hold a regular reference _on the object_, in those cases
+            loop._atexit_orig_close = loop.close
+            self._close_ref = lambda: loop._atexit_orig_close
         self.callbacks = []
 
     def close(self):
diff --git a/setup.cfg b/setup.cfg
index b521178..a34030d 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -20,7 +20,9 @@ py_modules = asyncio_atexit
 python_requires = >=3.6
 
 [options.extras_require]
-test = pytest
+test =
+    pytest
+    uvloop
 
 [tool:pytest]
 # asyncio_mode = auto


Test patch:
diff --git a/test_asyncio_atexit.py b/test_asyncio_atexit.py
index 9ddb3ec..dcadb06 100644
--- a/test_asyncio_atexit.py
+++ b/test_asyncio_atexit.py
@@ -1,6 +1,13 @@
 import asyncio
 import sys
 
+try:
+    import uvloop
+except ImportError:
+    uvloop = None
+
+import pytest
+
 import asyncio_atexit
 
 if sys.version_info >= (3, 7):
@@ -15,7 +22,24 @@ def asyncio_run(coro):
             loop.close()
 
 
-def test_asyncio_atexit():
+policies = ["default"]
+if uvloop is not None:
+    policies.append("uvloop")
+
+
+@pytest.fixture(params=policies)
+def policy(request):
+    before_policy = asyncio.get_event_loop_policy()
+    if request.param == "default":
+        policy = asyncio.DefaultEventLoopPolicy()
+    elif request.param == "uvloop":
+        policy = uvloop.EventLoopPolicy()
+    asyncio.set_event_loop_policy(policy)
+    yield
+    asyncio.set_event_loop_policy(before_policy)
+
+
+def test_asyncio_atexit(policy):
     sync_called = False
     async_called = False
 
@@ -37,7 +61,7 @@ async def test():
     assert async_called
 
 
-def test_unregister():
+def test_unregister(policy):
     sync_called = False
 
     def sync_cb():