diff --git a/django/utils/http.py b/django/utils/http.py index f3a3dce58c..272e73f190 100644 --- a/django/utils/http.py +++ b/django/utils/http.py @@ -167,8 +167,9 @@ def base36_to_int(s): if len(s) > 13: raise ValueError("Base36 input too large") value = int(s, 36) - # ... then do a final check that the value will fit into an int. - if value > sys.maxint: + # ... then do a final check that the value will fit into an int to avoid + # returning a long (#15067). The long type was removed in Python 3. + if not six.PY3 and value > sys.maxint: raise ValueError("Base36 input too large") return value @@ -178,8 +179,13 @@ def int_to_base36(i): """ digits = "0123456789abcdefghijklmnopqrstuvwxyz" factor = 0 - if not 0 <= i <= sys.maxint: - raise ValueError("Base36 conversion input too large or incorrect type.") + if i < 0: + raise ValueError("Negative base36 conversion input.") + if not six.PY3: + if not isinstance(i, six.integer_types): + raise TypeError("Non-integer base36 conversion input.") + if i > sys.maxint: + raise ValueError("Base36 conversion input too large.") # Find starting factor while True: factor += 1 diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index c2f2025bc3..5157c399da 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -504,11 +504,13 @@ escaping HTML. .. function:: base36_to_int(s) - Converts a base 36 string to an integer. + Converts a base 36 string to an integer. On Python 2 the output is + guaranteed to be an :class:`int` and not a :class:`long`. .. function:: int_to_base36(i) - Converts a positive integer less than sys.maxint to a base 36 string. + Converts a positive integer to a base 36 string. On Python 2 ``i`` must be + smaller than :attr:`sys.maxint`. ``django.utils.safestring`` =========================== diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt index aae8b25e07..968d63e1f3 100644 --- a/docs/releases/1.5.txt +++ b/docs/releases/1.5.txt @@ -244,6 +244,9 @@ Miscellaneous * GeoDjango dropped support for GDAL < 1.5 +* :func:`~django.utils.http.int_to_base36` properly raises a :exc:`TypeError` + instead of :exc:`ValueError` for non-integer inputs. + Features deprecated in 1.5 ========================== diff --git a/tests/regressiontests/utils/http.py b/tests/regressiontests/utils/http.py index 67dcd7af89..f22e05496d 100644 --- a/tests/regressiontests/utils/http.py +++ b/tests/regressiontests/utils/http.py @@ -1,10 +1,11 @@ import sys -from django.utils import http -from django.utils import unittest -from django.utils.datastructures import MultiValueDict from django.http import HttpResponse, utils from django.test import RequestFactory +from django.utils.datastructures import MultiValueDict +from django.utils import http +from django.utils import six +from django.utils import unittest class TestUtilsHttp(unittest.TestCase): @@ -110,22 +111,23 @@ class TestUtilsHttp(unittest.TestCase): def test_base36(self): # reciprocity works - for n in [0, 1, 1000, 1000000, sys.maxint]: + for n in [0, 1, 1000, 1000000]: self.assertEqual(n, http.base36_to_int(http.int_to_base36(n))) + if not six.PY3: + self.assertEqual(sys.maxint, http.base36_to_int(http.int_to_base36(sys.maxint))) # bad input - for n in [-1, sys.maxint+1, '1', 'foo', {1:2}, (1,2,3)]: - self.assertRaises(ValueError, http.int_to_base36, n) - + self.assertRaises(ValueError, http.int_to_base36, -1) + if not six.PY3: + self.assertRaises(ValueError, http.int_to_base36, sys.maxint + 1) + for n in ['1', 'foo', {1: 2}, (1, 2, 3), 3.141]: + self.assertRaises(TypeError, http.int_to_base36, n) + for n in ['#', ' ']: self.assertRaises(ValueError, http.base36_to_int, n) - - for n in [123, {1:2}, (1,2,3)]: + for n in [123, {1: 2}, (1, 2, 3), 3.141]: self.assertRaises(TypeError, http.base36_to_int, n) - # non-integer input - self.assertRaises(TypeError, http.int_to_base36, 3.141) - # more explicit output testing for n, b36 in [(0, '0'), (1, '1'), (42, '16'), (818469960, 'django')]: self.assertEqual(http.int_to_base36(n), b36)