tests for the paginator and page classes helper method that instantiates a paginator object from the passed params and then checks that its attributes match the passed output helper method that checks a single attribute and gives a nice error message upon test failure tests the paginator attributes using varying inputs each item is 2tuple first tuple is paginator parameters objectlist perpage orphans and allowemptyfirstpage second tuple is resulting paginator attributes count numpages and pagerange ten items varying orphans no empty first page ten items varying orphans allow empty first page one item varying orphans no empty first page one item varying orphans allow empty first page zero items varying orphans no empty first page zero items varying orphans allow empty first page number if items one less than perpage number if items equal to perpage number if items one more than perpage number if items one more than perpage with one orphan noninteger inputs invalid page numbers result in the correct exception being raised custom message default message with no content and allowemptyfirstpagetrue 1 is a valid page number paginator can be passed other objects with a count method paginator can be passed other objects that implement len helper method that instantiates a paginator object from the passed params and then checks that the start and end indexes of the passed pagenum match those given as a 2tuple in indexes paginator pages have the correct start and end indexes each item is 3tuple first tuple is paginator parameters objectlist perpage orphans and allowemptyfirstpage second tuple is the start and end indexes of the first page third tuple is the start and end indexes of the last page ten items varying perpage no orphans ten items varying perpage with orphans one item varying orphans no empty first page one item varying orphans allow empty first page zero items varying orphans allow empty first page when no items and no empty first page we should get emptypage error a paginator page acts like a standard sequence a paginator subclass can use the getpage hook to return an alternative to the standard page class paginator pagerange should be an iterator paginator getpage returns a valid page even with invalid page arguments an empty page returns the last page noninteger page returns the first page paginator getpage with an empty objectlist paginator paginator 2 an empty page returns the last page self assertequalpaginator getpage1 number 1 self assertequalpaginator getpage2 number 1 noninteger page returns the first page self assertequalpaginator getpagenone number 1 def testgetpageemptyobjectlistandallowemptyfirstpagefalseself paginator paginator 2 allowemptyfirstpagefalse with self assertraisesemptypage paginator getpage1 def testpaginatoriterationself paginator paginator1 2 3 2 pageiterator iterpaginator for page expected in enumerate1 2 3 start1 with self subtestpagepage self assertequalexpected listnextpageiterator self assertequal strpage for page in iterpaginator page 1 of 2 page 2 of 2 def testgetelidedpagerangeself paginator validatenumber must be called paginator paginator1 2 3 2 with unittest mock patch objectpaginator validatenumber as mock mock assertnotcalled listpaginator getelidedpagerange2 mock assertcalledwith2 ellipsis paginator ellipsis range is not elided if not enough pages when using default arguments paginator paginatorrange10 100 100 pagerange paginator getelidedpagerange1 self assertisinstancepagerange collections abc generator self assertnotinellipsis pagerange paginator paginatorrange10 100 1 100 self assertisinstancepagerange collections abc generator pagerange paginator getelidedpagerange1 self assertinellipsis pagerange range should be elided if enough pages when using default arguments tests oneachside3 onends2 1 1 2 3 4 ellipsis 49 50 6 1 2 3 4 5 6 7 8 9 ellipsis 49 50 7 1 2 3 4 5 6 7 8 9 10 ellipsis 49 50 8 1 2 ellipsis 5 6 7 8 9 10 11 ellipsis 49 50 43 1 2 ellipsis 40 41 42 43 44 45 46 ellipsis 49 50 44 1 2 ellipsis 41 42 43 44 45 46 47 48 49 50 45 1 2 ellipsis 42 43 44 45 46 47 48 49 50 50 1 2 ellipsis 47 48 49 50 paginator paginatorrange5000 100 for number expected in tests with self subtestnumbernumber pagerange paginator getelidedpagerangenumber self assertisinstancepagerange collections abc generator self assertequallistpagerange expected range is not elided if not enough pages when using custom arguments tests 6 2 1 1 8 1 3 1 8 4 0 1 4 1 1 1 when oneachside and onends are both 1 but not both 1 it is a special case where the range is not elided until an extra page is added 2 0 1 2 2 1 0 2 1 0 0 2 for pages oneachside onends elidedafter in tests for offset in rangeelidedafter 1 with self subtest pagespages offsetelidedafter oneachsideoneachside onendsonends paginator paginatorrangepages offset 100 100 pagerange paginator getelidedpagerange 1 oneachsideoneachside onendsonends self assertisinstancepagerange collections abc generator if offset elidedafter self assertnotinellipsis pagerange else self assertinellipsis pagerange range should be elided if enough pages when using custom arguments tests oneachside2 onends1 1 2 1 1 2 3 ellipsis 50 4 2 1 1 2 3 4 5 6 ellipsis 50 5 2 1 1 2 3 4 5 6 7 ellipsis 50 6 2 1 1 ellipsis 4 5 6 7 8 ellipsis 50 45 2 1 1 ellipsis 43 44 45 46 47 ellipsis 50 46 2 1 1 ellipsis 44 45 46 47 48 49 50 47 2 1 1 ellipsis 45 46 47 48 49 50 50 2 1 1 ellipsis 48 49 50 oneachside1 onends3 1 1 3 1 2 ellipsis 48 49 50 5 1 3 1 2 3 4 5 6 ellipsis 48 49 50 6 1 3 1 2 3 4 5 6 7 ellipsis 48 49 50 7 1 3 1 2 3 ellipsis 6 7 8 ellipsis 48 49 50 44 1 3 1 2 3 ellipsis 43 44 45 ellipsis 48 49 50 45 1 3 1 2 3 ellipsis 44 45 46 47 48 49 50 46 1 3 1 2 3 ellipsis 45 46 47 48 49 50 50 1 3 1 2 3 ellipsis 49 50 oneachside4 onends0 1 4 0 1 2 3 4 5 ellipsis 5 4 0 1 2 3 4 5 6 7 8 9 ellipsis 6 4 0 1 2 3 4 5 6 7 8 9 10 ellipsis 7 4 0 ellipsis 3 4 5 6 7 8 9 10 11 ellipsis 44 4 0 ellipsis 40 41 42 43 44 45 46 47 48 ellipsis 45 4 0 ellipsis 41 42 43 44 45 46 47 48 49 50 46 4 0 ellipsis 42 43 44 45 46 47 48 49 50 50 4 0 ellipsis 46 47 48 49 50 oneachside0 onends1 1 0 1 1 ellipsis 50 2 0 1 1 2 ellipsis 50 3 0 1 1 2 3 ellipsis 50 4 0 1 1 ellipsis 4 ellipsis 50 47 0 1 1 ellipsis 47 ellipsis 50 48 0 1 1 ellipsis 48 49 50 49 0 1 1 ellipsis 49 50 50 0 1 1 ellipsis 50 oneachside0 onends0 1 0 0 1 ellipsis 2 0 0 1 2 ellipsis 3 0 0 ellipsis 3 ellipsis 48 0 0 ellipsis 48 ellipsis 49 0 0 ellipsis 49 50 50 0 0 ellipsis 50 paginator paginatorrange5000 100 for number oneachside onends expected in tests with self subtest numbernumber oneachsideoneachside onendsonends pagerange paginator getelidedpagerange number oneachsideoneachside onendsonends self assertisinstancepagerange collections abc generator self assertequallistpagerange expected class modelpaginationteststestcase classmethod def setuptestdatacls prepare a list of objects for pagination pubdate datetime2005 7 29 cls articles article objects createheadlinefarticle x pubdatepubdate for x in range1 10 def testfirstpageself paginator paginatorarticle objects orderbyid 5 p paginator page1 self assertequalpage 1 of 2 strp self assertsequenceequalp objectlist self articles 5 self asserttruep hasnext self assertfalsep hasprevious self asserttruep hasotherpages self assertequal2 p nextpagenumber with self assertraisesinvalidpage p previouspagenumber self assertequal1 p startindex self assertequal5 p endindex def testlastpageself paginator paginatorarticle objects orderbyid 5 p paginator page2 self assertequalpage 2 of 2 strp self assertsequenceequalp objectlist self articles5 self assertfalsep hasnext self asserttruep hasprevious self asserttruep hasotherpages with self assertraisesinvalidpage p nextpagenumber self assertequal1 p previouspagenumber self assertequal6 p startindex self assertequal9 p endindex def testpagegetitemself paginator paginatorarticle objects orderbyid 5 p paginator page1 objectlist queryset is not evaluated by an invalid getitem call this happens from the template engine when using e g pageobj hasprevious self assertisnonep objectlist resultcache msg page indices must be integers or slices not str with self assertraisesmessagetypeerror msg phasprevious self assertisnonep objectlist resultcache self assertnotisinstancep objectlist list make sure slicing the page object with numbers and slice objects work self assertequalp0 self articles0 self assertsequenceequalpslice2 self articles 2 after getitem is called objectlist is a list self assertisinstancep objectlist list def testpaginatingunorderedquerysetraiseswarningself msg pagination may yield inconsistent results with an unordered objectlist class pagination models article queryset with self assertwarnsmessageunorderedobjectlistwarning msg as cm paginatorarticle objects all 5 the warning points at the paginator caller i e the stacklevel is appropriate self assertequalcm filename file def testpaginatingemptyquerysetdoesnotwarnself with warnings catchwarningsrecordtrue as recorded paginatorarticle objects none 5 self assertequallenrecorded 0 def testpaginatingunorderedobjectlistraiseswarningself class objectlist ordered false objectlist objectlist msg pagination may yield inconsistent results with an unordered objectlist r formatobjectlist with self assertwarnsmessageunorderedobjectlistwarning msg paginatorobjectlist 5 tests for the paginator and page classes helper method that instantiates a paginator object from the passed params and then checks that its attributes match the passed output helper method that checks a single attribute and gives a nice error message upon test failure tests the paginator attributes using varying inputs each item is 2 tuple first tuple is paginator parameters object_list per_page orphans and allow_empty_first_page second tuple is resulting paginator attributes count num_pages and page_range ten items varying orphans no empty first page ten items varying orphans allow empty first page one item varying orphans no empty first page one item varying orphans allow empty first page zero items varying orphans no empty first page zero items varying orphans allow empty first page number if items one less than per_page number if items equal to per_page number if items one more than per_page number if items one more than per_page with one orphan non integer inputs invalid page numbers result in the correct exception being raised custom message default message with no content and allow_empty_first_page true 1 is a valid page number paginator can be passed other objects with a count method paginator can be passed other objects that implement __len__ helper method that instantiates a paginator object from the passed params and then checks that the start and end indexes of the passed page_num match those given as a 2 tuple in indexes paginator pages have the correct start and end indexes each item is 3 tuple first tuple is paginator parameters object_list per_page orphans and allow_empty_first_page second tuple is the start and end indexes of the first page third tuple is the start and end indexes of the last page ten items varying per_page no orphans ten items varying per_page with orphans one item varying orphans no empty first page one item varying orphans allow empty first page zero items varying orphans allow empty first page when no items and no empty first page we should get emptypage error a paginator page acts like a standard sequence a paginator subclass can use the _get_page hook to return an alternative to the standard page class paginator page_range should be an iterator paginator get_page returns a valid page even with invalid page arguments an empty page returns the last page non integer page returns the first page paginator get_page with an empty object_list an empty page returns the last page non integer page returns the first page paginator get_page raises emptypage if allow_empty_first_page false and object_list is empty paginator validate_number must be called range is not elided if not enough pages when using default arguments range should be elided if enough pages when using default arguments on_each_side 3 on_ends 2 range is not elided if not enough pages when using custom arguments when on_each_side and on_ends are both 1 but not both 1 it is a special case where the range is not elided until an extra page is added range should be elided if enough pages when using custom arguments on_each_side 2 on_ends 1 on_each_side 1 on_ends 3 on_each_side 4 on_ends 0 on_each_side 0 on_ends 1 on_each_side 0 on_ends 0 test pagination with django model instances prepare a list of objects for pagination tests proper behavior of a paginator page __getitem__ queryset evaluation slicing exception raised object_list queryset is not evaluated by an invalid __getitem__ call this happens from the template engine when using e g page_obj has_previous make sure slicing the page object with numbers and slice objects work after __getitem__ is called object_list is a list the warning points at the paginator caller i e the stacklevel is appropriate unordered object list warning with an object that has an ordered attribute but not a model attribute
import collections.abc
import unittest.mock
import warnings
from datetime import datetime

