Fixed #20094 - Be more careful when checking for Iterator
Python 2.6 has some different behaviour when checking isinstance(foo, collections.Iterator).
This commit is contained in:
parent
f7795e968d
commit
829dc3c5a6
|
@ -1,6 +1,5 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import collections
|
|
||||||
import copy
|
import copy
|
||||||
import datetime
|
import datetime
|
||||||
import decimal
|
import decimal
|
||||||
|
@ -18,6 +17,7 @@ from django.core import exceptions, validators
|
||||||
from django.utils.datastructures import DictWrapper
|
from django.utils.datastructures import DictWrapper
|
||||||
from django.utils.dateparse import parse_date, parse_datetime, parse_time
|
from django.utils.dateparse import parse_date, parse_datetime, parse_time
|
||||||
from django.utils.functional import curry, total_ordering
|
from django.utils.functional import curry, total_ordering
|
||||||
|
from django.utils.itercompat import is_iterator
|
||||||
from django.utils.text import capfirst
|
from django.utils.text import capfirst
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
@ -488,7 +488,7 @@ class Field(object):
|
||||||
return bound_field_class(self, fieldmapping, original)
|
return bound_field_class(self, fieldmapping, original)
|
||||||
|
|
||||||
def _get_choices(self):
|
def _get_choices(self):
|
||||||
if isinstance(self._choices, collections.Iterator):
|
if is_iterator(self._choices):
|
||||||
choices, self._choices = tee(self._choices)
|
choices, self._choices = tee(self._choices)
|
||||||
return choices
|
return choices
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -4,7 +4,6 @@ Code to manage the creation and SQL rendering of 'where' constraints.
|
||||||
|
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
import collections
|
|
||||||
import datetime
|
import datetime
|
||||||
from itertools import repeat
|
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.fields import DateTimeField, Field
|
||||||
from django.db.models.sql.datastructures import EmptyResultSet, Empty
|
from django.db.models.sql.datastructures import EmptyResultSet, Empty
|
||||||
from django.db.models.sql.aggregates import Aggregate
|
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.six.moves import xrange
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils import tree
|
from django.utils import tree
|
||||||
|
@ -58,7 +58,7 @@ class WhereNode(tree.Node):
|
||||||
if not isinstance(data, (list, tuple)):
|
if not isinstance(data, (list, tuple)):
|
||||||
return data
|
return data
|
||||||
obj, lookup_type, value = data
|
obj, lookup_type, value = data
|
||||||
if isinstance(value, collections.Iterator):
|
if is_iterator(value):
|
||||||
# Consume any generators immediately, so that we can determine
|
# Consume any generators immediately, so that we can determine
|
||||||
# emptiness and transform any non-empty values correctly.
|
# emptiness and transform any non-empty values correctly.
|
||||||
value = list(value)
|
value = list(value)
|
||||||
|
|
|
@ -4,10 +4,12 @@ Where possible, we try to use the system-native version and only fall back to
|
||||||
these implementations if necessary.
|
these implementations if necessary.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.utils.six.moves import builtins
|
import collections
|
||||||
import itertools
|
import itertools
|
||||||
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
|
|
||||||
def is_iterable(x):
|
def is_iterable(x):
|
||||||
"A implementation independent way of checking for iterables"
|
"A implementation independent way of checking for iterables"
|
||||||
try:
|
try:
|
||||||
|
@ -17,6 +19,17 @@ def is_iterable(x):
|
||||||
else:
|
else:
|
||||||
return True
|
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):
|
def product(*args, **kwds):
|
||||||
warnings.warn("django.utils.itercompat.product is deprecated; use the native version instead",
|
warnings.warn("django.utils.itercompat.product is deprecated; use the native version instead",
|
||||||
DeprecationWarning, stacklevel=2)
|
DeprecationWarning, stacklevel=2)
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
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)
|
|
@ -1 +1,13 @@
|
||||||
# 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)
|
||||||
|
|
|
@ -18,6 +18,7 @@ from .feedgenerator import FeedgeneratorTest
|
||||||
from .functional import FunctionalTestCase
|
from .functional import FunctionalTestCase
|
||||||
from .html import TestUtilsHtml
|
from .html import TestUtilsHtml
|
||||||
from .http import TestUtilsHttp, ETagProcessingTests, HttpDateProcessingTests
|
from .http import TestUtilsHttp, ETagProcessingTests, HttpDateProcessingTests
|
||||||
|
from .itercompat import TestIsIterator
|
||||||
from .ipv6 import TestUtilsIPv6
|
from .ipv6 import TestUtilsIPv6
|
||||||
from .jslex import JsToCForGettextTest, JsTokensTest
|
from .jslex import JsToCForGettextTest, JsTokensTest
|
||||||
from .module_loading import (CustomLoader, DefaultLoader, EggLoader,
|
from .module_loading import (CustomLoader, DefaultLoader, EggLoader,
|
||||||
|
|
Loading…
Reference in New Issue