Repository: andrewgodwin/urlman

Issue:
__qualname__ access by Sphinx 3.4.0 fails
I'm using Sphinx to document my project that uses `urlman`. As of the new Sphinx 3.4.0, Sphinx [uses](https://github.com/sphinx-doc/sphinx/issues/5538) `__qualname__` to resolve inheritance when running `autodoc`. This process causes `urlman` to throw an exception, making the docs unbuildable:

```
  File "/home/pretalx_docs/.local/lib/python3.8/site-packages/sphinx/ext/autodoc/__init__.py", line 931, in generate
    sourcename = self.get_sourcename()
  File "/home/pretalx_docs/.local/lib/python3.8/site-packages/sphinx/ext/autodoc/__init__.py", line 575, in get_sourcename
    getattr(self.object, '__qualname__', None)):
  File "/home/pretalx_docs/.local/lib/python3.8/site-packages/urlman.py", line 69, in __getattr__
    return self.get_url(attr)
  File "/home/pretalx_docs/.local/lib/python3.8/site-packages/urlman.py", line 76, in get_url
    raise ValueError("No URL called %r on %r" %
ValueError: No URL called '__qualname__' on 'NoneType'
```

I'm not sure what the right solution is – I'm tempted to say that dunder attribute access should just not be interpreted as a URL, or at least should be given a second attempt at the actual attributes when URL lookup fails.

Discussion:

Patch:
diff --git a/urlman.py b/urlman.py
index 738018e..7700384 100644
--- a/urlman.py
+++ b/urlman.py
@@ -51,7 +51,7 @@ class UrlsMetaclass(type):
         return type.__new__(self, name, bases, attrs)
 
     def __get__(self, instance, klass):
-        return self(klass, instance)
+        return self(klass, instance, self.__name__)
 
 
 class Urls(with_metaclass(UrlsMetaclass)):
@@ -62,11 +62,12 @@ class Urls(with_metaclass(UrlsMetaclass)):
     format. If you need to you can also specify a handler function for a url.
     """
 
-    def __init__(self, klass, instance):
+    def __init__(self, klass, instance, name):
         self.klass = klass
         self.instance = instance
         self.context = {"self": self.instance}
         self.context.update(self.urls)
+        self.__qualname__ = ".".join((klass.__qualname__, name))
 
     def __getattr__(self, attr):
         return self.get_url(attr)


Test patch:
diff --git a/test_urlman.py b/test_urlman.py
index 6fb6713..201eed9 100644
--- a/test_urlman.py
+++ b/test_urlman.py
@@ -135,3 +135,7 @@ def test_rest_framework_serializer(post):
         "admin": post.urls.admin.full(),
     }
     assert relative_field.to_representation(post) == {"view": post.urls.view}
+
+
+def test_qualname(post):
+    assert post.urls.__qualname__ == "Post.urls"