From 66e1f35205027e4974a4fe04cc599f8d48a35709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 27 Aug 2013 13:50:38 +0300 Subject: [PATCH 1/4] Fix "silent" error handling in the TableData.__init__ to prepend the complete traceback and show original exception for Python 2.x. Previously it was impossible to debug query-time issues with django_tables2. --- django_tables2/tables.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/django_tables2/tables.py b/django_tables2/tables.py index 1b884e1..6d2785d 100644 --- a/django_tables2/tables.py +++ b/django_tables2/tables.py @@ -6,6 +6,7 @@ from .rows import BoundRows from .utils import (Accessor, AttributeDict, build_request, cached_property, OrderBy, OrderByTuple, segment, Sequence) import copy +import sys from django.core.paginator import Paginator from django.db.models.fields import FieldDoesNotExist from django.utils.datastructures import SortedDict @@ -36,10 +37,19 @@ class TableData(object): else: try: self.list = list(data) - except: - raise ValueError('data must be QuerySet-like (have count and ' - 'order_by) or support list(data) -- %s has ' - 'neither' % type(data).__name__) + except Exception as ex: + if six.PY3: + raise ValueError( + 'data must be QuerySet-like (have count and ' + 'order_by) or support list(data) -- %s has ' + 'neither' % type(data).__name__ + ) + else: + raise ValueError, ValueError( + 'data must be QuerySet-like (have count and ' + 'order_by) or support list(data) -- %s has ' + 'neither. Original exception: %s' % (type(data).__name__, ex) + ), sys.exc_info()[2] def __len__(self): if not hasattr(self, "_length"): From 3287aa2a180caf6f951217950e50f958904e8948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 27 Aug 2013 14:29:29 +0300 Subject: [PATCH 2/4] Fix the syntax to be parseable under py3 --- django_tables2/tables.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/django_tables2/tables.py b/django_tables2/tables.py index 6d2785d..a1cc7ea 100644 --- a/django_tables2/tables.py +++ b/django_tables2/tables.py @@ -45,11 +45,12 @@ class TableData(object): 'neither' % type(data).__name__ ) else: - raise ValueError, ValueError( + # really horrible, but this syntax is not supported on PY3 and would not work otherwise + exec """raise ValueError, ( 'data must be QuerySet-like (have count and ' 'order_by) or support list(data) -- %s has ' 'neither. Original exception: %s' % (type(data).__name__, ex) - ), sys.exc_info()[2] + ), sys.exc_info()[2]""" def __len__(self): if not hasattr(self, "_length"): From c76a2bea1eb80d6fda4871bc1ce173eef0f608d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 27 Aug 2013 14:38:32 +0300 Subject: [PATCH 3/4] Ooops, forgot the parens. --- django_tables2/tables.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/django_tables2/tables.py b/django_tables2/tables.py index a1cc7ea..28a160b 100644 --- a/django_tables2/tables.py +++ b/django_tables2/tables.py @@ -46,11 +46,11 @@ class TableData(object): ) else: # really horrible, but this syntax is not supported on PY3 and would not work otherwise - exec """raise ValueError, ( + exec("""raise ValueError, ( 'data must be QuerySet-like (have count and ' 'order_by) or support list(data) -- %s has ' 'neither. Original exception: %s' % (type(data).__name__, ex) - ), sys.exc_info()[2]""" + ), sys.exc_info()[2]""") def __len__(self): if not hasattr(self, "_length"): From a4eecf646e83cc1d9dfa72f20cebf00796ee9961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 28 Aug 2013 14:38:53 +0300 Subject: [PATCH 4/4] Replace the exception dance with light input validation. --- django_tables2/tables.py | 23 ++++++++--------------- tests/core.py | 23 +++++++++++++++++++++++ 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/django_tables2/tables.py b/django_tables2/tables.py index 28a160b..bd9e2b2 100644 --- a/django_tables2/tables.py +++ b/django_tables2/tables.py @@ -35,22 +35,15 @@ class TableData(object): self.queryset = data # otherwise it must be convertable to a list else: - try: + # do some light validation + if hasattr(data, '__iter__') or (hasattr(data, '__len__') and hasattr(data, '__getitem__')): self.list = list(data) - except Exception as ex: - if six.PY3: - raise ValueError( - 'data must be QuerySet-like (have count and ' - 'order_by) or support list(data) -- %s has ' - 'neither' % type(data).__name__ - ) - else: - # really horrible, but this syntax is not supported on PY3 and would not work otherwise - exec("""raise ValueError, ( - 'data must be QuerySet-like (have count and ' - 'order_by) or support list(data) -- %s has ' - 'neither. Original exception: %s' % (type(data).__name__, ex) - ), sys.exc_info()[2]""") + else: + raise ValueError( + 'data must be QuerySet-like (have count and ' + 'order_by) or support list(data) -- %s has ' + 'neither' % type(data).__name__ + ) def __len__(self): if not hasattr(self, "_length"): diff --git a/tests/core.py b/tests/core.py index eb29fc0..0c8cbb4 100644 --- a/tests/core.py +++ b/tests/core.py @@ -165,7 +165,30 @@ def should_support_haystack_data_source(): table = PersonTable(SearchQuerySet().all()) table.as_html() + + +@core.test +def data_validation(): + with raises(ValueError): + table = OrderedTable(None) + + class Bad: + def __len__(self): + pass + + with raises(ValueError): + table = OrderedTable(Bad()) + class Ok: + def __len__(self): + return 1 + def __getitem__(self, pos): + if pos != 0: + raise IndexError() + return {'a': 1} + + table = OrderedTable(Ok()) + assert len(table.rows) == 1 @core.test def ordering():