Commit 829dc3c5 authored by Marc Tamlyn's avatar Marc Tamlyn Committed by Claude Paroz
Browse files

Fixed #20094 - Be more careful when checking for Iterator

Python 2.6 has some different behaviour when checking
isinstance(foo, collections.Iterator).
parent f7795e96
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
from __future__ import unicode_literals

import collections
import copy
import datetime
import decimal
@@ -18,6 +17,7 @@ from django.core import exceptions, validators
from django.utils.datastructures import DictWrapper
from django.utils.dateparse import parse_date, parse_datetime, parse_time
from django.utils.functional import curry, total_ordering
from django.utils.itercompat import is_iterator
from django.utils.text import capfirst
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
@@ -488,7 +488,7 @@ class Field(object):
        return bound_field_class(self, fieldmapping, original)

    def _get_choices(self):
        if isinstance(self._choices, collections.Iterator):
        if is_iterator(self._choices):
            choices, self._choices = tee(self._choices)
            return choices
        else:
+2 −2
Original line number Diff line number Diff line
@@ -4,7 +4,6 @@ Code to manage the creation and SQL rendering of 'where' constraints.

from __future__ import absolute_import

import collections
import datetime
from itertools import repeat

@@ -12,6 +11,7 @@ from django.conf import settings
from django.db.models.fields import DateTimeField, Field
from django.db.models.sql.datastructures import EmptyResultSet, Empty
from django.db.models.sql.aggregates import Aggregate
from django.utils.itercompat import is_iterator
from django.utils.six.moves import xrange
from django.utils import timezone
from django.utils import tree
@@ -58,7 +58,7 @@ class WhereNode(tree.Node):
        if not isinstance(data, (list, tuple)):
            return data
        obj, lookup_type, value = data
        if isinstance(value, collections.Iterator):
        if is_iterator(value):
            # Consume any generators immediately, so that we can determine
            # emptiness and transform any non-empty values correctly.
            value = list(value)
+14 −1
Original line number Diff line number Diff line
@@ -4,10 +4,12 @@ Where possible, we try to use the system-native version and only fall back to
these implementations if necessary.
"""

from django.utils.six.moves import builtins
import collections
import itertools
import sys
import warnings


def is_iterable(x):
    "A implementation independent way of checking for iterables"
    try:
@@ -17,6 +19,17 @@ def is_iterable(x):
    else:
        return True

def is_iterator(x):
    """An implementation independent way of checking for iterators

    Python 2.6 has a different implementation of collections.Iterator which
    accepts anything with a `next` method. 2.7+ requires and `__iter__` method
    as well.
    """
    if sys.version_info >= (2, 7):
        return isinstance(x, collections.Iterator)
    return isinstance(x, collections.Iterator) and hasattr(x, '__iter__')

def product(*args, **kwds):
    warnings.warn("django.utils.itercompat.product is deprecated; use the native version instead",
                  DeprecationWarning, stacklevel=2)
+11 −0
Original line number Diff line number Diff line
from django.test import TestCase

from .models import Category, Thing


class TestIsIterator(TestCase):
    def test_regression(self):
        """This failed on Django 1.5/Py2.6 because category has a next method."""
        category = Category.objects.create(name='category')
        Thing.objects.create(category=category)
        Thing.objects.filter(category=category)
+13 −1
Original line number Diff line number Diff line
# Test runner needs a models.py file.
from django.db import models


class Category(models.Model):
    name = models.CharField(max_length=100)

    def next(self):
        return self


class Thing(models.Model):
    name = models.CharField(max_length=100)
    category = models.ForeignKey(Category)
Loading