from django.core.paginator import (
    EmptyPage,
    InvalidPage,
    PageNotAnInteger,
    Paginator,
    UnorderedObjectListWarning,
)
from django.test import SimpleTestCase, TestCase

from .custom import ValidAdjacentNumsPaginator
from .models import Article


class PaginationTests(SimpleTestCase):
    

    def check_paginator(self, params, output):
        
        count, num_pages, page_range = output
        paginator = Paginator(*params)
        self.check_attribute("count", paginator, count, params)
        self.check_attribute("num_pages", paginator, num_pages, params)
        self.check_attribute("page_range", paginator, page_range, params, coerce=list)

    def check_attribute(self, name, paginator, expected, params, coerce=None):
        
        got = getattr(paginator, name)
        if coerce is not None:
            got = coerce(got)
        self.assertEqual(
            expected,
            got,
            "For '%s', expected %s but got %s.  Paginator parameters were: %s"
            % (name, expected, got, params),
        )

    def test_paginator(self):
        
        nine = [1, 2, 3, 4, 5, 6, 7, 8, 9]
        ten = nine + [10]
        eleven = ten + [11]
        tests = (
            
            
            
            
            
            
            ((ten, 4, 0, False), (10, 3, [1, 2, 3])),
            ((ten, 4, 1, False), (10, 3, [1, 2, 3])),
            ((ten, 4, 2, False), (10, 2, [1, 2])),
            ((ten, 4, 5, False), (10, 2, [1, 2])),
            ((ten, 4, 6, False), (10, 1, [1])),
            
            ((ten, 4, 0, True), (10, 3, [1, 2, 3])),
            ((ten, 4, 1, True), (10, 3, [1, 2, 3])),
            ((ten, 4, 2, True), (10, 2, [1, 2])),
            ((ten, 4, 5, True), (10, 2, [1, 2])),
            ((ten, 4, 6, True), (10, 1, [1])),
            
            (([1], 4, 0, False), (1, 1, [1])),
            (([1], 4, 1, False), (1, 1, [1])),
            (([1], 4, 2, False), (1, 1, [1])),
            
            (([1], 4, 0, True), (1, 1, [1])),
            (([1], 4, 1, True), (1, 1, [1])),
            (([1], 4, 2, True), (1, 1, [1])),
            
            (([], 4, 0, False), (0, 0, [])),
            (([], 4, 1, False), (0, 0, [])),
            (([], 4, 2, False), (0, 0, [])),
            
            (([], 4, 0, True), (0, 1, [1])),
            (([], 4, 1, True), (0, 1, [1])),
            (([], 4, 2, True), (0, 1, [1])),
            
            (([], 1, 0, True), (0, 1, [1])),
            (([], 1, 0, False), (0, 0, [])),
            (([1], 2, 0, True), (1, 1, [1])),
            ((nine, 10, 0, True), (9, 1, [1])),
            
            (([1], 1, 0, True), (1, 1, [1])),
            (([1, 2], 2, 0, True), (2, 1, [1])),
            ((ten, 10, 0, True), (10, 1, [1])),
            
            (([1, 2], 1, 0, True), (2, 2, [1, 2])),
            (([1, 2, 3], 2, 0, True), (3, 2, [1, 2])),
            ((eleven, 10, 0, True), (11, 2, [1, 2])),
            
            (([1, 2], 1, 1, True), (2, 1, [1])),
            (([1, 2, 3], 2, 1, True), (3, 1, [1])),
            ((eleven, 10, 1, True), (11, 1, [1])),
            
            ((ten, "4", 1, False), (10, 3, [1, 2, 3])),
            ((ten, "4", 1, False), (10, 3, [1, 2, 3])),
            ((ten, 4, "1", False), (10, 3, [1, 2, 3])),
            ((ten, 4, "1", False), (10, 3, [1, 2, 3])),
        )
        for params, output in tests:
            self.check_paginator(params, output)

    def test_invalid_page_number(self):
        
        paginator = Paginator([1, 2, 3], 2)
        with self.assertRaises(InvalidPage):
            paginator.page(3)
        with self.assertRaises(PageNotAnInteger):
            paginator.validate_number(None)
        with self.assertRaises(PageNotAnInteger):
            paginator.validate_number("x")
        with self.assertRaises(PageNotAnInteger):
            paginator.validate_number(1.2)

    def test_error_messages(self):
        error_messages = {
            "invalid_page": "Wrong page number",
            "min_page": "Too small",
            "no_results": "There is nothing here",
        }
        paginator = Paginator([1, 2, 3], 2, error_messages=error_messages)
        msg = "Wrong page number"
        with self.assertRaisesMessage(PageNotAnInteger, msg):
            paginator.validate_number(1.2)
        msg = "Too small"
        with self.assertRaisesMessage(EmptyPage, msg):
            paginator.validate_number(-1)
        msg = "There is nothing here"
        with self.assertRaisesMessage(EmptyPage, msg):
            paginator.validate_number(3)

        error_messages = {"min_page": "Too small"}
        paginator = Paginator([1, 2, 3], 2, error_messages=error_messages)
        
        msg = "Too small"
        with self.assertRaisesMessage(EmptyPage, msg):
            paginator.validate_number(-1)
        
        msg = "That page contains no results"
        with self.assertRaisesMessage(EmptyPage, msg):
            paginator.validate_number(3)

    def test_float_integer_page(self):
        paginator = Paginator([1, 2, 3], 2)
        self.assertEqual(paginator.validate_number(1.0), 1)

    def test_no_content_allow_empty_first_page(self):
        
        paginator = Paginator([], 2)
        self.assertEqual(paginator.validate_number(1), 1)

    def test_paginate_misc_classes(self):
        class CountContainer:
            def count(self):
                return 42

        
        paginator = Paginator(CountContainer(), 10)
        self.assertEqual(42, paginator.count)
        self.assertEqual(5, paginator.num_pages)
        self.assertEqual([1, 2, 3, 4, 5], list(paginator.page_range))

        
        class LenContainer:
            def __len__(self):
                return 42

        paginator = Paginator(LenContainer(), 10)
        self.assertEqual(42, paginator.count)
        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):
        
        paginator = Paginator(*params)
        if page_num == "first":
            page_num = 1
        elif page_num == "last":
            page_num = paginator.num_pages
        page = paginator.page(page_num)
        start, end = indexes
        msg = "For %s of page %s, expected %s but got %s. Paginator parameters were: %s"
        self.assertEqual(
            start,
            page.start_index(),
            msg % ("start index", page_num, start, page.start_index(), params),
        )
        self.assertEqual(
            end,
            page.end_index(),
            msg % ("end index", page_num, end, page.end_index(), params),
        )

    def test_page_indexes(self):
        
        ten = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        tests = (
            
            
            
            
            
            
            ((ten, 1, 0, True), (1, 1), (10, 10)),
            ((ten, 2, 0, True), (1, 2), (9, 10)),
            ((ten, 3, 0, True), (1, 3), (10, 10)),
            ((ten, 5, 0, True), (1, 5), (6, 10)),
            
            ((ten, 1, 1, True), (1, 1), (9, 10)),
            ((ten, 1, 2, True), (1, 1), (8, 10)),
            ((ten, 3, 1, True), (1, 3), (7, 10)),
            ((ten, 3, 2, True), (1, 3), (7, 10)),
            ((ten, 3, 4, True), (1, 3), (4, 10)),
            ((ten, 5, 1, True), (1, 5), (6, 10)),
            ((ten, 5, 2, True), (1, 5), (6, 10)),
            ((ten, 5, 5, True), (1, 10), (1, 10)),
            
            (([1], 4, 0, False), (1, 1), (1, 1)),
            (([1], 4, 1, False), (1, 1), (1, 1)),
            (([1], 4, 2, False), (1, 1), (1, 1)),
            
            (([1], 4, 0, True), (1, 1), (1, 1)),
            (([1], 4, 1, True), (1, 1), (1, 1)),
            (([1], 4, 2, True), (1, 1), (1, 1)),
            
            (([], 4, 0, True), (0, 0), (0, 0)),
            (([], 4, 1, True), (0, 0), (0, 0)),
            (([], 4, 2, True), (0, 0), (0, 0)),
        )
        for params, first, last in tests:
            self.check_indexes(params, "first", first)
            self.check_indexes(params, "last", last)

        
        with self.assertRaises(EmptyPage):
            self.check_indexes(([], 4, 0, False), 1, None)
        with self.assertRaises(EmptyPage):
            self.check_indexes(([], 4, 1, False), 1, None)
        with self.assertRaises(EmptyPage):
            self.check_indexes(([], 4, 2, False), 1, None)

    def test_page_sequence(self):
        
        eleven = "abcdefghijk"
        page2 = Paginator(eleven, per_page=5, orphans=1).page(2)
        self.assertEqual(len(page2), 6)
        self.assertIn("k", page2)
        self.assertNotIn("a", page2)
        self.assertEqual("".join(page2), "fghijk")
        self.assertEqual("".join(reversed(page2)), "kjihgf")

    def test_get_page_hook(self):
        
        eleven = "abcdefghijk"
        paginator = ValidAdjacentNumsPaginator(eleven, per_page=6)
        page1 = paginator.page(1)
        page2 = paginator.page(2)
        self.assertIsNone(page1.previous_page_number())
        self.assertEqual(page1.next_page_number(), 2)
        self.assertEqual(page2.previous_page_number(), 1)
        self.assertIsNone(page2.next_page_number())

    def test_page_range_iterator(self):
        
        self.assertIsInstance(Paginator([1, 2, 3], 2).page_range, type(range(0)))

    def test_get_page(self):
        
        paginator = Paginator([1, 2, 3], 2)
        page = paginator.get_page(1)
        self.assertEqual(page.number, 1)
        self.assertEqual(page.object_list, [1, 2])
        
        self.assertEqual(paginator.get_page(3).number, 2)
        
        self.assertEqual(paginator.get_page(None).number, 1)

    def test_get_page_empty_object_list(self):
        
        paginator = Paginator([], 2)
        
        self.assertEqual(paginator.get_page(1).number, 1)
        self.assertEqual(paginator.get_page(2).number, 1)
        
        self.assertEqual(paginator.get_page(None).number, 1)

    def test_get_page_empty_object_list_and_allow_empty_first_page_false(self):
        
        paginator = Paginator([], 2, allow_empty_first_page=False)
        with self.assertRaises(EmptyPage):
            paginator.get_page(1)

    def test_paginator_iteration(self):
        paginator = Paginator([1, 2, 3], 2)
        page_iterator = iter(paginator)
        for page, expected in enumerate(([1, 2], [3]), start=1):
            with self.subTest(page=page):
                self.assertEqual(expected, list(next(page_iterator)))

        self.assertEqual(
            [str(page) for page in iter(paginator)],
            ["<Page 1 of 2>", "<Page 2 of 2>"],
        )

    def test_get_elided_page_range(self):
        
        paginator = Paginator([1, 2, 3], 2)
        with unittest.mock.patch.object(paginator, "validate_number") as mock:
            mock.assert_not_called()
            list(paginator.get_elided_page_range(2))
            mock.assert_called_with(2)

        ELLIPSIS = Paginator.ELLIPSIS

        
        paginator = Paginator(range(10 * 100), 100)
        page_range = paginator.get_elided_page_range(1)
        self.assertIsInstance(page_range, collections.abc.Generator)
        self.assertNotIn(ELLIPSIS, page_range)
        paginator = Paginator(range(10 * 100 + 1), 100)
        self.assertIsInstance(page_range, collections.abc.Generator)
        page_range = paginator.get_elided_page_range(1)
        self.assertIn(ELLIPSIS, page_range)

        
        tests = [
            
            (1, [1, 2, 3, 4, ELLIPSIS, 49, 50]),
            (6, [1, 2, 3, 4, 5, 6, 7, 8, 9, ELLIPSIS, 49, 50]),
            (7, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ELLIPSIS, 49, 50]),
            (8, [1, 2, ELLIPSIS, 5, 6, 7, 8, 9, 10, 11, ELLIPSIS, 49, 50]),
            (43, [1, 2, ELLIPSIS, 40, 41, 42, 43, 44, 45, 46, ELLIPSIS, 49, 50]),
            (44, [1, 2, ELLIPSIS, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50]),
            (45, [1, 2, ELLIPSIS, 42, 43, 44, 45, 46, 47, 48, 49, 50]),
            (50, [1, 2, ELLIPSIS, 47, 48, 49, 50]),
        ]
        paginator = Paginator(range(5000), 100)
        for number, expected in tests:
            with self.subTest(number=number):
                page_range = paginator.get_elided_page_range(number)
                self.assertIsInstance(page_range, collections.abc.Generator)
                self.assertEqual(list(page_range), expected)

        
        tests = [
            (6, 2, 1, 1),
            (8, 1, 3, 1),
            (8, 4, 0, 1),
            (4, 1, 1, 1),
            
            
            
            (2, 0, 1, 2),
            (2, 1, 0, 2),
            (1, 0, 0, 2),
        ]
        for pages, on_each_side, on_ends, elided_after in tests:
            for offset in range(elided_after + 1):
                with self.subTest(
                    pages=pages,
                    offset=elided_after,
                    on_each_side=on_each_side,
                    on_ends=on_ends,
                ):
                    paginator = Paginator(range((pages + offset) * 100), 100)
                    page_range = paginator.get_elided_page_range(
                        1,
                        on_each_side=on_each_side,
                        on_ends=on_ends,
                    )
                    self.assertIsInstance(page_range, collections.abc.Generator)
                    if offset < elided_after:
                        self.assertNotIn(ELLIPSIS, page_range)
                    else:
                        self.assertIn(ELLIPSIS, page_range)

        
        tests = [
            
            (1, 2, 1, [1, 2, 3, ELLIPSIS, 50]),
            (4, 2, 1, [1, 2, 3, 4, 5, 6, ELLIPSIS, 50]),
            (5, 2, 1, [1, 2, 3, 4, 5, 6, 7, ELLIPSIS, 50]),
            (6, 2, 1, [1, ELLIPSIS, 4, 5, 6, 7, 8, ELLIPSIS, 50]),
            (45, 2, 1, [1, ELLIPSIS, 43, 44, 45, 46, 47, ELLIPSIS, 50]),
            (46, 2, 1, [1, ELLIPSIS, 44, 45, 46, 47, 48, 49, 50]),
            (47, 2, 1, [1, ELLIPSIS, 45, 46, 47, 48, 49, 50]),
            (50, 2, 1, [1, ELLIPSIS, 48, 49, 50]),
            
            (1, 1, 3, [1, 2, ELLIPSIS, 48, 49, 50]),
            (5, 1, 3, [1, 2, 3, 4, 5, 6, ELLIPSIS, 48, 49, 50]),
            (6, 1, 3, [1, 2, 3, 4, 5, 6, 7, ELLIPSIS, 48, 49, 50]),
            (7, 1, 3, [1, 2, 3, ELLIPSIS, 6, 7, 8, ELLIPSIS, 48, 49, 50]),
            (44, 1, 3, [1, 2, 3, ELLIPSIS, 43, 44, 45, ELLIPSIS, 48, 49, 50]),
            (45, 1, 3, [1, 2, 3, ELLIPSIS, 44, 45, 46, 47, 48, 49, 50]),
            (46, 1, 3, [1, 2, 3, ELLIPSIS, 45, 46, 47, 48, 49, 50]),
            (50, 1, 3, [1, 2, 3, ELLIPSIS, 49, 50]),
            
            (1, 4, 0, [1, 2, 3, 4, 5, ELLIPSIS]),
            (5, 4, 0, [1, 2, 3, 4, 5, 6, 7, 8, 9, ELLIPSIS]),
            (6, 4, 0, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ELLIPSIS]),
            (7, 4, 0, [ELLIPSIS, 3, 4, 5, 6, 7, 8, 9, 10, 11, ELLIPSIS]),
            (44, 4, 0, [ELLIPSIS, 40, 41, 42, 43, 44, 45, 46, 47, 48, ELLIPSIS]),
            (45, 4, 0, [ELLIPSIS, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50]),
            (46, 4, 0, [ELLIPSIS, 42, 43, 44, 45, 46, 47, 48, 49, 50]),
            (50, 4, 0, [ELLIPSIS, 46, 47, 48, 49, 50]),
            
            (1, 0, 1, [1, ELLIPSIS, 50]),
            (2, 0, 1, [1, 2, ELLIPSIS, 50]),
            (3, 0, 1, [1, 2, 3, ELLIPSIS, 50]),
            (4, 0, 1, [1, ELLIPSIS, 4, ELLIPSIS, 50]),
            (47, 0, 1, [1, ELLIPSIS, 47, ELLIPSIS, 50]),
            (48, 0, 1, [1, ELLIPSIS, 48, 49, 50]),
            (49, 0, 1, [1, ELLIPSIS, 49, 50]),
            (50, 0, 1, [1, ELLIPSIS, 50]),
            
            (1, 0, 0, [1, ELLIPSIS]),
            (2, 0, 0, [1, 2, ELLIPSIS]),
            (3, 0, 0, [ELLIPSIS, 3, ELLIPSIS]),
            (48, 0, 0, [ELLIPSIS, 48, ELLIPSIS]),
            (49, 0, 0, [ELLIPSIS, 49, 50]),
            (50, 0, 0, [ELLIPSIS, 50]),
        ]
        paginator = Paginator(range(5000), 100)
        for number, on_each_side, on_ends, expected in tests:
            with self.subTest(
                number=number, on_each_side=on_each_side, on_ends=on_ends
            ):
                page_range = paginator.get_elided_page_range(
                    number,
                    on_each_side=on_each_side,
                    on_ends=on_ends,
                )
                self.assertIsInstance(page_range, collections.abc.Generator)
                self.assertEqual(list(page_range), expected)


