diff --git a/INSTALL b/INSTALL index 92be6da650..be64877476 100644 --- a/INSTALL +++ b/INSTALL @@ -1,17 +1,10 @@ Thanks for downloading Django. -To install it, make sure you have Python 2.7 or greater installed. Then run +To install it, make sure you have Python 3.4 or greater installed. Then run this command from the command prompt: python setup.py install If you're upgrading from a previous version, you need to remove it first. -AS AN ALTERNATIVE, you can just copy the entire "django" directory to Python's -site-packages directory, which is located wherever your Python installation -lives. Some places you might check are: - - /usr/lib/python2.7/site-packages (Unix, Python 2.7) - C:\\PYTHON\site-packages (Windows) - For more detailed instructions, see docs/intro/install.txt. diff --git a/django/apps/config.py b/django/apps/config.py index 78762dd612..1bc684b3ca 100644 --- a/django/apps/config.py +++ b/django/apps/config.py @@ -17,7 +17,7 @@ class AppConfig: self.name = app_name # Root module for the application eg. . + # from 'django/contrib/admin/__init__.py'>. self.module = app_module # Reference to the Apps registry that holds this AppConfig. Set by the @@ -37,13 +37,12 @@ class AppConfig: self.verbose_name = self.label.title() # Filesystem path to the application directory eg. - # u'/usr/lib/python2.7/dist-packages/django/contrib/admin'. Unicode on - # Python 2 and a str on Python 3. + # '/path/to/django/contrib/admin'. if not hasattr(self, 'path'): self.path = self._path_from_module(app_module) # Module containing models eg. . Set by import_models(). + # from 'django/contrib/admin/models.py'>. Set by import_models(). # None if the application doesn't have a models module. self.models_module = None diff --git a/django/conf/project_template/manage.py-tpl b/django/conf/project_template/manage.py-tpl index 41309844e0..9f83e65491 100755 --- a/django/conf/project_template/manage.py-tpl +++ b/django/conf/project_template/manage.py-tpl @@ -6,17 +6,10 @@ if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "{{ project_name }}.settings") try: from django.core.management import execute_from_command_line - except ImportError: - # The above import may fail for some other reason. Ensure that the - # issue is really that Django is missing to avoid masking other - # exceptions on Python 2. - try: - import django - except ImportError: - raise ImportError( - "Couldn't import Django. Are you sure it's installed and " - "available on your PYTHONPATH environment variable? Did you " - "forget to activate a virtual environment?" - ) - raise + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc execute_from_command_line(sys.argv) diff --git a/django/contrib/gis/measure.py b/django/contrib/gis/measure.py index fa3d7c34f2..d19737a118 100644 --- a/django/contrib/gis/measure.py +++ b/django/contrib/gis/measure.py @@ -159,9 +159,6 @@ class MeasureBase: else: raise TypeError('%(class)s must be divided with number or %(class)s' % {"class": pretty_name(self)}) - def __div__(self, other): # Python 2 compatibility - return type(self).__truediv__(self, other) - def __itruediv__(self, other): if isinstance(other, NUMERIC_TYPES): self.standard /= float(other) @@ -169,9 +166,6 @@ class MeasureBase: else: raise TypeError('%(class)s must be divided with number' % {"class": pretty_name(self)}) - def __idiv__(self, other): # Python 2 compatibility - return type(self).__itruediv__(self, other) - def __bool__(self): return bool(self.standard) @@ -333,9 +327,6 @@ class Area(MeasureBase): else: raise TypeError('%(class)s must be divided by a number' % {"class": pretty_name(self)}) - def __div__(self, other): # Python 2 compatibility - return type(self).__truediv__(self, other) - # Shortcuts D = Distance diff --git a/django/core/checks/model_checks.py b/django/core/checks/model_checks.py index a8b5652662..2397fe3bb2 100644 --- a/django/core/checks/model_checks.py +++ b/django/core/checks/model_checks.py @@ -64,7 +64,7 @@ def _check_lazy_references(apps, ignore=None): operation, args, keywords = obj, [], {} while hasattr(operation, 'func'): # The or clauses are redundant but work around a bug (#25945) in - # functools.partial in Python 3 <= 3.5.1 and Python 2 <= 2.7.11. + # functools.partial in Python <= 3.5.1. args.extend(getattr(operation, 'args', []) or []) keywords.update(getattr(operation, 'keywords', {}) or {}) operation = operation.func diff --git a/django/core/files/temp.py b/django/core/files/temp.py index f5aea1a9a8..d5c56a0ad0 100644 --- a/django/core/files/temp.py +++ b/django/core/files/temp.py @@ -9,8 +9,8 @@ if the same flag is not provided [1][2]. Note that this does not address the more general issue of opening a file for writing and reading in multiple processes in a manner that works across platforms. -Also note that the custom version of NamedTemporaryFile does not support the -full range of keyword arguments available in Python 2.6+ and 3.0+. +The custom version of NamedTemporaryFile doesn't support the same keyword +arguments available in tempfile.NamedTemporaryFile. 1: https://mail.python.org/pipermail/python-list/2005-December/336957.html 2: http://bugs.python.org/issue14243 @@ -30,10 +30,9 @@ if os.name == 'nt': Temporary file object constructor that supports reopening of the temporary file in Windows. - Note that unlike tempfile.NamedTemporaryFile from the standard library, - __init__() does not support the 'delete' keyword argument in - Python 2.6+, or the 'delete', 'buffering', 'encoding', or 'newline' - keyword arguments in Python 3.0+. + Unlike tempfile.NamedTemporaryFile from the standard library, + __init__() doesn't support the 'delete', 'buffering', 'encoding', or + 'newline' keyword arguments. """ def __init__(self, mode='w+b', bufsize=-1, suffix='', prefix='', dir=None): fd, name = tempfile.mkstemp(suffix=suffix, prefix=prefix, dir=dir) diff --git a/django/core/servers/basehttp.py b/django/core/servers/basehttp.py index a9ab0668f7..5a551b8ab1 100644 --- a/django/core/servers/basehttp.py +++ b/django/core/servers/basehttp.py @@ -83,7 +83,6 @@ class WSGIServer(simple_server.WSGIServer): super(WSGIServer, self).handle_error(request, client_address) -# Inheriting from object required on Python 2. class ServerHandler(simple_server.ServerHandler): def handle_error(self): # Ignore broken pipe errors, otherwise pass on diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index d056392753..1b5dc864b0 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -2,7 +2,6 @@ MySQL database backend for Django. Requires mysqlclient: https://pypi.python.org/pypi/mysqlclient/ -MySQLdb is supported for Python 2 only: http://sourceforge.net/projects/mysql-python """ import re import sys diff --git a/django/db/backends/oracle/introspection.py b/django/db/backends/oracle/introspection.py index 7af2a18a8d..8a648dba3f 100644 --- a/django/db/backends/oracle/introspection.py +++ b/django/db/backends/oracle/introspection.py @@ -81,7 +81,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): self.cache_bust_counter)) description = [] for desc in cursor.description: - name = force_text(desc[0]) # cx_Oracle always returns a 'str' on both Python 2 and 3 + name = force_text(desc[0]) # cx_Oracle always returns a 'str' internal_size, default = field_map[name] name = name % {} # cx_Oracle, for some reason, doubles percent signs. description.append(FieldInfo(*(name.lower(),) + desc[1:3] + (internal_size,) + desc[4:] + (default,))) diff --git a/django/db/migrations/serializer.py b/django/db/migrations/serializer.py index e15ffd87b5..58f90beaa2 100644 --- a/django/db/migrations/serializer.py +++ b/django/db/migrations/serializer.py @@ -160,24 +160,14 @@ class FunctionTypeSerializer(BaseSerializer): if "<" not in self.value.__qualname__: # Qualname can include return "%s.%s" % \ (self.value.__module__, self.value.__qualname__), {"import %s" % self.value.__module__} - # Python 2/fallback version + # Fallback version module_name = self.value.__module__ - # Make sure it's actually there and not an unbound method + # Make sure it's actually there module = import_module(module_name) if not hasattr(module, self.value.__name__): raise ValueError( - "Could not find function %s in %s.\n" - "Please note that due to Python 2 limitations, you cannot " - "serialize unbound method functions (e.g. a method " - "declared and used in the same class body). Please move " - "the function into the main module body to use migrations.\n" - "For more information, see " - "https://docs.djangoproject.com/en/%s/topics/migrations/#serializing-values" - % (self.value.__name__, module_name, get_docs_version()) + "Could not find function %s in %s.\n" % (self.value.__name__, module_name) ) - # Needed on Python 2 only - if module_name == '__builtin__': - return self.value.__name__, set() return "%s.%s" % (module_name, self.value.__name__), {"import %s" % module_name} diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py index de60a160e0..a1cec2393a 100644 --- a/django/db/models/expressions.py +++ b/django/db/models/expressions.py @@ -60,9 +60,6 @@ class Combinable: def __truediv__(self, other): return self._combine(other, self.DIV, False) - def __div__(self, other): # Python 2 compatibility - return type(self).__truediv__(self, other) - def __mod__(self, other): return self._combine(other, self.MOD, False) @@ -103,9 +100,6 @@ class Combinable: def __rtruediv__(self, other): return self._combine(other, self.DIV, True) - def __rdiv__(self, other): # Python 2 compatibility - return type(self).__rtruediv__(self, other) - def __rmod__(self, other): return self._combine(other, self.MOD, True) diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py index 54088b4fbb..c94a03f549 100644 --- a/django/db/models/query_utils.py +++ b/django/db/models/query_utils.py @@ -27,11 +27,8 @@ class InvalidQuery(Exception): def subclasses(cls): yield cls - # Python 2 lacks 'yield from', which could replace the inner loop for subclass in cls.__subclasses__(): - # yield from subclasses(subclass) - for item in subclasses(subclass): - yield item + yield from subclasses(subclass) class QueryWrapper: diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index 1be406d704..5e5984dfb3 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -875,14 +875,8 @@ class SQLCompiler: try: cursor.execute(sql, params) except Exception: - try: - # Might fail for server-side cursors (e.g. connection closed) - cursor.close() - except Exception: - # Ignore clean up errors and raise the original error instead. - # Python 2 doesn't chain exceptions. Remove this error - # silencing when dropping Python 2 compatibility. - pass + # Might fail for server-side cursors (e.g. connection closed) + cursor.close() raise if result_type == CURSOR: diff --git a/django/forms/utils.py b/django/forms/utils.py index 59d70178f4..b23e6be0cc 100644 --- a/django/forms/utils.py +++ b/django/forms/utils.py @@ -1,5 +1,6 @@ import json import sys +from collections import UserList from django.conf import settings from django.core.exceptions import ValidationError # backwards compatibility @@ -8,11 +9,6 @@ from django.utils.encoding import force_text from django.utils.html import escape, format_html, format_html_join, html_safe from django.utils.translation import ugettext_lazy as _ -try: - from collections import UserList -except ImportError: # Python 2 - from UserList import UserList - def pretty_name(name): """Converts 'first_name' to 'First name'""" diff --git a/django/test/utils.py b/django/test/utils.py index 2bd6bb4d0f..ef837ce5c7 100644 --- a/django/test/utils.py +++ b/django/test/utils.py @@ -310,7 +310,7 @@ def get_runner(settings, test_runner_class=None): test_runner_class = settings.TEST_RUNNER test_path = test_runner_class.split('.') - # Allow for Python 2.5 relative paths + # Allow for relative paths if len(test_path) > 1: test_module_name = '.'.join(test_path[:-1]) else: diff --git a/django/utils/deconstruct.py b/django/utils/deconstruct.py index 848b939b40..6bc70f7e53 100644 --- a/django/utils/deconstruct.py +++ b/django/utils/deconstruct.py @@ -24,7 +24,7 @@ def deconstructible(*args, **kwargs): Returns a 3-tuple of class import path, positional arguments, and keyword arguments. """ - # Python 2/fallback version + # Fallback version if path: module_name, _, name = path.rpartition('.') else: diff --git a/django/utils/html.py b/django/utils/html.py index fb9c18219c..f7f9a63350 100644 --- a/django/utils/html.py +++ b/django/utils/html.py @@ -172,9 +172,7 @@ def strip_tags(value): while '<' in value and '>' in value: new_value = _strip_once(value) if len(new_value) >= len(value): - # _strip_once was not able to detect more tags or length increased - # due to http://bugs.python.org/issue20288 - # (affects Python 2 < 2.7.7 and Python 3 < 3.3.5) + # _strip_once was not able to detect more tags break value = new_value return value diff --git a/django/utils/html_parser.py b/django/utils/html_parser.py index e3e19ee9c3..6b46ddc368 100644 --- a/django/utils/html_parser.py +++ b/django/utils/html_parser.py @@ -11,9 +11,7 @@ except AttributeError: class HTMLParser(html.parser.HTMLParser): """Explicitly set convert_charrefs to be False. - This silences a deprecation warning on Python 3.4, but we can't do - it at call time because Python 2.7 does not have the keyword - argument. + This silences a deprecation warning on Python 3.4. """ def __init__(self, convert_charrefs=False, **kwargs): html.parser.HTMLParser.__init__(self, convert_charrefs=convert_charrefs, **kwargs) diff --git a/django/utils/translation/trans_real.py b/django/utils/translation/trans_real.py index 62ff122621..c8913feb71 100644 --- a/django/utils/translation/trans_real.py +++ b/django/utils/translation/trans_real.py @@ -101,7 +101,6 @@ class DjangoTranslation(gettext_module.GNUTranslations): gettext_module.GNUTranslations.__init__(self) if domain is not None: self.domain = domain - self.set_output_charset('utf-8') # For Python 2 gettext() (#25720) self.__language = language self.__to_language = to_language(language) @@ -326,11 +325,7 @@ def do_translate(message, translation_function): def gettext(message): - """ - Returns a string of the translation of the message. - - Returns a string on Python 3 and an UTF-8-encoded bytestring on Python 2. - """ + """Return a string of the translation of the message.""" return do_translate(message, 'gettext') @@ -372,8 +367,6 @@ def ngettext(singular, plural, number): """ Returns a string of the translation of either the singular or plural, based on the number. - - Returns a string on Python 3 and an UTF-8-encoded bytestring on Python 2. """ return do_ntranslate(singular, plural, number, 'ngettext') diff --git a/docs/ref/applications.txt b/docs/ref/applications.txt index 1cff9aafa4..edd88ceec8 100644 --- a/docs/ref/applications.txt +++ b/docs/ref/applications.txt @@ -206,12 +206,12 @@ Read-only attributes .. attribute:: AppConfig.module Root module for the application, e.g. ````. + 'django/contrib/admin/__init__.py'>``. .. attribute:: AppConfig.models_module Module containing the models, e.g. ````. + from 'django/contrib/admin/models.py'>``. It may be ``None`` if the application doesn't contain a ``models`` module. Note that the database related signals such as diff --git a/tests/admin_changelist/tests.py b/tests/admin_changelist/tests.py index 33bf85a9ff..b6060ff9a3 100644 --- a/tests/admin_changelist/tests.py +++ b/tests/admin_changelist/tests.py @@ -568,10 +568,6 @@ class ChangeListTests(TestCase): self.assertNotContains(response, '' % link) def test_tuple_list_display(self): - """ - Regression test for #17128 - (ChangeList failing under Python 2.5 after r16319) - """ swallow = Swallow.objects.create(origin='Africa', load='12.34', speed='22.2') swallow2 = Swallow.objects.create(origin='Africa', load='12.34', speed='22.2') swallow_o2o = SwallowOneToOne.objects.create(swallow=swallow2) diff --git a/tests/admin_scripts/tests.py b/tests/admin_scripts/tests.py index 8494d76f4c..c8f5d2a20d 100644 --- a/tests/admin_scripts/tests.py +++ b/tests/admin_scripts/tests.py @@ -101,9 +101,6 @@ class AdminScriptTestCase(unittest.TestCase): if sys.platform.startswith('java'): # Jython produces module$py.class files os.remove(re.sub(r'\.py$', '$py.class', full_name)) - else: - # CPython produces module.pyc files - os.remove(full_name + 'c') except OSError: pass # Also remove a __pycache__ directory, if it exists diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py index ffce2c0f34..3a5f750309 100644 --- a/tests/admin_views/admin.py +++ b/tests/admin_views/admin.py @@ -971,8 +971,7 @@ site.register(RelatedWithUUIDPKModel) # related ForeignKey object not registered in admin # related OneToOne object registered in admin # related OneToOne object not registered in admin -# when deleting Book so as exercise all four troublesome (w.r.t escaping -# and calling force_text to avoid problems on Python 2.3) paths through +# when deleting Book so as exercise all four paths through # contrib.admin.utils's get_deleted_objects function. site.register(Book, inlines=[ChapterInline]) site.register(Promo) diff --git a/tests/auth_tests/test_context_processors.py b/tests/auth_tests/test_context_processors.py index d66b28cb9c..5b93800386 100644 --- a/tests/auth_tests/test_context_processors.py +++ b/tests/auth_tests/test_context_processors.py @@ -130,21 +130,7 @@ class AuthContextProcessorTests(TestCase): # bug #12037 is tested by the {% url %} in the template: self.assertContains(response, "url: /userpage/super/") - # See if this object can be used for queries where a Q() comparing - # a user can be used with another Q() (in an AND or OR fashion). - # This simulates what a template tag might do with the user from the - # context. Note that we don't need to execute a query, just build it. - # - # The failure case (bug #12049) on Python 2.4 with a LazyObject-wrapped - # User is a fatal TypeError: "function() takes at least 2 arguments - # (0 given)" deep inside deepcopy(). - # - # Python 2.5 and 2.6 succeeded, but logged internally caught exception - # spew: - # - # Exception RuntimeError: 'maximum recursion depth exceeded while - # calling a Python object' in - # ignored" + # A Q() comparing a user and with another Q() (in an AND or OR fashion). Q(user=response.context['user']) & Q(someflag=True) # Tests for user equality. This is hard because User defines diff --git a/tests/cache/tests.py b/tests/cache/tests.py index 5c3ec0fe04..ef77b28458 100644 --- a/tests/cache/tests.py +++ b/tests/cache/tests.py @@ -62,11 +62,6 @@ class Unpicklable: raise pickle.PickleError() -class UnpicklableType: - # Unpicklable using the default pickling protocol on Python 2. - __slots__ = 'a', - - @override_settings(CACHES={ 'default': { 'BACKEND': 'django.core.cache.backends.dummy.DummyCache', @@ -1360,10 +1355,6 @@ class FileBasedCacheTests(BaseCacheTests, TestCase): cache.set('foo', 'bar') os.path.exists(self.dirname) - def test_cache_write_unpicklable_type(self): - # This fails if not using the highest pickling protocol on Python 2. - cache.set('unpicklable', UnpicklableType()) - def test_get_ignores_enoent(self): cache.set('foo', 'bar') os.unlink(cache._key_to_file('foo')) diff --git a/tests/decorators/tests.py b/tests/decorators/tests.py index c2116a05ce..8fedacec22 100644 --- a/tests/decorators/tests.py +++ b/tests/decorators/tests.py @@ -256,8 +256,8 @@ class MethodDecoratorTests(SimpleTestCase): def test_bad_iterable(self): decorators = {myattr_dec_m, myattr2_dec_m} - # The rest of the exception message differs between Python 2 and 3. - with self.assertRaisesMessage(TypeError, "'set' object"): + msg = "'set' object is not subscriptable" + with self.assertRaisesMessage(TypeError, msg): @method_decorator(decorators, "method") class TestIterable: def method(self): diff --git a/tests/expressions_case/tests.py b/tests/expressions_case/tests.py index a662ffbc3b..20d1e801ec 100644 --- a/tests/expressions_case/tests.py +++ b/tests/expressions_case/tests.py @@ -637,8 +637,6 @@ class CaseExpressionTests(TestCase): def test_update_binary(self): CaseTestModel.objects.update( binary=Case( - # fails on postgresql on Python 2.7 if output_field is not - # set explicitly When(integer=1, then=Value(b'one', output_field=models.BinaryField())), When(integer=2, then=Value(b'two', output_field=models.BinaryField())), default=Value(b'', output_field=models.BinaryField()), diff --git a/tests/files/tests.py b/tests/files/tests.py index f7d05574d2..4036fc5e79 100644 --- a/tests/files/tests.py +++ b/tests/files/tests.py @@ -139,9 +139,6 @@ class FileTests(unittest.TestCase): test_file.seek(0) wrapper = TextIOWrapper(test_file, 'utf-8', newline='\n') self.assertEqual(wrapper.read(), content) - # The following seek() call is required on Windows Python 2 when - # switching from reading to writing. - wrapper.seek(0, 2) wrapper.write(content) wrapper.seek(0) self.assertEqual(wrapper.read(), content * 2) diff --git a/tests/forms_tests/tests/test_forms.py b/tests/forms_tests/tests/test_forms.py index 98c5ee8783..c13fbdf5c8 100644 --- a/tests/forms_tests/tests/test_forms.py +++ b/tests/forms_tests/tests/test_forms.py @@ -21,8 +21,7 @@ from django.template import Context, Template from django.test import SimpleTestCase from django.utils.datastructures import MultiValueDict from django.utils.encoding import force_text -from django.utils.html import format_html -from django.utils.safestring import SafeData, mark_safe +from django.utils.safestring import mark_safe class Person(Form): @@ -2028,22 +2027,6 @@ Password: form = PersonForm({}) self.assertEqual(form['name'].value(), 'John Doe') - def test_boundfield_rendering(self): - """ - Python 2 issue: Rendering a BoundField with bytestring content - doesn't lose it's safe string status (#22950). - """ - class CustomWidget(TextInput): - def render(self, name, value, attrs=None, choices=None, - renderer=None, extra_context=None): - return format_html(str(''), ' id=custom') - - class SampleForm(Form): - name = CharField(widget=CustomWidget) - - f = SampleForm(data={'name': 'bar'}) - self.assertIsInstance(force_text(f['name']), SafeData) - def test_custom_boundfield(self): class CustomField(CharField): def get_bound_field(self, form, name): diff --git a/tests/i18n/utils.py b/tests/i18n/utils.py index 2bf4f3e63e..43cb756564 100644 --- a/tests/i18n/utils.py +++ b/tests/i18n/utils.py @@ -7,7 +7,7 @@ source_code_dir = os.path.dirname(__file__) def copytree(src, dst): - shutil.copytree(src, dst, ignore=shutil.ignore_patterns('*.pyc', '__pycache__')) + shutil.copytree(src, dst, ignore=shutil.ignore_patterns('__pycache__')) class POFileAssertionMixin: diff --git a/tests/queries/test_qs_combinators.py b/tests/queries/test_qs_combinators.py index 02a0df216d..0528f1c08d 100644 --- a/tests/queries/test_qs_combinators.py +++ b/tests/queries/test_qs_combinators.py @@ -1,7 +1,6 @@ from django.db.models import F, IntegerField, Value from django.db.utils import DatabaseError from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature -from django.utils.six.moves import range from .models import Number, ReservedName diff --git a/tests/settings_tests/tests.py b/tests/settings_tests/tests.py index bf015affc2..fa51c0af20 100644 --- a/tests/settings_tests/tests.py +++ b/tests/settings_tests/tests.py @@ -305,8 +305,7 @@ class TestComplexSettingOverride(SimpleTestCase): self.assertEqual(settings.TEST_WARN, 'override') self.assertEqual(len(w), 1) - # File extension may by .py, .pyc, etc. Compare only basename. - self.assertEqual(os.path.splitext(w[0].filename)[0], os.path.splitext(__file__)[0]) + self.assertEqual(w[0].filename, __file__) self.assertEqual(str(w[0].message), 'Overriding setting TEST_WARN can lead to unexpected behavior.') diff --git a/tests/template_tests/utils.py b/tests/template_tests/utils.py index e2a9305b5e..3ebacf7bcb 100644 --- a/tests/template_tests/utils.py +++ b/tests/template_tests/utils.py @@ -164,7 +164,7 @@ class SilentAttrClass: class UTF8Class: - "Class whose __str__ returns non-ASCII data on Python 2" + "Class whose __str__ returns non-ASCII data" def __str__(self): return 'ŠĐĆŽćžšđ' diff --git a/tests/utils_tests/test_simplelazyobject.py b/tests/utils_tests/test_simplelazyobject.py index a83c78bf4c..d6386fe79c 100644 --- a/tests/utils_tests/test_simplelazyobject.py +++ b/tests/utils_tests/test_simplelazyobject.py @@ -7,15 +7,10 @@ from django.utils.functional import SimpleLazyObject class TestUtilsSimpleLazyObjectDjangoTestCase(TestCase): - def test_pickle_py2_regression(self): - # See ticket #20212 + def test_pickle(self): user = User.objects.create_user('johndoe', 'john@example.com', 'pass') x = SimpleLazyObject(lambda: user) - - # This would fail with "TypeError: can't pickle instancemethod objects", - # only on Python 2.X. pickle.dumps(x) - # Try the variant protocol levels. pickle.dumps(x, 0) pickle.dumps(x, 1)