Browse Source

Refs #23919 -- Removed misc references to Python 2.

Tim Graham 8 years ago
parent
commit
d170c63351

+ 1 - 8
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.

+ 3 - 4
django/apps/config.py

@@ -17,7 +17,7 @@ class AppConfig:
         self.name = app_name
 
         # Root module for the application eg. <module 'django.contrib.admin'
-        # from 'django/contrib/admin/__init__.pyc'>.
+        # 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. <module 'django.contrib.admin.models'
-        # from 'django/contrib/admin/models.pyc'>. 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
 

+ 6 - 13
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)

+ 0 - 9
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

+ 1 - 1
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

+ 5 - 6
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)

+ 0 - 1
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

+ 0 - 1
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

+ 1 - 1
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,)))

+ 3 - 13
django/db/migrations/serializer.py

@@ -160,24 +160,14 @@ class FunctionTypeSerializer(BaseSerializer):
             if "<" not in self.value.__qualname__:  # Qualname can include <locals>
                 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}
 
 

+ 0 - 6
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)
 

+ 1 - 4
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:

+ 2 - 8
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:

+ 1 - 5
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'"""

+ 1 - 1
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:

+ 1 - 1
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:

+ 1 - 3
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

+ 1 - 3
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)

+ 1 - 8
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')
 

+ 2 - 2
docs/ref/applications.txt

@@ -206,12 +206,12 @@ Read-only attributes
 .. attribute:: AppConfig.module
 
     Root module for the application, e.g. ``<module 'django.contrib.admin' from
-    'django/contrib/admin/__init__.pyc'>``.
+    'django/contrib/admin/__init__.py'>``.
 
 .. attribute:: AppConfig.models_module
 
     Module containing the models, e.g. ``<module 'django.contrib.admin.models'
-    from 'django/contrib/admin/models.pyc'>``.
+    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

+ 0 - 4
tests/admin_changelist/tests.py

@@ -568,10 +568,6 @@ class ChangeListTests(TestCase):
         self.assertNotContains(response, '<a href="%s">' % 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)

+ 0 - 3
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

+ 1 - 2
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)

+ 1 - 15
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 <type 'exceptions.AttributeError'>
-        #    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

+ 0 - 9
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'))

+ 2 - 2
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):

+ 0 - 2
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()),

+ 0 - 3
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)

+ 1 - 18
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: <input type="password" name="password" required /></li>
         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('<input{} />'), ' 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):

+ 1 - 1
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:

+ 0 - 1
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
 

+ 1 - 2
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.')
 
 

+ 1 - 1
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 'ŠĐĆŽćžšđ'
 

+ 1 - 6
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)