From 3767c7ff391d5f277e25bca38ef3730ddf9cea9c Mon Sep 17 00:00:00 2001 From: Josh Schneier Date: Mon, 6 Aug 2018 23:13:07 -0400 Subject: [PATCH] Fixed #29244 -- Prevented Paginator.count() from silencing TypeError and AttributeError. --- django/core/paginator.py | 13 ++++++------- tests/pagination/tests.py | 21 ++++++++++++++++++--- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/django/core/paginator.py b/django/core/paginator.py index 4af80a26f2..d48a244a44 100644 --- a/django/core/paginator.py +++ b/django/core/paginator.py @@ -1,8 +1,10 @@ import collections.abc +import inspect import warnings from math import ceil from django.utils.functional import cached_property +from django.utils.inspect import method_has_no_args from django.utils.translation import gettext_lazy as _ @@ -83,13 +85,10 @@ class Paginator: @cached_property def count(self): """Return the total number of objects, across all pages.""" - try: - return self.object_list.count() - except (AttributeError, TypeError): - # AttributeError if object_list has no count() method. - # TypeError if object_list.count() requires arguments - # (i.e. is of type list). - return len(self.object_list) + c = getattr(self.object_list, 'count', None) + if callable(c) and not inspect.isbuiltin(c) and method_has_no_args(c): + return c() + return len(self.object_list) @cached_property def num_pages(self): diff --git a/tests/pagination/tests.py b/tests/pagination/tests.py index 48d4acbb11..8ab04940ab 100644 --- a/tests/pagination/tests.py +++ b/tests/pagination/tests.py @@ -1,4 +1,3 @@ -import unittest import warnings from datetime import datetime @@ -6,13 +5,13 @@ from django.core.paginator import ( EmptyPage, InvalidPage, PageNotAnInteger, Paginator, UnorderedObjectListWarning, ) -from django.test import TestCase +from django.test import SimpleTestCase, TestCase from .custom import ValidAdjacentNumsPaginator from .models import Article -class PaginationTests(unittest.TestCase): +class PaginationTests(SimpleTestCase): """ Tests for the Paginator and Page classes. """ @@ -151,6 +150,22 @@ class PaginationTests(unittest.TestCase): self.assertEqual(5, paginator.num_pages) self.assertEqual([1, 2, 3, 4, 5], list(paginator.page_range)) + def test_count_does_not_silence_attribute_error(self): + class AttributeErrorContainer: + def count(self): + raise AttributeError('abc') + + with self.assertRaisesMessage(AttributeError, 'abc'): + Paginator(AttributeErrorContainer(), 10).count() + + def test_count_does_not_silence_type_error(self): + class TypeErrorContainer: + def count(self): + raise TypeError('abc') + + with self.assertRaisesMessage(TypeError, 'abc'): + Paginator(TypeErrorContainer(), 10).count() + def check_indexes(self, params, page_num, indexes): """ Helper method that instantiates a Paginator object from the passed