Repository: jazzband/django-debug-toolbar

Issue:
Versions panel: Assume less about the signature of apps' `get_version()` (was: Running with wagtailmenus)
Hi, 

I'm having an issue using django-debug-toolbar with wagtailmenus : a menu app for wagtail.

https://github.com/rkhleics/wagtailmenus/issues/115

Any idea ?
static file panel 400 bad request
1. **version**
Django 1.11.1
django-debug-toolbar 1.8

2. **my problem**
It loads static files on web browser without any problem in development env. I added django-debug-toolbar. Other panels of django-debug-toolbar works fine. But Just static file panel shows 400 bad request error.


![image](https://user-images.githubusercontent.com/30852958/30726760-bbe8c1ca-9f87-11e7-8e2c-d0378f26b620.png)

![image](https://user-images.githubusercontent.com/30852958/30726794-f5aa25fc-9f87-11e7-8964-2405281a0be5.png)

![image](https://user-images.githubusercontent.com/30852958/30726799-fff6e16c-9f87-11e7-86a8-5ef9c81e54ae.png)

3. **my code**
settings.py

```
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'src'), ] # there  are most of static files in this directory
```

urls.py
```

urlpatterns = [
    ...
]
+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)\
+ static(settings.STATIC_URL, document_root=settings.STATIC_URL)
```


Discussion:
give detail of your problem
Yeah, the code might be made more defensive in this area, and not crash and burn just because the signature of `get_version` differs between apps.

This should be an easy fix for a first time contributor.


Resolving patch:
diff --git a/debug_toolbar/panels/versions.py b/debug_toolbar/panels/versions.py
--- a/debug_toolbar/panels/versions.py
+++ b/debug_toolbar/panels/versions.py
@@ -41,20 +41,25 @@ def gen_app_versions(self):
                 yield app.__name__, name, version
 
     def get_app_version(self, app):
-        if hasattr(app, 'get_version'):
-            get_version = app.get_version
-            if callable(get_version):
-                version = get_version()
-            else:
-                version = get_version
-        elif hasattr(app, 'VERSION'):
-            version = app.VERSION
-        elif hasattr(app, '__version__'):
-            version = app.__version__
-        else:
-            return
+        version = self.get_version_from_app(app)
         if isinstance(version, (list, tuple)):
             # We strip dots from the right because we do not want to show
             # trailing dots if there are empty elements in the list/tuple
             version = '.'.join(str(o) for o in version).rstrip('.')
         return version
+
+    def get_version_from_app(self, app):
+        if hasattr(app, 'get_version'):
+            get_version = app.get_version
+            if callable(get_version):
+                try:
+                    return get_version()
+                except TypeError:
+                    pass
+            else:
+                return get_version
+        if hasattr(app, 'VERSION'):
+            return app.VERSION
+        if hasattr(app, '__version__'):
+            return app.__version__
+        return


Test patch:
diff --git a/tests/panels/test_versions.py b/tests/panels/test_versions.py
--- a/tests/panels/test_versions.py
+++ b/tests/panels/test_versions.py
@@ -25,6 +25,18 @@ def get_version(self):
 
         self.assertEqual(self.panel.get_app_version(FakeApp()), '1.2.3')
 
+    def test_incompatible_app_version_fn(self):
+
+        class FakeApp:
+
+            def get_version(self, some_other_arg):
+                # This should be ignored by the get_version_from_app
+                return version_info_t(0, 0, 0, '', '')
+
+            VERSION = version_info_t(1, 2, 3, '', '')
+
+        self.assertEqual(self.panel.get_app_version(FakeApp()), '1.2.3')
+
     def test_app_version_from_VERSION(self):
 
         class FakeApp:
@@ -32,7 +44,7 @@ class FakeApp:
 
         self.assertEqual(self.panel.get_app_version(FakeApp()), '1.2.3')
 
-    def test_app_version_from_underscode_version(self):
+    def test_app_version_from_underscore_version(self):
 
         class FakeApp:
             __version__ = version_info_t(1, 2, 3, '', '')