class ModelPaginationTests(TestCase):
    

    @classmethod
    def setUpTestData(cls):
        
        pub_date = datetime(2005, 7, 29)
        cls.articles = [
            Article.objects.create(headline=f"Article {x}", pub_date=pub_date)
            for x in range(1, 10)
        ]

    def test_first_page(self):
        paginator = Paginator(Article.objects.order_by("id"), 5)
        p = paginator.page(1)
        self.assertEqual("<Page 1 of 2>", str(p))
        self.assertSequenceEqual(p.object_list, self.articles[:5])
        self.assertTrue(p.has_next())
        self.assertFalse(p.has_previous())
        self.assertTrue(p.has_other_pages())
        self.assertEqual(2, p.next_page_number())
        with self.assertRaises(InvalidPage):
            p.previous_page_number()
        self.assertEqual(1, p.start_index())
        self.assertEqual(5, p.end_index())

    def test_last_page(self):
        paginator = Paginator(Article.objects.order_by("id"), 5)
        p = paginator.page(2)
        self.assertEqual("<Page 2 of 2>", str(p))
        self.assertSequenceEqual(p.object_list, self.articles[5:])
        self.assertFalse(p.has_next())
        self.assertTrue(p.has_previous())
        self.assertTrue(p.has_other_pages())
        with self.assertRaises(InvalidPage):
            p.next_page_number()
        self.assertEqual(1, p.previous_page_number())
        self.assertEqual(6, p.start_index())
        self.assertEqual(9, p.end_index())

    def test_page_getitem(self):
        
        paginator = Paginator(Article.objects.order_by("id"), 5)
        p = paginator.page(1)

        
        
        
        self.assertIsNone(p.object_list._result_cache)
        msg = "Page indices must be integers or slices, not str."
        with self.assertRaisesMessage(TypeError, msg):
            p["has_previous"]
        self.assertIsNone(p.object_list._result_cache)
        self.assertNotIsInstance(p.object_list, list)

        
        self.assertEqual(p[0], self.articles[0])
        self.assertSequenceEqual(p[slice(2)], self.articles[:2])
        
        self.assertIsInstance(p.object_list, list)

    def test_paginating_unordered_queryset_raises_warning(self):
        msg = (
            "Pagination may yield inconsistent results with an unordered "
            "object_list: <class 'pagination.models.Article'> QuerySet."
        )
        with self.assertWarnsMessage(UnorderedObjectListWarning, msg) as cm:
            Paginator(Article.objects.all(), 5)
        
        
        self.assertEqual(cm.filename, __file__)

    def test_paginating_empty_queryset_does_not_warn(self):
        with warnings.catch_warnings(record=True) as recorded:
            Paginator(Article.objects.none(), 5)
        self.assertEqual(len(recorded), 0)

    def test_paginating_unordered_object_list_raises_warning(self):
        

        class ObjectList:
            ordered = False

        object_list = ObjectList()
        msg = (
            "Pagination may yield inconsistent results with an unordered "
            "object_list: {!r}.".format(object_list)
        )
        with self.assertWarnsMessage(UnorderedObjectListWarning, msg):
            Paginator(object_list, 5)