diff --git a/django/contrib/admindocs/views.py b/django/contrib/admindocs/views.py index 3488e9fb3a..4a8e1eb599 100644 --- a/django/contrib/admindocs/views.py +++ b/django/contrib/admindocs/views.py @@ -15,7 +15,6 @@ from django.db import models from django.http import Http404 from django.template.engine import Engine from django.urls import get_mod_func, get_resolver, get_urlconf, reverse -from django.utils import six from django.utils.decorators import method_decorator from django.utils.inspect import ( func_accepts_kwargs, func_accepts_var_args, func_has_no_args, @@ -132,12 +131,7 @@ class ViewIndexView(BaseAdminDocsView): @staticmethod def _get_full_name(func): mod_name = func.__module__ - if six.PY3: - return '%s.%s' % (mod_name, func.__qualname__) - else: - # PY2 does not support __qualname__ - func_name = getattr(func, '__name__', func.__class__.__name__) - return '%s.%s' % (mod_name, func_name) + return '%s.%s' % (mod_name, func.__qualname__) def get_context_data(self, **kwargs): views = [] diff --git a/django/contrib/auth/management/__init__.py b/django/contrib/auth/management/__init__.py index 17fd8dd11a..0d3fca03eb 100644 --- a/django/contrib/auth/management/__init__.py +++ b/django/contrib/auth/management/__init__.py @@ -8,8 +8,6 @@ from django.apps import apps as global_apps from django.contrib.auth import get_permission_codename from django.core import exceptions from django.db import DEFAULT_DB_ALIAS, router -from django.utils import six -from django.utils.encoding import DEFAULT_LOCALE_ENCODING def _get_all_permissions(opts): @@ -98,12 +96,6 @@ def get_system_username(): # if there is no corresponding entry in the /etc/passwd file # (a very restricted chroot environment, for example). return '' - if six.PY2: - try: - result = result.decode(DEFAULT_LOCALE_ENCODING) - except UnicodeDecodeError: - # UnicodeDecodeError - preventive treatment for non-latin Windows. - return '' return result diff --git a/django/contrib/auth/migrations/0001_initial.py b/django/contrib/auth/migrations/0001_initial.py index f97caae1e4..5ab80b64e0 100644 --- a/django/contrib/auth/migrations/0001_initial.py +++ b/django/contrib/auth/migrations/0001_initial.py @@ -1,7 +1,7 @@ import django.contrib.auth.models from django.contrib.auth import validators from django.db import migrations, models -from django.utils import six, timezone +from django.utils import timezone class Migration(migrations.Migration): @@ -63,9 +63,7 @@ class Migration(migrations.Migration): ('username', models.CharField( help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', unique=True, max_length=30, verbose_name='username', - validators=[ - validators.UnicodeUsernameValidator() if six.PY3 else validators.ASCIIUsernameValidator() - ], + validators=[validators.UnicodeUsernameValidator()], )), ('first_name', models.CharField(max_length=30, verbose_name='first name', blank=True)), ('last_name', models.CharField(max_length=30, verbose_name='last name', blank=True)), diff --git a/django/contrib/auth/migrations/0004_alter_user_username_opts.py b/django/contrib/auth/migrations/0004_alter_user_username_opts.py index f4afcf7bd8..a16083ee37 100644 --- a/django/contrib/auth/migrations/0004_alter_user_username_opts.py +++ b/django/contrib/auth/migrations/0004_alter_user_username_opts.py @@ -1,6 +1,5 @@ from django.contrib.auth import validators from django.db import migrations, models -from django.utils import six class Migration(migrations.Migration): @@ -16,7 +15,7 @@ class Migration(migrations.Migration): name='username', field=models.CharField( error_messages={'unique': 'A user with that username already exists.'}, max_length=30, - validators=[validators.UnicodeUsernameValidator() if six.PY3 else validators.ASCIIUsernameValidator()], + validators=[validators.UnicodeUsernameValidator()], help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', unique=True, verbose_name='username' ), diff --git a/django/contrib/auth/migrations/0007_alter_validators_add_error_messages.py b/django/contrib/auth/migrations/0007_alter_validators_add_error_messages.py index 6d44280856..42f5087730 100644 --- a/django/contrib/auth/migrations/0007_alter_validators_add_error_messages.py +++ b/django/contrib/auth/migrations/0007_alter_validators_add_error_messages.py @@ -1,6 +1,5 @@ from django.contrib.auth import validators from django.db import migrations, models -from django.utils import six class Migration(migrations.Migration): @@ -18,7 +17,7 @@ class Migration(migrations.Migration): help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=30, unique=True, - validators=[validators.UnicodeUsernameValidator() if six.PY3 else validators.ASCIIUsernameValidator()], + validators=[validators.UnicodeUsernameValidator()], verbose_name='username', ), ), diff --git a/django/contrib/auth/migrations/0008_alter_user_username_max_length.py b/django/contrib/auth/migrations/0008_alter_user_username_max_length.py index 15b9cf8c94..7c9dae0950 100644 --- a/django/contrib/auth/migrations/0008_alter_user_username_max_length.py +++ b/django/contrib/auth/migrations/0008_alter_user_username_max_length.py @@ -1,6 +1,5 @@ from django.contrib.auth import validators from django.db import migrations, models -from django.utils import six class Migration(migrations.Migration): @@ -18,7 +17,7 @@ class Migration(migrations.Migration): help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, - validators=[validators.UnicodeUsernameValidator() if six.PY3 else validators.ASCIIUsernameValidator()], + validators=[validators.UnicodeUsernameValidator()], verbose_name='username', ), ), diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py index 51b0d8fa63..7155bd1199 100644 --- a/django/contrib/auth/models.py +++ b/django/contrib/auth/models.py @@ -9,7 +9,7 @@ from django.db.models.manager import EmptyManager from django.utils import six, timezone from django.utils.translation import ugettext_lazy as _ -from .validators import ASCIIUsernameValidator, UnicodeUsernameValidator +from .validators import UnicodeUsernameValidator def update_last_login(sender, user, **kwargs): @@ -297,7 +297,7 @@ class AbstractUser(AbstractBaseUser, PermissionsMixin): Username and password are required. Other fields are optional. """ - username_validator = UnicodeUsernameValidator() if six.PY3 else ASCIIUsernameValidator() + username_validator = UnicodeUsernameValidator() username = models.CharField( _('username'), diff --git a/django/contrib/auth/validators.py b/django/contrib/auth/validators.py index 0691d22a88..d8083de809 100644 --- a/django/contrib/auth/validators.py +++ b/django/contrib/auth/validators.py @@ -1,7 +1,6 @@ import re from django.core import validators -from django.utils import six from django.utils.deconstruct import deconstructible from django.utils.translation import ugettext_lazy as _ @@ -13,7 +12,7 @@ class ASCIIUsernameValidator(validators.RegexValidator): 'Enter a valid username. This value may contain only English letters, ' 'numbers, and @/./+/-/_ characters.' ) - flags = re.ASCII if six.PY3 else 0 + flags = re.ASCII @deconstructible @@ -23,4 +22,4 @@ class UnicodeUsernameValidator(validators.RegexValidator): 'Enter a valid username. This value may contain only letters, ' 'numbers, and @/./+/-/_ characters.' ) - flags = re.UNICODE if six.PY2 else 0 + flags = 0 diff --git a/django/contrib/gis/utils/ogrinspect.py b/django/contrib/gis/utils/ogrinspect.py index 3cf9b10fd5..71e4c64878 100644 --- a/django/contrib/gis/utils/ogrinspect.py +++ b/django/contrib/gis/utils/ogrinspect.py @@ -235,5 +235,4 @@ def _ogrinspect(data_source, model_name, geom_name='geom', layer_key=0, srid=Non if name_field: yield '' - yield ' def __%s__(self): return self.%s' % ( - 'str' if six.PY3 else 'unicode', name_field) + yield ' def __str__(self): return self.%s' % name_field diff --git a/django/contrib/postgres/signals.py b/django/contrib/postgres/signals.py index 183ba1d983..682627de1d 100644 --- a/django/contrib/postgres/signals.py +++ b/django/contrib/postgres/signals.py @@ -1,18 +1,13 @@ from psycopg2 import ProgrammingError from psycopg2.extras import register_hstore -from django.utils import six - def register_hstore_handler(connection, **kwargs): if connection.vendor != 'postgresql': return try: - if six.PY2: - register_hstore(connection.connection, globally=True, unicode=True) - else: - register_hstore(connection.connection, globally=True) + register_hstore(connection.connection, globally=True) except ProgrammingError: # Hstore is not available on the database. # diff --git a/django/core/cache/backends/db.py b/django/core/cache/backends/db.py index ce48018d91..ad805324bd 100644 --- a/django/core/cache/backends/db.py +++ b/django/core/cache/backends/db.py @@ -5,7 +5,7 @@ from datetime import datetime from django.conf import settings from django.core.cache.backends.base import DEFAULT_TIMEOUT, BaseCache from django.db import DatabaseError, connections, models, router, transaction -from django.utils import six, timezone +from django.utils import timezone from django.utils.encoding import force_bytes try: @@ -112,11 +112,9 @@ class DatabaseCache(BaseDatabaseCache): if num > self._max_entries: self._cull(db, cursor, now) pickled = pickle.dumps(value, pickle.HIGHEST_PROTOCOL) - b64encoded = base64.b64encode(pickled) # The DB column is expecting a string, so make sure the value is a # string, not bytes. Refs #19274. - if six.PY3: - b64encoded = b64encoded.decode('latin1') + b64encoded = base64.b64encode(pickled).decode('latin1') try: # Note: typecasting for datetimes is needed by some 3rd party # database backends. All core backends work without typecasting, diff --git a/django/core/exceptions.py b/django/core/exceptions.py index 47c5359c75..3bf52fab00 100644 --- a/django/core/exceptions.py +++ b/django/core/exceptions.py @@ -1,7 +1,6 @@ """ Global Django exception and warning classes. """ -from django.utils import six from django.utils.encoding import force_text @@ -115,10 +114,7 @@ class ValidationError(Exception): if isinstance(message, ValidationError): if hasattr(message, 'error_dict'): message = message.error_dict - # PY2 has a `message` property which is always there so we can't - # duck-type on it. It was introduced in Python 2.5 and already - # deprecated in Python 2.6. - elif not hasattr(message, 'message' if six.PY3 else 'code'): + elif not hasattr(message, 'message'): message = message.error_list else: message, code, params = message.message, message.code, message.params diff --git a/django/core/files/base.py b/django/core/files/base.py index f797158881..dd07bc959a 100644 --- a/django/core/files/base.py +++ b/django/core/files/base.py @@ -3,7 +3,7 @@ from io import BytesIO, StringIO, UnsupportedOperation from django.core.files.utils import FileProxyMixin from django.utils import six -from django.utils.encoding import force_bytes, force_str, force_text +from django.utils.encoding import force_str, force_text class File(FileProxyMixin): @@ -140,11 +140,7 @@ class ContentFile(File): A File-like object that takes just raw content, rather than an actual file. """ def __init__(self, content, name=None): - if six.PY3: - stream_class = StringIO if isinstance(content, six.text_type) else BytesIO - else: - stream_class = BytesIO - content = force_bytes(content) + stream_class = StringIO if isinstance(content, six.text_type) else BytesIO super(ContentFile, self).__init__(stream_class(content), name=name) self.size = len(content) diff --git a/django/core/handlers/wsgi.py b/django/core/handlers/wsgi.py index 538373cd8d..f4d3e6553a 100644 --- a/django/core/handlers/wsgi.py +++ b/django/core/handlers/wsgi.py @@ -8,7 +8,6 @@ from django.conf import settings from django.core import signals from django.core.handlers import base from django.urls import set_script_prefix -from django.utils import six from django.utils.encoding import ( force_str, force_text, repercent_broken_unicode, ) @@ -212,22 +211,20 @@ def get_bytes_from_wsgi(environ, key, default): """ Get a value from the WSGI environ dictionary as bytes. - key and default should be str objects. Under Python 2 they may also be - unicode objects provided they only contain ASCII characters. + key and default should be str objects. """ value = environ.get(str(key), str(default)) - # Under Python 3, non-ASCII values in the WSGI environ are arbitrarily - # decoded with ISO-8859-1. This is wrong for Django websites where UTF-8 - # is the default. Re-encode to recover the original bytestring. - return value.encode(ISO_8859_1) if six.PY3 else value + # Non-ASCII values in the WSGI environ are arbitrarily decoded with + # ISO-8859-1. This is wrong for Django websites where UTF-8 is the default. + # Re-encode to recover the original bytestring. + return value.encode(ISO_8859_1) def get_str_from_wsgi(environ, key, default): """ Get a value from the WSGI environ dictionary as str. - key and default should be str objects. Under Python 2 they may also be - unicode objects provided they only contain ASCII characters. + key and default should be str objects. """ value = get_bytes_from_wsgi(environ, key, default) - return value.decode(UTF_8, errors='replace') if six.PY3 else value + return value.decode(UTF_8, errors='replace') diff --git a/django/core/mail/backends/console.py b/django/core/mail/backends/console.py index 0ce88534ea..49ddade984 100644 --- a/django/core/mail/backends/console.py +++ b/django/core/mail/backends/console.py @@ -5,7 +5,6 @@ import sys import threading from django.core.mail.backends.base import BaseEmailBackend -from django.utils import six class EmailBackend(BaseEmailBackend): @@ -17,9 +16,8 @@ class EmailBackend(BaseEmailBackend): def write_message(self, message): msg = message.message() msg_data = msg.as_bytes() - if six.PY3: - charset = msg.get_charset().get_output_charset() if msg.get_charset() else 'utf-8' - msg_data = msg_data.decode(charset) + charset = msg.get_charset().get_output_charset() if msg.get_charset() else 'utf-8' + msg_data = msg_data.decode(charset) self.stream.write('%s\n' % msg_data) self.stream.write('-' * 79) self.stream.write('\n') diff --git a/django/core/mail/message.py b/django/core/mail/message.py index 2c68e82809..c4df44fa9d 100644 --- a/django/core/mail/message.py +++ b/django/core/mail/message.py @@ -5,7 +5,9 @@ import time from email import ( charset as Charset, encoders as Encoders, generator, message_from_string, ) +from email.errors import InvalidHeaderDefect, NonASCIILocalPartDefect from email.header import Header +from email.headerregistry import Address from email.message import Message from email.mime.base import MIMEBase from email.mime.message import MIMEMessage @@ -139,18 +141,8 @@ def sanitize_address(addr, encoding): except UnicodeEncodeError: # IDN or non-ascii in the local part localpart, domain = split_addr(addr, encoding) - if six.PY2: - # On Python 2, use the stdlib since `email.headerregistry` doesn't exist. - from email.utils import formataddr - if localpart and domain: - addr = '@'.join([localpart, domain]) - return formataddr((nm, addr)) - - # On Python 3, an `email.headerregistry.Address` object is used since + # An `email.headerregistry.Address` object is used since # email.utils.formataddr() naively encodes the name as ascii (see #25986). - from email.headerregistry import Address - from email.errors import InvalidHeaderDefect, NonASCIILocalPartDefect - if localpart and domain: address = Address(nm, username=localpart, domain=domain) return str(address) @@ -174,27 +166,21 @@ class MIMEMixin(): """ fp = six.StringIO() g = generator.Generator(fp, mangle_from_=False) - if six.PY2: - g.flatten(self, unixfrom=unixfrom) - else: - g.flatten(self, unixfrom=unixfrom, linesep=linesep) + g.flatten(self, unixfrom=unixfrom, linesep=linesep) return fp.getvalue() - if six.PY2: - as_bytes = as_string - else: - def as_bytes(self, unixfrom=False, linesep='\n'): - """Return the entire formatted message as bytes. - Optional `unixfrom' when True, means include the Unix From_ envelope - header. + def as_bytes(self, unixfrom=False, linesep='\n'): + """Return the entire formatted message as bytes. + Optional `unixfrom' when True, means include the Unix From_ envelope + header. - This overrides the default as_bytes() implementation to not mangle - lines that begin with 'From '. See bug #13433 for details. - """ - fp = BytesIO() - g = generator.BytesGenerator(fp, mangle_from_=False) - g.flatten(self, unixfrom=unixfrom, linesep=linesep) - return fp.getvalue() + This overrides the default as_bytes() implementation to not mangle + lines that begin with 'From '. See bug #13433 for details. + """ + fp = BytesIO() + g = generator.BytesGenerator(fp, mangle_from_=False) + g.flatten(self, unixfrom=unixfrom, linesep=linesep) + return fp.getvalue() class SafeMIMEMessage(MIMEMixin, MIMEMessage): @@ -450,8 +436,6 @@ class EmailMessage(object): try: filename.encode('ascii') except UnicodeEncodeError: - if six.PY2: - filename = filename.encode('utf-8') filename = ('utf-8', '', filename) attachment.add_header('Content-Disposition', 'attachment', filename=filename) diff --git a/django/core/management/commands/loaddata.py b/django/core/management/commands/loaddata.py index 4b621205e6..9f71400862 100644 --- a/django/core/management/commands/loaddata.py +++ b/django/core/management/commands/loaddata.py @@ -20,7 +20,6 @@ from django.utils import lru_cache from django.utils._os import upath from django.utils.encoding import force_text from django.utils.functional import cached_property -from django.utils.glob import glob_escape try: import bz2 @@ -238,7 +237,7 @@ class Command(BaseCommand): self.stdout.write("Checking %s for fixtures..." % humanize(fixture_dir)) fixture_files_in_dir = [] path = os.path.join(fixture_dir, fixture_name) - for candidate in glob.iglob(glob_escape(path) + '*'): + for candidate in glob.iglob(glob.escape(path) + '*'): if os.path.basename(candidate) in targets: # Save the fixture_dir and fixture_name for future error messages. fixture_files_in_dir.append((candidate, fixture_dir, fixture_name)) diff --git a/django/core/management/commands/runserver.py b/django/core/management/commands/runserver.py index 62370072c5..e5dcaa1771 100644 --- a/django/core/management/commands/runserver.py +++ b/django/core/management/commands/runserver.py @@ -10,8 +10,8 @@ from django.core.management.base import BaseCommand, CommandError from django.core.servers.basehttp import ( WSGIServer, get_internal_wsgi_application, run, ) -from django.utils import autoreload, six -from django.utils.encoding import force_text, get_system_encoding +from django.utils import autoreload +from django.utils.encoding import force_text naiveip_re = re.compile(r"""^(?: @@ -125,8 +125,6 @@ class Command(BaseCommand): # requires_migrations_check attribute. self.check_migrations() now = datetime.now().strftime('%B %d, %Y - %X') - if six.PY2: - now = now.decode(get_system_encoding()) self.stdout.write(now) self.stdout.write(( "Django version %(version)s, using settings %(settings)r\n" diff --git a/django/core/management/templates.py b/django/core/management/templates.py index 805da99148..775e75201b 100644 --- a/django/core/management/templates.py +++ b/django/core/management/templates.py @@ -16,7 +16,7 @@ from django.conf import settings from django.core.management.base import BaseCommand, CommandError from django.core.management.utils import handle_extensions from django.template import Context, Engine -from django.utils import archive, six +from django.utils import archive from django.utils.six.moves.urllib.request import urlretrieve from django.utils.version import get_docs_version @@ -218,21 +218,11 @@ class TemplateCommand(BaseCommand): raise CommandError("you must provide %s %s name" % ( "an" if app_or_project == "app" else "a", app_or_project)) # If it's not a valid directory name. - if six.PY2: - if not re.search(r'^[_a-zA-Z]\w*$', name): - # Provide a smart error message, depending on the error. - if not re.search(r'^[_a-zA-Z]', name): - message = 'make sure the name begins with a letter or underscore' - else: - message = 'use only numbers, letters and underscores' - raise CommandError("%r is not a valid %s name. Please %s." % - (name, app_or_project, message)) - else: - if not name.isidentifier(): - raise CommandError( - "%r is not a valid %s name. Please make sure the name is " - "a valid identifier." % (name, app_or_project) - ) + if not name.isidentifier(): + raise CommandError( + "%r is not a valid %s name. Please make sure the name is " + "a valid identifier." % (name, app_or_project) + ) def download(self, url): """ diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index 3d6cb71915..495ca33221 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -219,8 +219,6 @@ class DatabaseWrapper(BaseDatabaseWrapper): 'conv': django_conversions, 'charset': 'utf8', } - if six.PY2: - kwargs['use_unicode'] = True settings_dict = self.settings_dict if settings_dict['USER']: kwargs['user'] = settings_dict['USER'] diff --git a/django/db/backends/oracle/operations.py b/django/db/backends/oracle/operations.py index fd9d2d1179..9878fbd9bb 100644 --- a/django/db/backends/oracle/operations.py +++ b/django/db/backends/oracle/operations.py @@ -252,8 +252,6 @@ WHEN (new.%(col_name)s IS NULL) # https://cx-oracle.readthedocs.io/en/latest/cursor.html#Cursor.statement # The DB API definition does not define this attribute. statement = cursor.statement - if statement and six.PY2 and not isinstance(statement, unicode): # NOQA: unicode undefined on PY3 - statement = statement.decode('utf-8') # Unlike Psycopg's `query` and MySQLdb`'s `_last_executed`, CxOracle's # `statement` doesn't contain the query parameters. refs #20010. return super(DatabaseOperations, self).last_executed_query(cursor, statement, params) diff --git a/django/db/backends/postgresql/base.py b/django/db/backends/postgresql/base.py index 85a21a8905..10b54faa20 100644 --- a/django/db/backends/postgresql/base.py +++ b/django/db/backends/postgresql/base.py @@ -12,7 +12,6 @@ from django.core.exceptions import ImproperlyConfigured from django.db import DEFAULT_DB_ALIAS from django.db.backends.base.base import BaseDatabaseWrapper from django.db.utils import DatabaseError as WrappedDatabaseError -from django.utils import six from django.utils.encoding import force_str from django.utils.functional import cached_property from django.utils.safestring import SafeBytes, SafeText @@ -46,9 +45,6 @@ from .schema import DatabaseSchemaEditor # NOQA isort:skip from .utils import utc_tzinfo_factory # NOQA isort:skip from .version import get_version # NOQA isort:skip -if six.PY2: - psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) - psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY) psycopg2.extensions.register_adapter(SafeBytes, psycopg2.extensions.QuotedString) psycopg2.extensions.register_adapter(SafeText, psycopg2.extensions.QuotedString) psycopg2.extras.register_uuid() diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py index 0cd0516a0a..de3aa40000 100644 --- a/django/db/backends/sqlite3/base.py +++ b/django/db/backends/sqlite3/base.py @@ -19,7 +19,6 @@ from django.utils.dateparse import ( parse_date, parse_datetime, parse_duration, parse_time, ) from django.utils.encoding import force_text -from django.utils.safestring import SafeBytes try: try: @@ -55,9 +54,6 @@ Database.register_converter(str("TIMESTAMP"), decoder(parse_datetime)) Database.register_converter(str("decimal"), decoder(backend_utils.typecast_decimal)) Database.register_adapter(decimal.Decimal, backend_utils.rev_typecast_decimal) -if six.PY2: - Database.register_adapter(str, lambda s: s.decode('utf-8')) - Database.register_adapter(SafeBytes, lambda s: s.decode('utf-8')) class DatabaseWrapper(BaseDatabaseWrapper): diff --git a/django/db/backends/sqlite3/features.py b/django/db/backends/sqlite3/features.py index 43a29b78f5..5bf1997b4c 100644 --- a/django/db/backends/sqlite3/features.py +++ b/django/db/backends/sqlite3/features.py @@ -1,6 +1,5 @@ from django.db import utils from django.db.backends.base.features import BaseDatabaseFeatures -from django.utils import six from django.utils.functional import cached_property from .base import Database @@ -48,7 +47,6 @@ class DatabaseFeatures(BaseDatabaseFeatures): @cached_property def can_share_in_memory_db(self): return ( - six.PY3 and Database.__name__ == 'sqlite3.dbapi2' and Database.sqlite_version_info >= (3, 7, 13) ) diff --git a/django/db/migrations/questioner.py b/django/db/migrations/questioner.py index d508ee9b78..17b9da8eaf 100644 --- a/django/db/migrations/questioner.py +++ b/django/db/migrations/questioner.py @@ -4,7 +4,7 @@ import sys from django.apps import apps from django.db.models.fields import NOT_PROVIDED -from django.utils import datetime_safe, six, timezone +from django.utils import datetime_safe, timezone from django.utils.six.moves import input from .loader import MigrationLoader @@ -125,13 +125,7 @@ class InteractiveMigrationQuestioner(MigrationQuestioner): prompt = "[default: {}] >>> ".format(default) else: prompt = ">>> " - if six.PY3: - # Six does not correctly abstract over the fact that - # py3 input returns a unicode string, while py2 raw_input - # returns a bytestring. - code = input(prompt) - else: - code = input(prompt).decode(sys.stdin.encoding) + code = input(prompt) if not code and default: code = default if not code: diff --git a/django/db/migrations/serializer.py b/django/db/migrations/serializer.py index b273097da4..261fcabd73 100644 --- a/django/db/migrations/serializer.py +++ b/django/db/migrations/serializer.py @@ -53,11 +53,7 @@ class BaseSimpleSerializer(BaseSerializer): class ByteTypeSerializer(BaseSerializer): def serialize(self): - value_repr = repr(self.value) - if six.PY2: - # Prepend the `b` prefix since we're importing unicode_literals - value_repr = 'b' + value_repr - return value_repr, set() + return repr(self.value), set() class DatetimeSerializer(BaseSerializer): @@ -276,11 +272,7 @@ class SettingsReferenceSerializer(BaseSerializer): class TextTypeSerializer(BaseSerializer): def serialize(self): - value_repr = repr(self.value) - if six.PY2: - # Strip the `u` prefix since we're importing unicode_literals - value_repr = value_repr[1:] - return value_repr, set() + return repr(self.value), set() class TimedeltaSerializer(BaseSerializer): diff --git a/django/db/models/base.py b/django/db/models/base.py index e18615d76d..38d0b96217 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -510,9 +510,7 @@ class Model(six.with_metaclass(ModelBase)): return force_str('<%s: %s>' % (self.__class__.__name__, u)) def __str__(self): - if six.PY2 and hasattr(self, '__unicode__'): - return force_text(self).encode('utf-8') - return str('%s object' % self.__class__.__name__) + return '%s object' % self.__class__.__name__ def __eq__(self, other): if not isinstance(other, Model): diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index aec362e84a..7f7a6d3ea8 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -107,7 +107,6 @@ class RelatedField(Field): return errors def _check_related_name_is_valid(self): - import re import keyword related_name = self.remote_field.related_name if related_name is None: @@ -115,12 +114,8 @@ class RelatedField(Field): is_valid_id = True if keyword.iskeyword(related_name): is_valid_id = False - if six.PY3: - if not related_name.isidentifier(): - is_valid_id = False - else: - if not re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*\Z', related_name): - is_valid_id = False + if not related_name.isidentifier(): + is_valid_id = False if not (is_valid_id or related_name.endswith('+')): return [ checks.Error( diff --git a/django/db/models/manager.py b/django/db/models/manager.py index 5b498d631b..96c82e54eb 100644 --- a/django/db/models/manager.py +++ b/django/db/models/manager.py @@ -4,7 +4,6 @@ from importlib import import_module from django.db import router from django.db.models.query import QuerySet -from django.utils import six class BaseManager(object): @@ -86,9 +85,7 @@ class BaseManager(object): return manager_method new_methods = {} - # Refs http://bugs.python.org/issue1785. - predicate = inspect.isfunction if six.PY3 else inspect.ismethod - for name, method in inspect.getmembers(queryset_class, predicate=predicate): + for name, method in inspect.getmembers(queryset_class, predicate=inspect.isfunction): # Only copy missing methods. if hasattr(cls, name): continue diff --git a/django/db/utils.py b/django/db/utils.py index 92354b7bad..4451f5a60e 100644 --- a/django/db/utils.py +++ b/django/db/utils.py @@ -14,7 +14,7 @@ DEFAULT_DB_ALIAS = 'default' DJANGO_VERSION_PICKLE_KEY = '_django_version' -class Error(Exception if six.PY3 else StandardError): # NOQA: StandardError undefined on PY3 +class Error(Exception): pass diff --git a/django/dispatch/dispatcher.py b/django/dispatch/dispatcher.py index db2f6839f8..8cf1680b6e 100644 --- a/django/dispatch/dispatcher.py +++ b/django/dispatch/dispatcher.py @@ -2,15 +2,9 @@ import sys import threading import weakref -from django.utils import six from django.utils.inspect import func_accepts_kwargs from django.utils.six.moves import range -if six.PY2: - from .weakref_backports import WeakMethod -else: - from weakref import WeakMethod - def _make_id(target): if hasattr(target, '__func__'): @@ -107,13 +101,10 @@ class Signal(object): receiver_object = receiver # Check for bound methods if hasattr(receiver, '__self__') and hasattr(receiver, '__func__'): - ref = WeakMethod + ref = weakref.WeakMethod receiver_object = receiver.__self__ - if six.PY3: - receiver = ref(receiver) - weakref.finalize(receiver_object, self._remove_receiver) - else: - receiver = ref(receiver, self._remove_receiver) + receiver = ref(receiver) + weakref.finalize(receiver_object, self._remove_receiver) with self.lock: self._clear_dead_receivers() diff --git a/django/dispatch/weakref_backports.py b/django/dispatch/weakref_backports.py deleted file mode 100644 index 736f7e1ab8..0000000000 --- a/django/dispatch/weakref_backports.py +++ /dev/null @@ -1,67 +0,0 @@ -""" -weakref_backports is a partial backport of the weakref module for python -versions below 3.4. - -Copyright (C) 2013 Python Software Foundation, see LICENSE.python for details. - -The following changes were made to the original sources during backporting: - - * Added `self` to `super` calls. - * Removed `from None` when raising exceptions. - -""" -from weakref import ref - - -class WeakMethod(ref): - """ - A custom `weakref.ref` subclass which simulates a weak reference to - a bound method, working around the lifetime problem of bound methods. - """ - - __slots__ = "_func_ref", "_meth_type", "_alive", "__weakref__" - - def __new__(cls, meth, callback=None): - try: - obj = meth.__self__ - func = meth.__func__ - except AttributeError: - raise TypeError("argument should be a bound method, not {}" - .format(type(meth))) - def _cb(arg): - # The self-weakref trick is needed to avoid creating a reference - # cycle. - self = self_wr() - if self._alive: - self._alive = False - if callback is not None: - callback(self) - self = ref.__new__(cls, obj, _cb) - self._func_ref = ref(func, _cb) - self._meth_type = type(meth) - self._alive = True - self_wr = ref(self) - return self - - def __call__(self): - obj = super(WeakMethod, self).__call__() - func = self._func_ref() - if obj is None or func is None: - return None - return self._meth_type(func, obj) - - def __eq__(self, other): - if isinstance(other, WeakMethod): - if not self._alive or not other._alive: - return self is other - return ref.__eq__(self, other) and self._func_ref == other._func_ref - return False - - def __ne__(self, other): - if isinstance(other, WeakMethod): - if not self._alive or not other._alive: - return self is not other - return ref.__ne__(self, other) or self._func_ref != other._func_ref - return True - - __hash__ = ref.__hash__ diff --git a/django/http/cookie.py b/django/http/cookie.py index 49048787eb..7251d04c40 100644 --- a/django/http/cookie.py +++ b/django/http/cookie.py @@ -1,12 +1,7 @@ import sys -from django.utils import six -from django.utils.encoding import force_str from django.utils.six.moves import http_cookies -# http://bugs.python.org/issue2193 is fixed in Python 3.3+. -_cookie_allows_colon_in_names = six.PY3 - # Cookie pickling bug is fixed in Python 2.7.9 and Python 3.4.3+ # http://bugs.python.org/issue22775 cookie_pickles_properly = ( @@ -14,7 +9,7 @@ cookie_pickles_properly = ( sys.version_info >= (3, 4, 3) ) -if _cookie_allows_colon_in_names and cookie_pickles_properly: +if cookie_pickles_properly: SimpleCookie = http_cookies.SimpleCookie else: Morsel = http_cookies.Morsel @@ -30,37 +25,12 @@ else: else: super(SimpleCookie, self).__setitem__(key, value) - if not _cookie_allows_colon_in_names: - def load(self, rawdata): - self.bad_cookies = set() - if isinstance(rawdata, six.text_type): - rawdata = force_str(rawdata) - super(SimpleCookie, self).load(rawdata) - for key in self.bad_cookies: - del self[key] - - # override private __set() method: - # (needed for using our Morsel, and for laxness with CookieError - def _BaseCookie__set(self, key, real_value, coded_value): - key = force_str(key) - try: - M = self.get(key, Morsel()) - M.set(key, real_value, coded_value) - dict.__setitem__(self, key, M) - except http_cookies.CookieError: - if not hasattr(self, 'bad_cookies'): - self.bad_cookies = set() - self.bad_cookies.add(key) - dict.__setitem__(self, key, http_cookies.Morsel()) - def parse_cookie(cookie): """ Return a dictionary parsed from a `Cookie:` header string. """ cookiedict = {} - if six.PY2: - cookie = force_str(cookie) for chunk in cookie.split(str(';')): if str('=') in chunk: key, val = chunk.split(str('='), 1) diff --git a/django/http/multipartparser.py b/django/http/multipartparser.py index ecfbdf7252..812e83eff0 100644 --- a/django/http/multipartparser.py +++ b/django/http/multipartparser.py @@ -40,8 +40,6 @@ RAW = "raw" FILE = "file" FIELD = "field" -_BASE64_DECODE_ERROR = TypeError if six.PY2 else binascii.Error - class MultiPartParser(object): """ @@ -190,7 +188,7 @@ class MultiPartParser(object): num_bytes_read += len(raw_data) try: data = base64.b64decode(raw_data) - except _BASE64_DECODE_ERROR: + except binascii.Error: data = raw_data else: data = field_stream.read(size=read_size) @@ -684,10 +682,7 @@ def parse_header(line): value = p[i + 1:].strip() if has_encoding: encoding, lang, value = value.split(b"'") - if six.PY3: - value = unquote(value.decode(), encoding=encoding.decode()) - else: - value = unquote(value).decode(encoding) + value = unquote(value.decode(), encoding=encoding.decode()) if len(value) >= 2 and value[:1] == value[-1:] == b'"': value = value[1:-1] value = value.replace(b'\\\\', b'\\').replace(b'\\"', b'"') diff --git a/django/http/request.py b/django/http/request.py index 559081b50c..b4053142ac 100644 --- a/django/http/request.py +++ b/django/http/request.py @@ -14,7 +14,7 @@ from django.http.multipartparser import MultiPartParser, MultiPartParserError from django.utils import six from django.utils.datastructures import ImmutableList, MultiValueDict from django.utils.encoding import ( - escape_uri_path, force_bytes, force_str, force_text, iri_to_uri, + escape_uri_path, force_bytes, force_str, iri_to_uri, ) from django.utils.http import is_same_domain, limited_parse_qsl from django.utils.six.moves.urllib.parse import ( @@ -381,24 +381,15 @@ class QueryDict(MultiValueDict): 'fields_limit': settings.DATA_UPLOAD_MAX_NUMBER_FIELDS, 'encoding': encoding, } - if six.PY3: - if isinstance(query_string, bytes): - # query_string normally contains URL-encoded data, a subset of ASCII. - try: - query_string = query_string.decode(encoding) - except UnicodeDecodeError: - # ... but some user agents are misbehaving :-( - query_string = query_string.decode('iso-8859-1') - for key, value in limited_parse_qsl(query_string, **parse_qsl_kwargs): - self.appendlist(key, value) - else: - for key, value in limited_parse_qsl(query_string, **parse_qsl_kwargs): - try: - value = value.decode(encoding) - except UnicodeDecodeError: - value = value.decode('iso-8859-1') - self.appendlist(force_text(key, encoding, errors='replace'), - value) + if isinstance(query_string, bytes): + # query_string normally contains URL-encoded data, a subset of ASCII. + try: + query_string = query_string.decode(encoding) + except UnicodeDecodeError: + # ... but some user agents are misbehaving :-( + query_string = query_string.decode('iso-8859-1') + for key, value in limited_parse_qsl(query_string, **parse_qsl_kwargs): + self.appendlist(key, value) self._mutable = mutable @classmethod diff --git a/django/http/response.py b/django/http/response.py index 597d688602..e4cce4fdbe 100644 --- a/django/http/response.py +++ b/django/http/response.py @@ -100,10 +100,7 @@ class HttpResponseBase(six.Iterator): ] return b'\r\n'.join(headers) - if six.PY3: - __bytes__ = serialize_headers - else: - __str__ = serialize_headers + __bytes__ = serialize_headers @property def _content_type_for_repr(self): @@ -122,20 +119,12 @@ class HttpResponseBase(six.Iterator): isinstance(value, six.text_type) and ('\n' in value or '\r' in value)): raise BadHeaderError("Header values can't contain newlines (got %r)" % value) try: - if six.PY3: - if isinstance(value, str): - # Ensure string is valid in given charset - value.encode(charset) - else: - # Convert bytestring using given charset - value = value.decode(charset) + if isinstance(value, str): + # Ensure string is valid in given charset + value.encode(charset) else: - if isinstance(value, str): - # Ensure string is valid in given charset - value.decode(charset) - else: - # Convert unicode string to given charset - value = value.encode(charset) + # Convert bytestring using given charset + value = value.decode(charset) except UnicodeError as e: if mime_encode: # Wrapping in str() is a workaround for #12422 under Python 2. @@ -311,10 +300,7 @@ class HttpResponse(HttpResponseBase): """Full HTTP message, including headers, as a bytestring.""" return self.serialize_headers() + b'\r\n\r\n' + self.content - if six.PY3: - __bytes__ = serialize - else: - __str__ = serialize + __bytes__ = serialize @property def content(self): diff --git a/django/test/client.py b/django/test/client.py index 7365b1d2aa..8134c17253 100644 --- a/django/test/client.py +++ b/django/test/client.py @@ -322,10 +322,10 @@ class RequestFactory(object): if parsed[3]: path += str(";") + force_str(parsed[3]) path = uri_to_iri(path).encode(UTF_8) - # Under Python 3, non-ASCII values in the WSGI environ are arbitrarily - # decoded with ISO-8859-1. We replicate this behavior here. + # Replace the behavior where non-ASCII values in the WSGI environ are + # arbitrarily decoded with ISO-8859-1. # Refs comment in `get_bytes_from_wsgi()`. - return path.decode(ISO_8859_1) if six.PY3 else path + return path.decode(ISO_8859_1) def get(self, path, data=None, secure=False, **extra): "Construct a GET request." @@ -406,10 +406,8 @@ class RequestFactory(object): r.update(extra) # If QUERY_STRING is absent or empty, we want to extract it from the URL. if not r.get('QUERY_STRING'): - query_string = force_bytes(parsed[4]) # WSGI requires latin-1 encoded strings. See get_path_info(). - if six.PY3: - query_string = query_string.decode('iso-8859-1') + query_string = force_bytes(parsed[4]).decode('iso-8859-1') r['QUERY_STRING'] = query_string return self.request(**r) diff --git a/django/test/signals.py b/django/test/signals.py index 7b105eda35..a623e756ce 100644 --- a/django/test/signals.py +++ b/django/test/signals.py @@ -9,7 +9,7 @@ from django.core.signals import setting_changed from django.db import connections, router from django.db.utils import ConnectionRouter from django.dispatch import Signal, receiver -from django.utils import six, timezone +from django.utils import timezone from django.utils.formats import FORMAT_SETTINGS, reset_format_cache from django.utils.functional import empty @@ -139,7 +139,7 @@ def complex_setting_changed(**kwargs): # Considering the current implementation of the signals framework, # this stacklevel shows the line containing the override_settings call. warnings.warn("Overriding setting %s can lead to unexpected behavior." - % kwargs['setting'], stacklevel=5 if six.PY2 else 6) + % kwargs['setting'], stacklevel=6) @receiver(setting_changed) diff --git a/django/test/testcases.py b/django/test/testcases.py index 88d355311d..7e8cee5c0d 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -775,12 +775,6 @@ class SimpleTestCase(unittest.TestCase): standardMsg = '%s == %s' % (safe_repr(xml1, True), safe_repr(xml2, True)) self.fail(self._formatMessage(msg, standardMsg)) - if six.PY2: - assertCountEqual = unittest.TestCase.assertItemsEqual - assertNotRegex = unittest.TestCase.assertNotRegexpMatches - assertRaisesRegex = unittest.TestCase.assertRaisesRegexp - assertRegex = unittest.TestCase.assertRegexpMatches - class TransactionTestCase(SimpleTestCase): diff --git a/django/test/utils.py b/django/test/utils.py index b24f2e8c80..5ed32dbab5 100644 --- a/django/test/utils.py +++ b/django/test/utils.py @@ -6,6 +6,7 @@ import time import warnings from contextlib import contextmanager from functools import wraps +from types import SimpleNamespace from unittest import TestCase, skipIf, skipUnless from xml.dom.minidom import Node, parseString @@ -25,12 +26,6 @@ from django.utils.decorators import available_attrs from django.utils.encoding import force_str from django.utils.translation import deactivate -if six.PY3: - from types import SimpleNamespace -else: - class SimpleNamespace(object): - pass - try: import jinja2 except ImportError: @@ -615,7 +610,7 @@ def strip_quotes(want, got): def str_prefix(s): - return s % {'_': '' if six.PY3 else 'u'} + return s % {'_': ''} class CaptureQueriesContext(object): diff --git a/django/urls/resolvers.py b/django/urls/resolvers.py index 4a66895de5..4e8bded3db 100644 --- a/django/urls/resolvers.py +++ b/django/urls/resolvers.py @@ -209,11 +209,7 @@ class RegexURLPattern(LocaleRegexProvider): callback = callback.func if not hasattr(callback, '__name__'): return callback.__module__ + "." + callback.__class__.__name__ - elif six.PY3: - return callback.__module__ + "." + callback.__qualname__ - else: - # PY2 does not support __qualname__ - return callback.__module__ + "." + callback.__name__ + return callback.__module__ + "." + callback.__qualname__ class RegexURLResolver(LocaleRegexProvider): diff --git a/django/utils/_os.py b/django/utils/_os.py index 994f766d49..ddf2132f5f 100644 --- a/django/utils/_os.py +++ b/django/utils/_os.py @@ -1,41 +1,18 @@ import os -import sys import tempfile -from os.path import abspath, dirname, isabs, join, normcase, normpath, sep +from os.path import abspath, dirname, join, normcase, sep from django.core.exceptions import SuspiciousFileOperation -from django.utils import six from django.utils.encoding import force_text -if six.PY2: - fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding() - -# Under Python 2, define our own abspath function that can handle joining -# unicode paths to a current working directory that has non-ASCII characters -# in it. This isn't necessary on Windows since the Windows version of abspath -# handles this correctly. It also handles drive letters differently than the -# pure Python implementation, so it's best not to replace it. -if six.PY3 or os.name == 'nt': - abspathu = abspath -else: - def abspathu(path): - """ - Version of os.path.abspath that uses the unicode representation - of the current working directory, thus avoiding a UnicodeDecodeError - in join when the cwd has non-ASCII characters. - """ - if not isabs(path): - path = join(os.getcwdu(), path) - return normpath(path) +abspathu = abspath def upath(path): """ Always return a unicode path. """ - if six.PY2 and not isinstance(path, six.text_type): - return path.decode(fs_encoding) return path @@ -44,8 +21,6 @@ def npath(path): Always return a native path, that is unicode on Python 3 and bytestring on Python 2. """ - if six.PY2 and not isinstance(path, bytes): - return path.encode(fs_encoding) return path diff --git a/django/utils/crypto.py b/django/utils/crypto.py index a7257a1b3a..554958b6ee 100644 --- a/django/utils/crypto.py +++ b/django/utils/crypto.py @@ -9,7 +9,6 @@ import struct import time from django.conf import settings -from django.utils import six from django.utils.encoding import force_bytes from django.utils.six.moves import range @@ -94,7 +93,7 @@ else: if len(val1) != len(val2): return False result = 0 - if six.PY3 and isinstance(val1, bytes) and isinstance(val2, bytes): + if isinstance(val1, bytes) and isinstance(val2, bytes): for x, y in zip(val1, val2): result |= x ^ y else: diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py index a95f8c351a..7367924600 100644 --- a/django/utils/datastructures.py +++ b/django/utils/datastructures.py @@ -179,7 +179,7 @@ class MultiValueDict(dict): """Appends an item to the internal list associated with key.""" self.setlistdefault(key).append(value) - def _iteritems(self): + def items(self): """ Yields (key, value) pairs, where value is the last item in the list associated with the key. @@ -187,33 +187,15 @@ class MultiValueDict(dict): for key in self: yield key, self[key] - def _iterlists(self): + def lists(self): """Yields (key, list) pairs.""" return six.iteritems(super(MultiValueDict, self)) - def _itervalues(self): + def values(self): """Yield the last value on every key list.""" for key in self: yield self[key] - if six.PY3: - items = _iteritems - lists = _iterlists - values = _itervalues - else: - iteritems = _iteritems - iterlists = _iterlists - itervalues = _itervalues - - def items(self): - return list(self.iteritems()) - - def lists(self): - return list(self.iterlists()) - - def values(self): - return list(self.itervalues()) - def copy(self): """Returns a shallow copy of this object.""" return copy.copy(self) diff --git a/django/utils/decorators.py b/django/utils/decorators.py index 7ba31a32e3..071028930f 100644 --- a/django/utils/decorators.py +++ b/django/utils/decorators.py @@ -7,8 +7,6 @@ except ImportError: from functools import WRAPPER_ASSIGNMENTS, update_wrapper, wraps -from django.utils import six - class classonlymethod(classmethod): def __get__(self, instance, cls=None): @@ -121,13 +119,10 @@ def decorator_from_middleware(middleware_class): def available_attrs(fn): """ Return the list of functools-wrappable attributes on a callable. - This is required as a workaround for http://bugs.python.org/issue3445 + This was required as a workaround for http://bugs.python.org/issue3445 under Python 2. """ - if six.PY3: - return WRAPPER_ASSIGNMENTS - else: - return tuple(a for a in WRAPPER_ASSIGNMENTS if hasattr(fn, a)) + return WRAPPER_ASSIGNMENTS def make_middleware_decorator(middleware_class): diff --git a/django/utils/encoding.py b/django/utils/encoding.py index a3b07dcec6..f0627f5d39 100644 --- a/django/utils/encoding.py +++ b/django/utils/encoding.py @@ -2,13 +2,11 @@ import codecs import datetime import locale from decimal import Decimal +from urllib.parse import unquote_to_bytes from django.utils import six from django.utils.functional import Promise -from django.utils.six.moves.urllib.parse import quote, unquote - -if six.PY3: - from urllib.parse import unquote_to_bytes +from django.utils.six.moves.urllib.parse import quote class DjangoUnicodeDecodeError(UnicodeDecodeError): @@ -66,15 +64,10 @@ def force_text(s, encoding='utf-8', strings_only=False, errors='strict'): return s try: if not issubclass(type(s), six.string_types): - if six.PY3: - if isinstance(s, bytes): - s = six.text_type(s, encoding, errors) - else: - s = six.text_type(s) - elif hasattr(s, '__unicode__'): - s = six.text_type(s) + if isinstance(s, bytes): + s = six.text_type(s, encoding, errors) else: - s = six.text_type(bytes(s), encoding, errors) + s = six.text_type(s) else: # Note: We use .decode() here, instead of six.text_type(s, encoding, # errors), so that if s is a SafeBytes, it ends up being a @@ -127,10 +120,7 @@ def force_bytes(s, encoding='utf-8', strings_only=False, errors='strict'): return six.text_type(s).encode(encoding, errors) if not isinstance(s, six.string_types): try: - if six.PY3: - return six.text_type(s).encode(encoding) - else: - return bytes(s) + return six.text_type(s).encode(encoding) except UnicodeEncodeError: if isinstance(s, Exception): # An Exception subclass containing non-ASCII data that doesn't @@ -143,15 +133,8 @@ def force_bytes(s, encoding='utf-8', strings_only=False, errors='strict'): return s.encode(encoding, errors) -if six.PY3: - smart_str = smart_text - force_str = force_text -else: - smart_str = smart_bytes - force_str = force_bytes - # backwards compatibility for Python 2 - smart_unicode = smart_text - force_unicode = force_text +smart_str = smart_text +force_str = force_text smart_str.__doc__ = """ Apply smart_text in Python 3 and smart_bytes in Python 2. @@ -207,7 +190,7 @@ def uri_to_iri(uri): if uri is None: return uri uri = force_bytes(uri) - iri = unquote_to_bytes(uri) if six.PY3 else unquote(uri) + iri = unquote_to_bytes(uri) return repercent_broken_unicode(iri).decode('utf-8') diff --git a/django/utils/feedgenerator.py b/django/utils/feedgenerator.py index 64aa970e93..fbab58b905 100644 --- a/django/utils/feedgenerator.py +++ b/django/utils/feedgenerator.py @@ -23,7 +23,7 @@ http://web.archive.org/web/20110718035220/http://diveintomark.org/archives/2004/ """ import datetime -from django.utils import datetime_safe, six +from django.utils import datetime_safe from django.utils.encoding import force_text, iri_to_uri from django.utils.six import StringIO from django.utils.six.moves.urllib.parse import urlparse @@ -42,8 +42,6 @@ def rfc2822_date(date): dow = days[date.weekday()] month = months[date.month - 1] time_str = date.strftime('%s, %%d %s %%Y %%H:%%M:%%S ' % (dow, month)) - if six.PY2: # strftime returns a byte string in Python 2 - time_str = time_str.decode('utf-8') offset = date.utcoffset() # Historically, this function assumes that naive datetimes are in UTC. if offset is None: @@ -58,8 +56,6 @@ def rfc3339_date(date): # Support datetime objects older than 1900 date = datetime_safe.new_datetime(date) time_str = date.strftime('%Y-%m-%dT%H:%M:%S') - if six.PY2: # strftime returns a byte string in Python 2 - time_str = time_str.decode('utf-8') offset = date.utcoffset() # Historically, this function assumes that naive datetimes are in UTC. if offset is None: diff --git a/django/utils/functional.py b/django/utils/functional.py index 794f31047c..933085391d 100644 --- a/django/utils/functional.py +++ b/django/utils/functional.py @@ -92,16 +92,9 @@ def lazy(func, *resultclasses): assert not (cls._delegate_bytes and cls._delegate_text), ( "Cannot call lazy() with both bytes and text return types.") if cls._delegate_text: - if six.PY3: - cls.__str__ = cls.__text_cast - else: - cls.__unicode__ = cls.__text_cast - cls.__str__ = cls.__bytes_cast_encoded + cls.__str__ = cls.__text_cast elif cls._delegate_bytes: - if six.PY3: - cls.__bytes__ = cls.__bytes_cast - else: - cls.__str__ = cls.__bytes_cast + cls.__bytes__ = cls.__bytes_cast @classmethod def __promise__(cls, method_name): @@ -154,9 +147,7 @@ def lazy(func, *resultclasses): return hash(self.__cast()) def __mod__(self, rhs): - if self._delegate_bytes and six.PY2: - return bytes(self) % rhs - elif self._delegate_text: + if self._delegate_text: return six.text_type(self) % rhs return self.__cast() % rhs @@ -316,14 +307,9 @@ class LazyObject(object): return result return copy.deepcopy(self._wrapped, memo) - if six.PY3: - __bytes__ = new_method_proxy(bytes) - __str__ = new_method_proxy(str) - __bool__ = new_method_proxy(bool) - else: - __str__ = new_method_proxy(str) - __unicode__ = new_method_proxy(unicode) # NOQA: unicode undefined on PY3 - __nonzero__ = new_method_proxy(bool) + __bytes__ = new_method_proxy(bytes) + __str__ = new_method_proxy(str) + __bool__ = new_method_proxy(bool) # Introspection support __dir__ = new_method_proxy(dir) diff --git a/django/utils/glob.py b/django/utils/glob.py deleted file mode 100644 index 92194d21eb..0000000000 --- a/django/utils/glob.py +++ /dev/null @@ -1,19 +0,0 @@ -import os.path -import re - -from django.utils import six - -# backport of Python 3.4's glob.escape - -if six.PY3: - from glob import escape as glob_escape -else: - _magic_check = re.compile('([*?[])') - - def glob_escape(pathname): - """ - Escape all special characters. - """ - drive, pathname = os.path.splitdrive(pathname) - pathname = _magic_check.sub(r'[\1]', pathname) - return drive + pathname diff --git a/django/utils/html.py b/django/utils/html.py index 384093a02f..36b5d53283 100644 --- a/django/utils/html.py +++ b/django/utils/html.py @@ -363,22 +363,12 @@ def html_safe(klass): "can't apply @html_safe to %s because it defines " "__html__()." % klass.__name__ ) - if six.PY2: - if '__unicode__' not in klass.__dict__: - raise ValueError( - "can't apply @html_safe to %s because it doesn't " - "define __unicode__()." % klass.__name__ - ) - klass_unicode = klass.__unicode__ - klass.__unicode__ = lambda self: mark_safe(klass_unicode(self)) - klass.__html__ = lambda self: unicode(self) # NOQA: unicode undefined on PY3 - else: - if '__str__' not in klass.__dict__: - raise ValueError( - "can't apply @html_safe to %s because it doesn't " - "define __str__()." % klass.__name__ - ) - klass_str = klass.__str__ - klass.__str__ = lambda self: mark_safe(klass_str(self)) - klass.__html__ = lambda self: str(self) + if '__str__' not in klass.__dict__: + raise ValueError( + "can't apply @html_safe to %s because it doesn't " + "define __str__()." % klass.__name__ + ) + klass_str = klass.__str__ + klass.__str__ = lambda self: mark_safe(klass_str(self)) + klass.__html__ = lambda self: str(self) return klass diff --git a/django/utils/html_parser.py b/django/utils/html_parser.py index 89646079db..d272004f77 100644 --- a/django/utils/html_parser.py +++ b/django/utils/html_parser.py @@ -1,4 +1,3 @@ -from django.utils import six from django.utils.six.moves import html_parser as _html_parser try: @@ -8,15 +7,13 @@ except AttributeError: class HTMLParseError(Exception): pass -if six.PY3: - 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. - """ - def __init__(self, convert_charrefs=False, **kwargs): - _html_parser.HTMLParser.__init__(self, convert_charrefs=convert_charrefs, **kwargs) -else: - HTMLParser = _html_parser.HTMLParser +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. + """ + def __init__(self, convert_charrefs=False, **kwargs): + _html_parser.HTMLParser.__init__(self, convert_charrefs=convert_charrefs, **kwargs) diff --git a/django/utils/http.py b/django/utils/http.py index f4e77b86f9..0308e14676 100644 --- a/django/utils/http.py +++ b/django/utils/http.py @@ -189,12 +189,7 @@ def base36_to_int(s): # is sufficient to base36-encode any 64-bit integer) 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 to avoid - # returning a long (#15067). The long type was removed in Python 3. - if six.PY2 and value > sys.maxint: - raise ValueError("Base36 input too large") - return value + return int(s, 36) def int_to_base36(i): @@ -204,11 +199,6 @@ def int_to_base36(i): char_set = '0123456789abcdefghijklmnopqrstuvwxyz' if i < 0: raise ValueError("Negative base36 conversion input.") - if six.PY2: - 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.") if i < 36: return char_set[i] b36 = '' @@ -296,11 +286,6 @@ def is_safe_url(url, host=None, allowed_hosts=None, require_https=False): url = url.strip() if not url: return False - if six.PY2: - try: - url = force_text(url) - except UnicodeDecodeError: - return False if allowed_hosts is None: allowed_hosts = set() if host: @@ -388,13 +373,9 @@ def limited_parse_qsl(qs, keep_blank_values=False, encoding='utf-8', else: continue if len(nv[1]) or keep_blank_values: - if six.PY3: - name = nv[0].replace('+', ' ') - name = unquote(name, encoding=encoding, errors=errors) - value = nv[1].replace('+', ' ') - value = unquote(value, encoding=encoding, errors=errors) - else: - name = unquote(nv[0].replace(b'+', b' ')) - value = unquote(nv[1].replace(b'+', b' ')) + name = nv[0].replace('+', ' ') + name = unquote(name, encoding=encoding, errors=errors) + value = nv[1].replace('+', ' ') + value = unquote(value, encoding=encoding, errors=errors) r.append((name, value)) return r diff --git a/django/utils/inspect.py b/django/utils/inspect.py index ffcbf30407..9ef28bbf00 100644 --- a/django/utils/inspect.py +++ b/django/utils/inspect.py @@ -1,12 +1,7 @@ import inspect -from django.utils import six - def getargspec(func): - if six.PY2: - return inspect.getargspec(func) - sig = inspect.signature(func) args = [ p.name for p in sig.parameters.values() @@ -30,10 +25,6 @@ def getargspec(func): def get_func_args(func): - if six.PY2: - argspec = inspect.getargspec(func) - return argspec.args[1:] # ignore 'self' - sig = inspect.signature(func) return [ arg_name for arg_name, param in sig.parameters.items() @@ -47,20 +38,6 @@ def get_func_full_args(func): does not have a default value, omit it in the tuple. Arguments such as *args and **kwargs are also included. """ - if six.PY2: - argspec = inspect.getargspec(func) - args = argspec.args[1:] # ignore 'self' - defaults = argspec.defaults or [] - # Split args into two lists depending on whether they have default value - no_default = args[:len(args) - len(defaults)] - with_default = args[len(args) - len(defaults):] - # Join the two lists and combine it with default values - args = [(arg,) for arg in no_default] + zip(with_default, defaults) - # Add possible *args and **kwargs and prepend them with '*' or '**' - varargs = [('*' + argspec.varargs,)] if argspec.varargs else [] - kwargs = [('**' + argspec.keywords,)] if argspec.keywords else [] - return args + varargs + kwargs - sig = inspect.signature(func) args = [] for arg_name, param in sig.parameters.items(): @@ -80,20 +57,6 @@ def get_func_full_args(func): def func_accepts_kwargs(func): - if six.PY2: - # Not all callables are inspectable with getargspec, so we'll - # try a couple different ways but in the end fall back on assuming - # it is -- we don't want to prevent registration of valid but weird - # callables. - try: - argspec = inspect.getargspec(func) - except TypeError: - try: - argspec = inspect.getargspec(func.__call__) - except (TypeError, AttributeError): - argspec = None - return not argspec or argspec[2] is not None - return any( p for p in inspect.signature(func).parameters.values() if p.kind == p.VAR_KEYWORD @@ -104,9 +67,6 @@ def func_accepts_var_args(func): """ Return True if function 'func' accepts positional arguments *args. """ - if six.PY2: - return inspect.getargspec(func)[1] is not None - return any( p for p in inspect.signature(func).parameters.values() if p.kind == p.VAR_POSITIONAL @@ -114,7 +74,7 @@ def func_accepts_var_args(func): def func_has_no_args(func): - args = inspect.getargspec(func)[0] if six.PY2 else [ + args = [ p for p in inspect.signature(func).parameters.values() if p.kind == p.POSITIONAL_OR_KEYWORD ] @@ -122,8 +82,4 @@ def func_has_no_args(func): def func_supports_parameter(func, parameter): - if six.PY3: - return parameter in inspect.signature(func).parameters - else: - args, varargs, varkw, defaults = inspect.getargspec(func) - return parameter in args + return parameter in inspect.signature(func).parameters diff --git a/django/utils/module_loading.py b/django/utils/module_loading.py index 9dcbc926b9..ed2b8da635 100644 --- a/django/utils/module_loading.py +++ b/django/utils/module_loading.py @@ -2,6 +2,7 @@ import copy import os import sys from importlib import import_module +from importlib.util import find_spec as importlib_find from django.utils import six @@ -63,88 +64,17 @@ def autodiscover_modules(*args, **kwargs): raise -if six.PY3: - from importlib.util import find_spec as importlib_find +def module_has_submodule(package, module_name): + """See if 'module' is in 'package'.""" + try: + package_name = package.__name__ + package_path = package.__path__ + except AttributeError: + # package isn't a package. + return False - def module_has_submodule(package, module_name): - """See if 'module' is in 'package'.""" - try: - package_name = package.__name__ - package_path = package.__path__ - except AttributeError: - # package isn't a package. - return False - - full_module_name = package_name + '.' + module_name - return importlib_find(full_module_name, package_path) is not None - -else: - import imp - - def module_has_submodule(package, module_name): - """See if 'module' is in 'package'.""" - name = ".".join([package.__name__, module_name]) - try: - # None indicates a cached miss; see mark_miss() in Python/import.c. - return sys.modules[name] is not None - except KeyError: - pass - try: - package_path = package.__path__ # No __path__, then not a package. - except AttributeError: - # Since the remainder of this function assumes that we're dealing with - # a package (module with a __path__), so if it's not, then bail here. - return False - for finder in sys.meta_path: - if finder.find_module(name, package_path): - return True - for entry in package_path: - try: - # Try the cached finder. - finder = sys.path_importer_cache[entry] - if finder is None: - # Implicit import machinery should be used. - try: - file_, _, _ = imp.find_module(module_name, [entry]) - if file_: - file_.close() - return True - except ImportError: - continue - # Else see if the finder knows of a loader. - elif finder.find_module(name): - return True - else: - continue - except KeyError: - # No cached finder, so try and make one. - for hook in sys.path_hooks: - try: - finder = hook(entry) - # XXX Could cache in sys.path_importer_cache - if finder.find_module(name): - return True - else: - # Once a finder is found, stop the search. - break - except ImportError: - # Continue the search for a finder. - continue - else: - # No finder found. - # Try the implicit import machinery if searching a directory. - if os.path.isdir(entry): - try: - file_, _, _ = imp.find_module(module_name, [entry]) - if file_: - file_.close() - return True - except ImportError: - pass - # XXX Could insert None or NullImporter - else: - # Exhausted the search, so the module cannot be found. - return False + full_module_name = package_name + '.' + module_name + return importlib_find(full_module_name, package_path) is not None def module_dir(module): diff --git a/django/utils/safestring.py b/django/utils/safestring.py index 4355a050a0..3f4b3d48fd 100644 --- a/django/utils/safestring.py +++ b/django/utils/safestring.py @@ -83,12 +83,7 @@ class SafeText(six.text_type, SafeData): encode = curry(_proxy_method, method=six.text_type.encode) -if six.PY3: - SafeString = SafeText -else: - SafeString = SafeBytes - # backwards compatibility for Python 2 - SafeUnicode = SafeText +SafeString = SafeText def _safety_decorator(safety_marker, func): diff --git a/django/utils/text.py b/django/utils/text.py index dcedc524b0..fc8677cf4e 100644 --- a/django/utils/text.py +++ b/django/utils/text.py @@ -12,11 +12,6 @@ from django.utils.safestring import SafeText, mark_safe from django.utils.six.moves import html_entities from django.utils.translation import pgettext, ugettext as _, ugettext_lazy -if six.PY2: - # Import force_unicode even though this module doesn't use it, because some - # people rely on it being here. - from django.utils.encoding import force_unicode # NOQA - @keep_lazy_text def capfirst(x): diff --git a/django/utils/translation/template.py b/django/utils/translation/template.py index 04dac65e90..b6d8832f9c 100644 --- a/django/utils/translation/template.py +++ b/django/utils/translation/template.py @@ -5,7 +5,6 @@ from django.template.base import ( TOKEN_BLOCK, TOKEN_COMMENT, TOKEN_TEXT, TOKEN_VAR, TRANSLATOR_COMMENT_MARK, Lexer, ) -from django.utils import six from django.utils.encoding import force_text from django.utils.six import StringIO @@ -57,7 +56,7 @@ def templatize(src, origin=None, charset='utf-8'): comment_lineno_cache = None # Adding the u prefix allows gettext to recognize the Unicode string # (#26093). - raw_prefix = 'u' if six.PY3 else '' + raw_prefix = 'u' def join_tokens(tokens, trim=False): message = ''.join(tokens) diff --git a/django/utils/translation/trans_real.py b/django/utils/translation/trans_real.py index 9f016e8524..aca57ded0e 100644 --- a/django/utils/translation/trans_real.py +++ b/django/utils/translation/trans_real.py @@ -13,7 +13,7 @@ from django.conf.locale import LANG_INFO from django.core.exceptions import AppRegistryNotReady from django.core.signals import setting_changed from django.dispatch import receiver -from django.utils import lru_cache, six +from django.utils import lru_cache from django.utils._os import upath from django.utils.encoding import force_text from django.utils.safestring import SafeData, mark_safe @@ -336,11 +336,7 @@ def gettext(message): return do_translate(message, 'gettext') -if six.PY3: - ugettext = gettext -else: - def ugettext(message): - return do_translate(message, 'ugettext') +ugettext = gettext def pgettext(context, message): @@ -384,15 +380,7 @@ def ngettext(singular, plural, number): return do_ntranslate(singular, plural, number, 'ngettext') -if six.PY3: - ungettext = ngettext -else: - def ungettext(singular, plural, number): - """ - Returns a unicode strings of the translation of either the singular or - plural, based on the number. - """ - return do_ntranslate(singular, plural, number, 'ungettext') +ungettext = ngettext def npgettext(context, singular, plural, number): diff --git a/django/views/debug.py b/django/views/debug.py index 7c33aa8eca..2390995b56 100644 --- a/django/views/debug.py +++ b/django/views/debug.py @@ -399,11 +399,9 @@ class ExceptionReporter(object): if not exceptions: return frames - # In case there's just one exception (always in Python 2, - # sometimes in Python 3), take the traceback from self.tb (Python 2 - # doesn't have a __traceback__ attribute on Exception) + # In case there's just one exception, take the traceback from self.tb exc_value = exceptions.pop() - tb = self.tb if six.PY2 or not exceptions else exc_value.__traceback__ + tb = self.tb if not exceptions else exc_value.__traceback__ while tb is not None: # Support for __traceback_hide__ which is used by a few libraries @@ -438,9 +436,7 @@ class ExceptionReporter(object): # If the traceback for current exception is consumed, try the # other exception. - if six.PY2: - tb = tb.tb_next - elif not tb.tb_next and exceptions: + if not tb.tb_next and exceptions: exc_value = exceptions.pop() tb = exc_value.__traceback__ else: diff --git a/setup.cfg b/setup.cfg index 9d336981dd..c1ff45c56a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,7 +3,7 @@ doc_files = docs extras AUTHORS INSTALL LICENSE README.rst install-script = scripts/rpm-install.sh [flake8] -exclude = build,.git,.tox,./django/utils/lru_cache.py,./django/utils/six.py,./django/conf/app_template/*,./django/dispatch/weakref_backports.py,./tests/.env,./xmlrunner,tests/view_tests/tests/py3_test_debug.py,tests/template_tests/annotated_tag_function.py +exclude = build,.git,.tox,./django/utils/lru_cache.py,./django/utils/six.py,./django/conf/app_template/*,./tests/.env,./xmlrunner ignore = W601 max-line-length = 119 diff --git a/tests/admin_docs/test_views.py b/tests/admin_docs/test_views.py index 9f7f0ee268..34f6af6cdc 100644 --- a/tests/admin_docs/test_views.py +++ b/tests/admin_docs/test_views.py @@ -10,7 +10,6 @@ from django.db.models import fields from django.test import SimpleTestCase, modify_settings, override_settings from django.test.utils import captured_stderr from django.urls import reverse -from django.utils import six from .models import Company, Person from .tests import AdminDocsTestCase, TestDataMixin @@ -53,7 +52,6 @@ class AdminDocViewTests(TestDataMixin, AdminDocsTestCase): self.assertContains(response, 'Views by namespace test') self.assertContains(response, 'Name: test:func.') - @unittest.skipIf(six.PY2, "Python 2 doesn't support __qualname__.") def test_view_index_with_method(self): """ Views that are methods are listed correctly. @@ -89,7 +87,7 @@ class AdminDocViewTests(TestDataMixin, AdminDocsTestCase): """ url = reverse('django-admindocs-views-detail', args=['django.contrib.admin.sites.AdminSite.index']) response = self.client.get(url) - self.assertEqual(response.status_code, 200 if six.PY3 else 404) + self.assertEqual(response.status_code, 200) def test_model_index(self): response = self.client.get(reverse('django-admindocs-models-index')) diff --git a/tests/admin_scripts/tests.py b/tests/admin_scripts/tests.py index 08fe645535..e37e8a4256 100644 --- a/tests/admin_scripts/tests.py +++ b/tests/admin_scripts/tests.py @@ -27,7 +27,7 @@ from django.test import ( ) from django.utils._os import npath, upath from django.utils.encoding import force_text -from django.utils.six import PY2, StringIO +from django.utils.six import StringIO custom_templates_dir = os.path.join(os.path.dirname(upath(__file__)), 'custom_templates') @@ -626,7 +626,6 @@ class DjangoAdminSettingsDirectory(AdminScriptTestCase): self.assertTrue(os.path.exists(app_path)) self.assertTrue(os.path.exists(os.path.join(app_path, 'api.py'))) - @unittest.skipIf(PY2, "Python 2 doesn't support Unicode package names.") def test_startapp_unicode_name(self): "directory: startapp creates the correct directory with unicode characters" args = ['startapp', 'こんにちは'] @@ -1897,18 +1896,11 @@ class StartProject(LiveServerTestCase, AdminScriptTestCase): self.addCleanup(shutil.rmtree, testproject_dir, True) out, err = self.run_django_admin(args) - if PY2: - self.assertOutput( - err, - "Error: '%s' is not a valid project name. Please make " - "sure the name begins with a letter or underscore." % bad_name - ) - else: - self.assertOutput( - err, - "Error: '%s' is not a valid project name. Please make " - "sure the name is a valid identifier." % bad_name - ) + self.assertOutput( + err, + "Error: '%s' is not a valid project name. Please make " + "sure the name is a valid identifier." % bad_name + ) self.assertFalse(os.path.exists(testproject_dir)) def test_simple_project_different_directory(self): diff --git a/tests/annotations/tests.py b/tests/annotations/tests.py index bffac69e21..65886b2063 100644 --- a/tests/annotations/tests.py +++ b/tests/annotations/tests.py @@ -8,7 +8,6 @@ from django.db.models import ( ) from django.db.models.functions import Lower from django.test import TestCase, skipUnlessDBFeature -from django.utils import six from .models import ( Author, Book, Company, DepartmentStore, Employee, Publisher, Store, Ticket, @@ -24,7 +23,7 @@ def cxOracle_py3_bug(func): """ from unittest import expectedFailure from django.db import connection - return expectedFailure(func) if connection.vendor == 'oracle' and six.PY3 else func + return expectedFailure(func) if connection.vendor == 'oracle' else func class NonAggregateAnnotationTestCase(TestCase): diff --git a/tests/apps/tests.py b/tests/apps/tests.py index d2adc681f2..9576420b54 100644 --- a/tests/apps/tests.py +++ b/tests/apps/tests.py @@ -1,5 +1,4 @@ import os -from unittest import skipUnless from django.apps import AppConfig, apps from django.apps.registry import Apps @@ -8,7 +7,6 @@ from django.core.exceptions import AppRegistryNotReady, ImproperlyConfigured from django.db import models from django.test import SimpleTestCase, override_settings from django.test.utils import extend_sys_path, isolate_apps -from django.utils import six from django.utils._os import upath from .default_config_app.apps import CustomConfig @@ -371,7 +369,6 @@ class AppConfigTests(SimpleTestCase): self.assertEqual(ac.path, 'a') -@skipUnless(six.PY3, "Namespace packages sans __init__.py were added in Python 3.3") class NamespacePackageAppTests(SimpleTestCase): # We need nsapp to be top-level so our multiple-paths tests can add another # location for it (if its inside a normal package with an __init__.py that diff --git a/tests/auth_tests/test_forms.py b/tests/auth_tests/test_forms.py index 36b4e8f4e7..c055b58042 100644 --- a/tests/auth_tests/test_forms.py +++ b/tests/auth_tests/test_forms.py @@ -1,6 +1,5 @@ import datetime import re -from unittest import skipIf from django import forms from django.contrib.auth.forms import ( @@ -15,7 +14,7 @@ from django.core import mail from django.core.mail import EmailMultiAlternatives from django.forms.fields import CharField, Field, IntegerField from django.test import SimpleTestCase, TestCase, mock, override_settings -from django.utils import six, translation +from django.utils import translation from django.utils.encoding import force_text from django.utils.text import capfirst from django.utils.translation import ugettext as _ @@ -114,14 +113,10 @@ class UserCreationFormTest(TestDataMixin, TestCase): 'password2': 'test123', } form = UserCreationForm(data) - if six.PY3: - self.assertTrue(form.is_valid()) - u = form.save() - self.assertEqual(u.username, '宝') - else: - self.assertFalse(form.is_valid()) + self.assertTrue(form.is_valid()) + u = form.save() + self.assertEqual(u.username, '宝') - @skipIf(six.PY2, "Python 2 doesn't support unicode usernames by default.") def test_normalize_username(self): # The normalization happens in AbstractBaseUser.clean() and ModelForm # validation calls Model.clean(). @@ -137,7 +132,6 @@ class UserCreationFormTest(TestDataMixin, TestCase): self.assertNotEqual(user.username, ohm_username) self.assertEqual(user.username, 'testΩ') # U+03A9 GREEK CAPITAL LETTER OMEGA - @skipIf(six.PY2, "Python 2 doesn't support unicode usernames by default.") def test_duplicate_normalized_unicode(self): """ To prevent almost identical usernames, visually identical but differing diff --git a/tests/auth_tests/test_management.py b/tests/auth_tests/test_management.py index eaceafb444..ac57bbd511 100644 --- a/tests/auth_tests/test_management.py +++ b/tests/auth_tests/test_management.py @@ -32,9 +32,6 @@ def mock_inputs(inputs): class mock_getpass: @staticmethod def getpass(prompt=b'Password: ', stream=None): - if six.PY2: - # getpass on Windows only supports prompt as bytestring (#19807) - assert isinstance(prompt, six.binary_type) if callable(inputs['password']): return inputs['password']() return inputs['password'] diff --git a/tests/auth_tests/test_tokens.py b/tests/auth_tests/test_tokens.py index 7ff3f15f3d..0662ec513e 100644 --- a/tests/auth_tests/test_tokens.py +++ b/tests/auth_tests/test_tokens.py @@ -1,11 +1,9 @@ -import unittest from datetime import date, timedelta from django.conf import settings from django.contrib.auth.models import User from django.contrib.auth.tokens import PasswordResetTokenGenerator from django.test import TestCase -from django.utils.six import PY3 class TokenGeneratorTest(TestCase): @@ -51,18 +49,6 @@ class TokenGeneratorTest(TestCase): p2 = Mocked(date.today() + timedelta(settings.PASSWORD_RESET_TIMEOUT_DAYS + 1)) self.assertFalse(p2.check_token(user, tk1)) - @unittest.skipIf(PY3, "Unnecessary test with Python 3") - def test_date_length(self): - """ - Overly long dates, which are a potential DoS vector, aren't allowed. - """ - user = User.objects.create_user('ima1337h4x0r', 'test4@example.com', 'p4ssw0rd') - p0 = PasswordResetTokenGenerator() - - # This will put a 14-digit base36 timestamp into the token, which is too large. - with self.assertRaises(ValueError): - p0._make_token_with_timestamp(user, 175455491841851871349) - def test_check_token_with_nonexistent_token_and_user(self): user = User.objects.create_user('tokentestuser', 'test2@example.com', 'testpw') p0 = PasswordResetTokenGenerator() diff --git a/tests/backends/test_utils.py b/tests/backends/test_utils.py index 6f59d1b23b..d158e2a5a2 100644 --- a/tests/backends/test_utils.py +++ b/tests/backends/test_utils.py @@ -1,7 +1,6 @@ from django.core.exceptions import ImproperlyConfigured from django.db.utils import load_backend from django.test import SimpleTestCase -from django.utils import six class TestLoadBackend(SimpleTestCase): @@ -10,7 +9,7 @@ class TestLoadBackend(SimpleTestCase): "'foo' isn't an available database backend.\n" "Try using 'django.db.backends.XXX', where XXX is one of:\n" " 'mysql', 'oracle', 'postgresql', 'sqlite3'\n" - "Error was: No module named %s" - ) % "foo.base" if six.PY2 else "'foo'" + "Error was: No module named 'foo'" + ) with self.assertRaisesMessage(ImproperlyConfigured, msg): load_backend('foo') diff --git a/tests/base/models.py b/tests/base/models.py index 077b93fbb8..fb91522717 100644 --- a/tests/base/models.py +++ b/tests/base/models.py @@ -13,12 +13,3 @@ class CustomBaseModel(models.base.ModelBase): class MyModel(six.with_metaclass(CustomBaseModel, models.Model)): """Model subclass with a custom base using six.with_metaclass.""" - - -# This is done to ensure that for Python2 only, defining metaclasses -# still does not fail to create the model. - -if six.PY2: - class MyPython2Model(models.Model): - """Model subclass with a custom base using __metaclass__.""" - __metaclass__ = CustomBaseModel diff --git a/tests/cache/tests.py b/tests/cache/tests.py index 5be3db0ef4..caeb8a47df 100644 --- a/tests/cache/tests.py +++ b/tests/cache/tests.py @@ -928,11 +928,7 @@ class BaseCacheTests(object): self.assertEqual(cache.get_or_set('mykey', my_callable()), 'value') def test_get_or_set_version(self): - msg = ( - "get_or_set() missing 1 required positional argument: 'default'" - if six.PY3 - else 'get_or_set() takes at least 3 arguments' - ) + msg = "get_or_set() missing 1 required positional argument: 'default'" cache.get_or_set('brian', 1979, version=2) with self.assertRaisesMessage(TypeError, msg): cache.get_or_set('brian') diff --git a/tests/dbshell/test_postgresql_psycopg2.py b/tests/dbshell/test_postgresql_psycopg2.py index 4c4a1ae25e..755464b3bb 100644 --- a/tests/dbshell/test_postgresql_psycopg2.py +++ b/tests/dbshell/test_postgresql_psycopg2.py @@ -3,7 +3,6 @@ import os from django.db.backends.postgresql.client import DatabaseClient from django.test import SimpleTestCase, mock -from django.utils import six from django.utils.encoding import force_bytes, force_str @@ -91,16 +90,12 @@ class PostgreSqlDbshellCommandTestCase(SimpleTestCase): encoding = locale.getpreferredencoding() username = 'rôle' password = 'sésame' - try: - username_str = force_str(username, encoding) - password_str = force_str(password, encoding) - pgpass_bytes = force_bytes( - 'somehost:444:dbname:%s:%s' % (username, password), - encoding=encoding, - ) - except UnicodeEncodeError: - if six.PY2: - self.skipTest("Your locale can't run this test.") + username_str = force_str(username, encoding) + password_str = force_str(password, encoding) + pgpass_bytes = force_bytes( + 'somehost:444:dbname:%s:%s' % (username, password), + encoding=encoding, + ) self.assertEqual( self._run_it({ 'database': 'dbname', diff --git a/tests/file_uploads/tests.py b/tests/file_uploads/tests.py index 769c06c904..f9cecf96a2 100644 --- a/tests/file_uploads/tests.py +++ b/tests/file_uploads/tests.py @@ -15,7 +15,7 @@ from django.http.multipartparser import MultiPartParser, parse_header from django.test import SimpleTestCase, TestCase, client, override_settings from django.utils.encoding import force_bytes from django.utils.http import urlquote -from django.utils.six import PY2, StringIO +from django.utils.six import StringIO from . import uploadhandler from .models import FileModel @@ -102,9 +102,7 @@ class FileUploadTests(TestCase): self._test_base64_upload("Big data" * 68000) # > 512Kb def test_big_base64_newlines_upload(self): - self._test_base64_upload( - # encodestring is a deprecated alias on Python 3 - "Big data" * 68000, encode=base64.encodestring if PY2 else base64.encodebytes) + self._test_base64_upload("Big data" * 68000, encode=base64.encodebytes) def test_unicode_file_name(self): tdir = sys_tempfile.mkdtemp() diff --git a/tests/files/tests.py b/tests/files/tests.py index 276cbed544..a0ff3d4782 100644 --- a/tests/files/tests.py +++ b/tests/files/tests.py @@ -181,10 +181,7 @@ class ContentFileTestCase(unittest.TestCase): retrieved content is of the same type. """ self.assertIsInstance(ContentFile(b"content").read(), bytes) - if six.PY3: - self.assertIsInstance(ContentFile("español").read(), six.text_type) - else: - self.assertIsInstance(ContentFile("español").read(), bytes) + self.assertIsInstance(ContentFile("español").read(), six.text_type) class DimensionClosingBug(unittest.TestCase): diff --git a/tests/fixtures_regress/tests.py b/tests/fixtures_regress/tests.py index 3667fcbd4c..7ea9e6cc3e 100644 --- a/tests/fixtures_regress/tests.py +++ b/tests/fixtures_regress/tests.py @@ -2,10 +2,8 @@ import json import os import re -import unittest import warnings -import django from django.core import management, serializers from django.core.exceptions import ImproperlyConfigured from django.core.serializers.base import DeserializationError @@ -15,9 +13,8 @@ from django.test import ( TestCase, TransactionTestCase, override_settings, skipIfDBFeature, skipUnlessDBFeature, ) -from django.utils import six from django.utils._os import upath -from django.utils.six import PY3, StringIO +from django.utils.six import StringIO from .models import ( Absolute, Animal, Article, Book, Child, Circle1, Circle2, Circle3, @@ -32,16 +29,6 @@ from .models import ( _cur_dir = os.path.dirname(os.path.abspath(upath(__file__))) -def is_ascii(s): - return all(ord(c) < 128 for c in s) - - -skipIfNonASCIIPath = unittest.skipIf( - not is_ascii(django.__file__) and six.PY2, - 'Python 2 crashes when checking non-ASCII exception messages.' -) - - class TestFixtures(TestCase): def animal_pre_save_check(self, signal, sender, instance, **kwargs): @@ -205,7 +192,6 @@ class TestFixtures(TestCase): verbosity=0, ) - @skipIfNonASCIIPath @override_settings(SERIALIZATION_MODULES={'unkn': 'unexistent.path'}) def test_unimportable_serializer(self): """ @@ -350,8 +336,8 @@ class TestFixtures(TestCase): self.assertEqual( self.pre_save_checks, [ - ("Count = 42 (<%s 'int'>)" % ('class' if PY3 else 'type'), - "Weight = 1.2 (<%s 'float'>)" % ('class' if PY3 else 'type')) + ("Count = 42 ()", + "Weight = 1.2 ()") ] ) finally: @@ -531,7 +517,6 @@ class TestFixtures(TestCase): with self.assertRaisesMessage(ImproperlyConfigured, "settings.FIXTURE_DIRS contains duplicates."): management.call_command('loaddata', 'absolute.json', verbosity=0) - @skipIfNonASCIIPath @override_settings(FIXTURE_DIRS=[os.path.join(_cur_dir, 'fixtures')]) def test_fixture_dirs_with_default_fixture_path(self): """ diff --git a/tests/handlers/tests.py b/tests/handlers/tests.py index bae2043918..72ba98b1d3 100644 --- a/tests/handlers/tests.py +++ b/tests/handlers/tests.py @@ -34,7 +34,7 @@ class HandlerTests(SimpleTestCase): produces a 404. """ environ = RequestFactory().get('/').environ - environ['PATH_INFO'] = b'\xed' if six.PY2 else '\xed' + environ['PATH_INFO'] = '\xed' handler = WSGIHandler() response = handler(environ, lambda *a, **k: None) # The path of the request will be encoded to '/%ED'. @@ -53,25 +53,17 @@ class HandlerTests(SimpleTestCase): ] got = [] for raw_query_string in raw_query_strings: - if six.PY3: - # Simulate http.server.BaseHTTPRequestHandler.parse_request handling of raw request - environ['QUERY_STRING'] = str(raw_query_string, 'iso-8859-1') - else: - environ['QUERY_STRING'] = raw_query_string + # Simulate http.server.BaseHTTPRequestHandler.parse_request handling of raw request + environ['QUERY_STRING'] = str(raw_query_string, 'iso-8859-1') request = WSGIRequest(environ) got.append(request.GET['want']) - if six.PY2: - self.assertListEqual(got, ['café', 'café', 'café', 'café']) - else: - # On Python 3, %E9 is converted to the unicode replacement character by parse_qsl - self.assertListEqual(got, ['café', 'café', 'caf\ufffd', 'café']) + # %E9 is converted to the unicode replacement character by parse_qsl + self.assertListEqual(got, ['café', 'café', 'caf\ufffd', 'café']) def test_non_ascii_cookie(self): """Non-ASCII cookies set in JavaScript are properly decoded (#20557).""" environ = RequestFactory().get('/').environ - raw_cookie = 'want="café"' - if six.PY3: - raw_cookie = raw_cookie.encode('utf-8').decode('iso-8859-1') + raw_cookie = 'want="café"'.encode('utf-8').decode('iso-8859-1') environ['HTTP_COOKIE'] = raw_cookie request = WSGIRequest(environ) # If would be nicer if request.COOKIES returned unicode values. diff --git a/tests/httpwrappers/tests.py b/tests/httpwrappers/tests.py index d9885ea40c..9042f8ed6e 100644 --- a/tests/httpwrappers/tests.py +++ b/tests/httpwrappers/tests.py @@ -55,8 +55,6 @@ class QueryDictTests(SimpleTestCase): def test_immutable_basic_operations(self): q = QueryDict() self.assertEqual(q.getlist('foo'), []) - if six.PY2: - self.assertIs(q.has_key('foo'), False) self.assertNotIn('foo', q) self.assertEqual(list(six.iteritems(q)), []) self.assertEqual(list(six.iterlists(q)), []) @@ -85,11 +83,7 @@ class QueryDictTests(SimpleTestCase): with self.assertRaises(AttributeError): q.appendlist('foo', ['bar']) - if six.PY2: - self.assertTrue(q.has_key('foo')) self.assertIn('foo', q) - if six.PY2: - self.assertFalse(q.has_key('bar')) self.assertNotIn('bar', q) self.assertEqual(list(six.iteritems(q)), [('foo', 'bar')]) @@ -150,8 +144,6 @@ class QueryDictTests(SimpleTestCase): q.appendlist('foo', 'another') self.assertEqual(q.getlist('foo'), ['bar', 'baz', 'another']) self.assertEqual(q['foo'], 'another') - if six.PY2: - self.assertTrue(q.has_key('foo')) self.assertIn('foo', q) self.assertListEqual(sorted(six.iteritems(q)), @@ -199,11 +191,7 @@ class QueryDictTests(SimpleTestCase): with self.assertRaises(AttributeError): q.appendlist('foo', ['bar']) - if six.PY2: - self.assertIs(q.has_key('vote'), True) self.assertIn('vote', q) - if six.PY2: - self.assertIs(q.has_key('foo'), False) self.assertNotIn('foo', q) self.assertEqual(list(six.iteritems(q)), [('vote', 'no')]) self.assertEqual(list(six.iterlists(q)), [('vote', ['yes', 'no'])]) @@ -224,19 +212,6 @@ class QueryDictTests(SimpleTestCase): with self.assertRaises(AttributeError): q.__delitem__('vote') - if six.PY2: - def test_invalid_input_encoding(self): - """ - QueryDicts must be able to handle invalid input encoding (in this - case, bad UTF-8 encoding), falling back to ISO-8859-1 decoding. - - This test doesn't apply under Python 3 because the URL is a string - and not a bytestring. - """ - q = QueryDict(str(b'foo=bar&foo=\xff')) - self.assertEqual(q['foo'], '\xff') - self.assertEqual(q.getlist('foo'), ['bar', '\xff']) - def test_pickle(self): q = QueryDict() q1 = pickle.loads(pickle.dumps(q, 2)) @@ -807,15 +782,6 @@ class CookieTests(unittest.TestCase): c.load({'name': 'val'}) self.assertEqual(c['name'].value, 'val') - @unittest.skipUnless(six.PY2, "PY3 throws an exception on invalid cookie keys.") - def test_bad_cookie(self): - """ - Regression test for #18403 - """ - r = HttpResponse() - r.set_cookie("a:.b/", 1) - self.assertEqual(len(r.cookies.bad_cookies), 1) - def test_pickle(self): rawdata = 'Customer="WILE_E_COYOTE"; Path=/acme; Version=1' expected_output = 'Set-Cookie: %s' % rawdata diff --git a/tests/i18n/test_compilation.py b/tests/i18n/test_compilation.py index d17078e290..3f3bf773bf 100644 --- a/tests/i18n/test_compilation.py +++ b/tests/i18n/test_compilation.py @@ -12,7 +12,7 @@ from django.core.management.commands.makemessages import \ from django.core.management.utils import find_command from django.test import SimpleTestCase, mock, override_settings from django.test.utils import captured_stderr, captured_stdout -from django.utils import six, translation +from django.utils import translation from django.utils.encoding import force_text from django.utils.six import StringIO from django.utils.translation import ugettext @@ -144,18 +144,11 @@ class CompilationErrorHandling(MessageCompilationTests): env = os.environ.copy() env.update({str('LANG'): str('C')}) with mock.patch('django.core.management.utils.Popen', lambda *args, **kwargs: Popen(*args, env=env, **kwargs)): - if six.PY2: - # Various assertRaises on PY2 don't support unicode error messages. - try: - call_command('compilemessages', locale=['ko'], verbosity=0) - except CommandError as err: - self.assertIn("' cannot start a field name", six.text_type(err)) - else: - cmd = MakeMessagesCommand() - if cmd.gettext_version < (0, 18, 3): - self.skipTest("python-brace-format is a recent gettext addition.") - with self.assertRaisesMessage(CommandError, "' cannot start a field name"): - call_command('compilemessages', locale=['ko'], verbosity=0) + cmd = MakeMessagesCommand() + if cmd.gettext_version < (0, 18, 3): + self.skipTest("python-brace-format is a recent gettext addition.") + with self.assertRaisesMessage(CommandError, "' cannot start a field name"): + call_command('compilemessages', locale=['ko'], verbosity=0) class ProjectAndAppTests(MessageCompilationTests): diff --git a/tests/i18n/tests.py b/tests/i18n/tests.py index 2b9793109b..d039d016ce 100644 --- a/tests/i18n/tests.py +++ b/tests/i18n/tests.py @@ -6,7 +6,6 @@ import pickle from contextlib import contextmanager from importlib import import_module from threading import local -from unittest import skipUnless from django import forms from django.conf import settings @@ -23,13 +22,11 @@ from django.utils.formats import ( ) from django.utils.numberformat import format as nformat from django.utils.safestring import SafeBytes, SafeText -from django.utils.six import PY3 from django.utils.translation import ( LANGUAGE_SESSION_KEY, activate, check_for_language, deactivate, - get_language, get_language_from_request, get_language_info, gettext, - gettext_lazy, ngettext_lazy, npgettext, npgettext_lazy, pgettext, - pgettext_lazy, trans_real, ugettext, ugettext_lazy, ungettext, - ungettext_lazy, + get_language, get_language_from_request, get_language_info, gettext_lazy, + ngettext_lazy, npgettext, npgettext_lazy, pgettext, trans_real, ugettext, + ugettext_lazy, ungettext, ungettext_lazy, ) from .forms import CompanyForm, I18nForm, SelectDateForm @@ -141,33 +138,6 @@ class TranslationTests(SimpleTestCase): s4 = ugettext_lazy('Some other string') self.assertNotEqual(s, s4) - @skipUnless(six.PY2, "No more bytestring translations on PY3") - def test_bytestrings(self): - """gettext() returns a bytestring if input is bytestring.""" - - # Using repr() to check translated text and type - self.assertEqual(repr(gettext(b"Time")), repr(b"Time")) - self.assertEqual(repr(gettext("Time")), repr("Time")) - - with translation.override('de', deactivate=True): - self.assertEqual(repr(gettext(b"Time")), repr(b"Zeit")) - self.assertEqual(repr(gettext("Time")), repr(b"Zeit")) - - @skipUnless(six.PY2, "No more bytestring translations on PY3") - def test_lazy_and_bytestrings(self): - # On Python 2, (n)gettext_lazy should not transform a bytestring to unicode - self.assertEqual(gettext_lazy(b"test").upper(), b"TEST") - self.assertEqual((ngettext_lazy(b"%d test", b"%d tests") % 1).upper(), b"1 TEST") - - # Other versions of lazy functions always return unicode - self.assertEqual(ugettext_lazy(b"test").upper(), "TEST") - self.assertEqual((ungettext_lazy(b"%d test", b"%d tests") % 1).upper(), "1 TEST") - self.assertEqual(pgettext_lazy(b"context", b"test").upper(), "TEST") - self.assertEqual( - (npgettext_lazy(b"context", b"%d test", b"%d tests") % 1).upper(), - "1 TEST" - ) - def test_lazy_pickle(self): s1 = ugettext_lazy("test") self.assertEqual(six.text_type(s1), "test") @@ -223,20 +193,6 @@ class TranslationTests(SimpleTestCase): with self.assertRaisesMessage(KeyError, 'Your dictionary lacks key'): complex_context_deferred % {'name': 'Jim'} - @skipUnless(six.PY2, "PY3 doesn't have distinct int and long types") - def test_ungettext_lazy_long(self): - """ - Regression test for #22820: int and long should be treated alike in ungettext_lazy. - """ - result = ungettext_lazy('%(name)s has %(num)d good result', '%(name)s has %(num)d good results', 4) - self.assertEqual(result % {'name': 'Joe', 'num': 4}, "Joe has 4 good results") - # Now with a long - result = ungettext_lazy( - '%(name)s has %(num)d good result', '%(name)s has %(num)d good results', - long(4) # NOQA: long undefined on PY3 - ) - self.assertEqual(result % {'name': 'Joe', 'num': 4}, "Joe has 4 good results") - def test_ungettext_lazy_bool(self): self.assertTrue(ungettext_lazy('%d good result', '%d good results')) self.assertFalse(ungettext_lazy('', '')) @@ -298,7 +254,7 @@ class FormattingTests(SimpleTestCase): self.d = datetime.date(2009, 12, 31) self.dt = datetime.datetime(2009, 12, 31, 20, 50) self.t = datetime.time(10, 15, 48) - self.long = 10000 if PY3 else long(10000) # NOQA: long undefined on PY3 + self.long = 10000 self.ctxt = Context({ 'n': self.n, 't': self.t, diff --git a/tests/inspectdb/tests.py b/tests/inspectdb/tests.py index 1293ff68ac..8f17735929 100644 --- a/tests/inspectdb/tests.py +++ b/tests/inspectdb/tests.py @@ -5,7 +5,7 @@ from django.core.management import call_command from django.db import connection from django.test import TestCase, mock, skipUnlessDBFeature from django.utils.encoding import force_text -from django.utils.six import PY3, StringIO +from django.utils.six import StringIO from .models import ColumnTypes @@ -196,11 +196,7 @@ class InspectDBTestCase(TestCase): self.assertIn("field_field_0 = models.IntegerField(db_column='%s__')" % base_name, output) self.assertIn("field_field_1 = models.IntegerField(db_column='__field')", output) self.assertIn("prc_x = models.IntegerField(db_column='prc(%) x')", output) - if PY3: - # Python 3 allows non-ASCII identifiers - self.assertIn("tamaño = models.IntegerField()", output) - else: - self.assertIn("tama_o = models.IntegerField(db_column='tama\\xf1o')", output) + self.assertIn("tamaño = models.IntegerField()", output) def test_table_name_introspection(self): """ diff --git a/tests/invalid_models_tests/test_relative_fields.py b/tests/invalid_models_tests/test_relative_fields.py index 5fd76229e9..384d1fa03e 100644 --- a/tests/invalid_models_tests/test_relative_fields.py +++ b/tests/invalid_models_tests/test_relative_fields.py @@ -3,7 +3,6 @@ from django.db import models from django.db.models.fields.related import ForeignObject from django.test.testcases import SimpleTestCase, skipIfDBFeature from django.test.utils import isolate_apps, override_settings -from django.utils import six @isolate_apps('invalid_models_tests') @@ -655,10 +654,8 @@ class RelativeFieldTests(SimpleTestCase): 'with', # a Python keyword 'related_name\n', '', + ',', # non-ASCII ] - # Python 2 crashes on non-ASCII strings. - if six.PY3: - invalid_related_names.append(',') class Parent(models.Model): pass @@ -695,10 +692,9 @@ class RelativeFieldTests(SimpleTestCase): 'ends_with_plus+', '_+', '+', + '試', + '試驗+', ] - # Python 2 crashes on non-ASCII strings. - if six.PY3: - related_names.extend(['試', '試驗+']) class Parent(models.Model): pass diff --git a/tests/mail/tests.py b/tests/mail/tests.py index ba7ff44714..cfe2889aa6 100644 --- a/tests/mail/tests.py +++ b/tests/mail/tests.py @@ -8,8 +8,10 @@ import socket import sys import tempfile import threading +from email import message_from_binary_file, message_from_bytes from email.header import Header from email.mime.text import MIMEText +from email.utils import parseaddr from smtplib import SMTP, SMTPAuthenticationError, SMTPException from ssl import SSLError @@ -24,19 +26,9 @@ from django.test import SimpleTestCase, override_settings from django.test.utils import requires_tz_support from django.utils._os import upath from django.utils.encoding import force_bytes, force_text -from django.utils.six import PY3, StringIO, binary_type +from django.utils.six import StringIO, binary_type from django.utils.translation import ugettext_lazy -if PY3: - from email.utils import parseaddr - from email import message_from_bytes, message_from_binary_file -else: - from email.Utils import parseaddr - from email import ( - message_from_string as message_from_bytes, - message_from_file as message_from_binary_file, - ) - class HeadersCheckMixin(object): @@ -656,16 +648,10 @@ class MailTests(HeadersCheckMixin, SimpleTestCase): sanitize_address(('A name', 'to@example.com'), 'ascii'), 'A name ' ) - if PY3: - self.assertEqual( - sanitize_address(('A name', 'to@example.com'), 'utf-8'), - '=?utf-8?q?A_name?= ' - ) - else: - self.assertEqual( - sanitize_address(('A name', 'to@example.com'), 'utf-8'), - 'A name ' - ) + self.assertEqual( + sanitize_address(('A name', 'to@example.com'), 'utf-8'), + '=?utf-8?q?A_name?= ' + ) # Unicode characters are are supported in RFC-6532. self.assertEqual( @@ -1165,18 +1151,8 @@ class FakeSMTPServer(smtpd.SMTPServer, threading.Thread): self.active_lock = threading.Lock() self.sink_lock = threading.Lock() - if not PY3: - def handle_accept(self): - # copy of Python 2.7 smtpd.SMTPServer.handle_accept with hardcoded - # SMTPChannel replaced by self.channel_class - pair = self.accept() - if pair is not None: - conn, addr = pair - self.channel_class(self, conn, addr) - def process_message(self, peer, mailfrom, rcpttos, data): - if PY3: - data = data.encode('utf-8') + data = data.encode('utf-8') m = message_from_bytes(data) maddr = parseaddr(m.get('from'))[1] @@ -1448,8 +1424,7 @@ class SMTPBackendTests(BaseEmailBackendTests, SMTPBackendTestsBase): self.assertTrue(msg) - if PY3: - msg = msg.decode('utf-8') + msg = msg.decode('utf-8') # The message only contains CRLF and not combinations of CRLF, LF, and CR. msg = msg.replace('\r\n', '') self.assertNotIn('\r', msg) diff --git a/tests/middleware/tests.py b/tests/middleware/tests.py index 43ea0748a0..3c0d3d81ea 100644 --- a/tests/middleware/tests.py +++ b/tests/middleware/tests.py @@ -2,7 +2,6 @@ import gzip import random import re from io import BytesIO -from unittest import skipIf from django.conf import settings from django.core import mail @@ -401,24 +400,6 @@ class BrokenLinkEmailsMiddlewareTest(SimpleTestCase): BrokenLinkEmailsMiddleware().process_response(self.req, self.resp) self.assertEqual(len(mail.outbox), 0) - @skipIf(six.PY3, "HTTP_REFERER is str type on Python 3") - def test_404_error_nonascii_referrer(self): - # Such referer strings should not happen, but anyway, if it happens, - # let's not crash - self.req.META['HTTP_REFERER'] = b'http://testserver/c/\xd0\xbb\xd0\xb8/' - BrokenLinkEmailsMiddleware().process_response(self.req, self.resp) - self.assertEqual(len(mail.outbox), 1) - - @skipIf(six.PY3, "HTTP_USER_AGENT is str type on Python 3") - def test_404_error_nonascii_user_agent(self): - # Such user agent strings should not happen, but anyway, if it happens, - # let's not crash - self.req.META['HTTP_REFERER'] = '/another/url/' - self.req.META['HTTP_USER_AGENT'] = b'\xd0\xbb\xd0\xb8\xff\xff' - BrokenLinkEmailsMiddleware().process_response(self.req, self.resp) - self.assertEqual(len(mail.outbox), 1) - self.assertIn('User agent: \u043b\u0438\ufffd\ufffd\n', mail.outbox[0].body) - def test_custom_request_checker(self): class SubclassedMiddleware(BrokenLinkEmailsMiddleware): ignored_user_agent_patterns = (re.compile(r'Spider.*'), re.compile(r'Robot.*')) diff --git a/tests/migrations/test_commands.py b/tests/migrations/test_commands.py index ab2e47fabd..ca6b42044f 100644 --- a/tests/migrations/test_commands.py +++ b/tests/migrations/test_commands.py @@ -661,18 +661,10 @@ class MakeMigrationsTests(MigrationTestBase): self.assertIn('migrations.CreateModel', content) self.assertIn('initial = True', content) - if six.PY3: - self.assertIn('úñí©óðé µóðéø', content) # Meta.verbose_name - self.assertIn('úñí©óðé µóðéøß', content) # Meta.verbose_name_plural - self.assertIn('ÚÑÍ¢ÓÐÉ', content) # title.verbose_name - self.assertIn('“Ðjáñgó”', content) # title.default - else: - # Meta.verbose_name - self.assertIn('\\xfa\\xf1\\xed\\xa9\\xf3\\xf0\\xe9 \\xb5\\xf3\\xf0\\xe9\\xf8', content) - # Meta.verbose_name_plural - self.assertIn('\\xfa\\xf1\\xed\\xa9\\xf3\\xf0\\xe9 \\xb5\\xf3\\xf0\\xe9\\xf8\\xdf', content) - self.assertIn('\\xda\\xd1\\xcd\\xa2\\xd3\\xd0\\xc9', content) # title.verbose_name - self.assertIn('\\u201c\\xd0j\\xe1\\xf1g\\xf3\\u201d', content) # title.default + self.assertIn('úñí©óðé µóðéø', content) # Meta.verbose_name + self.assertIn('úñí©óðé µóðéøß', content) # Meta.verbose_name_plural + self.assertIn('ÚÑÍ¢ÓÐÉ', content) # title.verbose_name + self.assertIn('“Ðjáñgó”', content) # title.default def test_makemigrations_order(self): """ diff --git a/tests/migrations/test_loader.py b/tests/migrations/test_loader.py index 6c91a50430..b06ff8f89c 100644 --- a/tests/migrations/test_loader.py +++ b/tests/migrations/test_loader.py @@ -1,5 +1,3 @@ -from unittest import skipIf - from django.db import connection, connections from django.db.migrations.exceptions import ( AmbiguityError, InconsistentMigrationHistory, NodeNotFoundError, @@ -7,7 +5,6 @@ from django.db.migrations.exceptions import ( from django.db.migrations.loader import MigrationLoader from django.db.migrations.recorder import MigrationRecorder from django.test import TestCase, modify_settings, override_settings -from django.utils import six class RecorderTests(TestCase): @@ -170,7 +167,6 @@ class LoaderTests(TestCase): "App with migrations module file not in unmigrated apps." ) - @skipIf(six.PY2, "PY2 doesn't load empty dirs.") def test_load_empty_dir(self): with override_settings(MIGRATION_MODULES={"migrations": "migrations.faulty_migrations.namespace"}): loader = MigrationLoader(connection) diff --git a/tests/migrations/test_writer.py b/tests/migrations/test_writer.py index c03d8cfd38..b926757920 100644 --- a/tests/migrations/test_writer.py +++ b/tests/migrations/test_writer.py @@ -47,12 +47,6 @@ class Money(decimal.Decimal): ) -class TestModel1(object): - def upload_to(self): - return "somewhere dynamic" - thing = models.FileField(upload_to=upload_to) - - class OperationWriterTests(SimpleTestCase): def test_empty_signature(self): @@ -486,15 +480,6 @@ class WriterTests(SimpleTestCase): self.assertEqual(string, 'range') self.assertEqual(imports, set()) - @unittest.skipUnless(six.PY2, "Only applies on Python 2") - def test_serialize_direct_function_reference(self): - """ - Ticket #22436: You cannot use a function straight from its body - (e.g. define the method and use it in the same body) - """ - with self.assertRaises(ValueError): - self.serialize_round_trip(TestModel1.thing) - def test_serialize_local_function_reference(self): """ Neither py2 or py3 can serialize a reference in a local scope. diff --git a/tests/model_fields/test_promises.py b/tests/model_fields/test_promises.py index bf5258cddf..0dcb1abf3b 100644 --- a/tests/model_fields/test_promises.py +++ b/tests/model_fields/test_promises.py @@ -1,13 +1,12 @@ import datetime -import unittest from decimal import Decimal from django.db.models.fields import ( - AutoField, BigIntegerField, BinaryField, BooleanField, CharField, - DateField, DateTimeField, DecimalField, EmailField, FilePathField, - FloatField, GenericIPAddressField, IntegerField, IPAddressField, - NullBooleanField, PositiveIntegerField, PositiveSmallIntegerField, - SlugField, SmallIntegerField, TextField, TimeField, URLField, + AutoField, BinaryField, BooleanField, CharField, DateField, DateTimeField, + DecimalField, EmailField, FilePathField, FloatField, GenericIPAddressField, + IntegerField, IPAddressField, NullBooleanField, PositiveIntegerField, + PositiveSmallIntegerField, SlugField, SmallIntegerField, TextField, + TimeField, URLField, ) from django.db.models.fields.files import FileField, ImageField from django.test import SimpleTestCase @@ -21,11 +20,6 @@ class PromiseTest(SimpleTestCase): lazy_func = lazy(lambda: 1, int) self.assertIsInstance(AutoField(primary_key=True).get_prep_value(lazy_func()), int) - @unittest.skipIf(six.PY3, 'Python 3 has no `long` type.') - def test_BigIntegerField(self): - lazy_func = lazy(lambda: long(9999999999999999999), long) # NOQA: long undefined on PY3 - self.assertIsInstance(BigIntegerField().get_prep_value(lazy_func()), long) # NOQA - def test_BinaryField(self): lazy_func = lazy(lambda: b'', bytes) self.assertIsInstance(BinaryField().get_prep_value(lazy_func()), bytes) diff --git a/tests/project_template/test_settings.py b/tests/project_template/test_settings.py index a0047dd836..5623df320b 100644 --- a/tests/project_template/test_settings.py +++ b/tests/project_template/test_settings.py @@ -1,18 +1,11 @@ import os import shutil -import unittest from django import conf from django.test import TestCase -from django.utils import six from django.utils._os import upath -@unittest.skipIf( - six.PY2, - 'Python 2 cannot import the project template because ' - 'django/conf/project_template doesn\'t have an __init__.py file.' -) class TestStartProjectSettings(TestCase): def setUp(self): # Ensure settings.py exists diff --git a/tests/queries/tests.py b/tests/queries/tests.py index e2dd86bac1..ed323be154 100644 --- a/tests/queries/tests.py +++ b/tests/queries/tests.py @@ -11,7 +11,6 @@ from django.db.models.sql.constants import LOUTER from django.db.models.sql.where import NothingNode, WhereNode from django.test import TestCase, skipUnlessDBFeature from django.test.utils import CaptureQueriesContext -from django.utils import six from django.utils.six.moves import range from .models import ( @@ -406,7 +405,7 @@ class Queries1Tests(TestCase): local_recursion_limit = 127 msg = 'Maximum recursion depth exceeded: too many subqueries.' with self.assertRaisesMessage(RuntimeError, msg): - for i in six.moves.range(local_recursion_limit * 2): + for i in range(local_recursion_limit * 2): x = Tag.objects.filter(pk__in=x) def test_reasonable_number_of_subq_aliases(self): @@ -2249,25 +2248,6 @@ class QuerySetSupportsPythonIdioms(TestCase): ] ) - @unittest.skipUnless(six.PY2, "Python 2 only -- Python 3 doesn't have longs.") - def test_slicing_works_with_longs(self): - # NOQA: long undefined on PY3 - self.assertEqual(self.get_ordered_articles()[long(0)].name, 'Article 1') # NOQA - self.assertQuerysetEqual(self.get_ordered_articles()[long(1):long(3)], # NOQA - ["", ""]) - self.assertQuerysetEqual( - self.get_ordered_articles()[::long(2)], [ # NOQA - "", - "", - "", - "" - ] - ) - - # And can be mixed with ints. - self.assertQuerysetEqual(self.get_ordered_articles()[1:long(3)], # NOQA - ["", ""]) - def test_slicing_without_step_is_lazy(self): with self.assertNumQueries(0): self.get_ordered_articles()[0:5] @@ -2965,8 +2945,6 @@ class QuerySetExceptionTests(TestCase): def test_invalid_order_by(self): msg = "Invalid order_by arguments: ['*']" - if six.PY2: - msg = msg.replace("[", "[u") with self.assertRaisesMessage(FieldError, msg): list(Article.objects.order_by('*')) diff --git a/tests/queryset_pickle/models.py b/tests/queryset_pickle/models.py index 5e60963272..4faad0175b 100644 --- a/tests/queryset_pickle/models.py +++ b/tests/queryset_pickle/models.py @@ -1,7 +1,6 @@ import datetime from django.db import DJANGO_VERSION_PICKLE_KEY, models -from django.utils import six from django.utils.translation import ugettext_lazy as _ @@ -45,9 +44,7 @@ class Happening(models.Model): when = models.DateTimeField(blank=True, default=datetime.datetime.now) name = models.CharField(blank=True, max_length=100, default="test") number1 = models.IntegerField(blank=True, default=standalone_number) - if six.PY3: - # default serializable on Python 3 only - number2 = models.IntegerField(blank=True, default=Numbers.get_static_number) + number2 = models.IntegerField(blank=True, default=Numbers.get_static_number) class Container(object): diff --git a/tests/queryset_pickle/tests.py b/tests/queryset_pickle/tests.py index 9333e96fc3..ac5174051f 100644 --- a/tests/queryset_pickle/tests.py +++ b/tests/queryset_pickle/tests.py @@ -1,10 +1,8 @@ import datetime import pickle -import unittest from django.db import models from django.test import TestCase -from django.utils import six from django.utils.version import get_version from .models import Container, Event, Group, Happening, M2MModel @@ -33,7 +31,6 @@ class PickleabilityTestCase(TestCase): def test_standalone_method_as_default(self): self.assert_pickles(Happening.objects.filter(number1=1)) - @unittest.skipIf(six.PY2, "Field doesn't exist on Python 2.") def test_staticmethod_as_default(self): self.assert_pickles(Happening.objects.filter(number2=1)) diff --git a/tests/requests/tests.py b/tests/requests/tests.py index c5a148ffbb..09b08540f4 100644 --- a/tests/requests/tests.py +++ b/tests/requests/tests.py @@ -12,7 +12,6 @@ from django.http.request import split_domain_port from django.test import RequestFactory, SimpleTestCase, override_settings from django.test.client import FakePayload from django.test.utils import freeze_time, str_prefix -from django.utils import six from django.utils.encoding import force_str from django.utils.http import cookie_date, urlencode from django.utils.six.moves import http_cookies @@ -168,9 +167,8 @@ class RequestsTests(SimpleTestCase): def test_wsgirequest_path_info(self): def wsgi_str(path_info, encoding='utf-8'): - path_info = path_info.encode(encoding) # Actual URL sent by the browser (bytestring) - if six.PY3: - path_info = path_info.decode('iso-8859-1') # Value in the WSGI environ dict (native string) + path_info = path_info.encode(encoding) # Actual URL sent by the browser (bytestring) + path_info = path_info.decode('iso-8859-1') # Value in the WSGI environ dict (native string) return path_info # Regression for #19468 request = WSGIRequest({'PATH_INFO': wsgi_str("/سلام/"), 'REQUEST_METHOD': 'get', 'wsgi.input': BytesIO(b'')}) @@ -583,7 +581,7 @@ class RequestsTests(SimpleTestCase): request = WSGIRequest({ 'REQUEST_METHOD': 'GET', 'wsgi.input': '', - 'QUERY_STRING': b'name=Hello%20G%C3%BCnter' if six.PY2 else 'name=Hello%20G%C3%BCnter' + 'QUERY_STRING': 'name=Hello%20G%C3%BCnter', }) self.assertEqual(request.GET, {'name': ['Hello Günter']}) request.encoding = 'iso-8859-16' diff --git a/tests/signing/tests.py b/tests/signing/tests.py index c6145d09cb..bc838d015f 100644 --- a/tests/signing/tests.py +++ b/tests/signing/tests.py @@ -3,7 +3,6 @@ import datetime from django.core import signing from django.test import SimpleTestCase from django.test.utils import freeze_time -from django.utils import six from django.utils.encoding import force_str @@ -45,8 +44,6 @@ class TestSigner(SimpleTestCase): 'jkw osanteuh ,rcuh nthu aou oauh ,ud du', '\u2019', ] - if six.PY2: - examples.append(b'a byte string') for example in examples: signed = signer.sign(example) self.assertIsInstance(signed, str) @@ -76,8 +73,6 @@ class TestSigner(SimpleTestCase): 'a unicode string \u2019', {'a': 'dictionary'}, ] - if six.PY2: - objects.append(b'a byte string') for o in objects: self.assertNotEqual(o, signing.dumps(o)) self.assertEqual(o, signing.loads(signing.dumps(o))) diff --git a/tests/staticfiles_tests/test_management.py b/tests/staticfiles_tests/test_management.py index ad608ea24b..e8c00eb3f5 100644 --- a/tests/staticfiles_tests/test_management.py +++ b/tests/staticfiles_tests/test_management.py @@ -183,8 +183,7 @@ class TestInteractiveMessages(CollectionTestCase): @staticmethod def mock_input(stdout): def _input(msg): - # Python 2 reads bytes from the console output, use bytes for the StringIO - stdout.write(msg.encode('utf-8') if six.PY2 else msg) + stdout.write(msg) return 'yes' return _input diff --git a/tests/str/models.py b/tests/str/models.py index 12f0247570..d2f7a45c20 100644 --- a/tests/str/models.py +++ b/tests/str/models.py @@ -1,31 +1,16 @@ """ -Adding __str__() or __unicode__() to models +Adding __str__() to models -Although it's not a strict requirement, each model should have a -``_str__()`` or ``__unicode__()`` method to return a "human-readable" -representation of the object. Do this not only for your own sanity when dealing -with the interactive prompt, but also because objects' representations are used -throughout Django's automatically-generated admin. - -Normally, you should write ``__unicode__()`` method, since this will work for -all field types (and Django will automatically provide an appropriate -``__str__()`` method). However, you can write a ``__str__()`` method directly, -if you prefer. You must be careful to encode the results correctly, though. +Although it's not a strict requirement, each model should have a ``_str__()`` +method to return a "human-readable" representation of the object. Do this not +only for your own sanity when dealing with the interactive prompt, but also +because objects' representations are used throughout Django's +automatically-generated admin. """ from django.db import models -class Article(models.Model): - headline = models.CharField(max_length=100) - pub_date = models.DateTimeField() - - def __str__(self): - # Caution: this is only safe if you are certain that headline will be - # in ASCII. - return self.headline - - class InternationalArticle(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateTimeField() diff --git a/tests/str/tests.py b/tests/str/tests.py index c4dd1d6b26..2d82c03ebf 100644 --- a/tests/str/tests.py +++ b/tests/str/tests.py @@ -1,25 +1,14 @@ import datetime -from unittest import skipIf from django.db import models from django.test import TestCase from django.test.utils import isolate_apps -from django.utils import six -from .models import Article, InternationalArticle +from .models import InternationalArticle class SimpleTests(TestCase): - @skipIf(six.PY3, "tests a __str__ method returning unicode under Python 2") - def test_basic(self): - a = Article.objects.create( - headline=b'Parrot programs in Python', - pub_date=datetime.datetime(2005, 7, 28) - ) - self.assertEqual(str(a), str('Parrot programs in Python')) - self.assertEqual(repr(a), str('')) - def test_international(self): a = InternationalArticle.objects.create( headline='Girl wins €12.500 in lottery', diff --git a/tests/template_tests/syntax_tests/test_width_ratio.py b/tests/template_tests/syntax_tests/test_width_ratio.py index 8206b83c58..bec7d93a4c 100644 --- a/tests/template_tests/syntax_tests/test_width_ratio.py +++ b/tests/template_tests/syntax_tests/test_width_ratio.py @@ -1,6 +1,5 @@ from django.template import TemplateSyntaxError from django.test import SimpleTestCase -from django.utils import six from ..utils import setup @@ -36,11 +35,10 @@ class WidthRatioTagTests(SimpleTestCase): @setup({'widthratio06': '{% widthratio a b 100 %}'}) def test_widthratio06(self): """ - 62.5 should round to 63 on Python 2 and 62 on Python 3 - See http://docs.python.org/py3k/whatsnew/3.0.html + 62.5 should round to 62 """ output = self.engine.render_to_string('widthratio06', {'a': 50, 'b': 80}) - self.assertEqual(output, '62' if six.PY3 else '63') + self.assertEqual(output, '62') @setup({'widthratio07': '{% widthratio a b 100 %}'}) def test_widthratio07(self): diff --git a/tests/template_tests/test_custom.py b/tests/template_tests/test_custom.py index df8ea17fb0..03d4308937 100644 --- a/tests/template_tests/test_custom.py +++ b/tests/template_tests/test_custom.py @@ -1,12 +1,10 @@ import os -from unittest import skipUnless from django.template import Context, Engine, TemplateSyntaxError from django.template.base import Node from django.template.library import InvalidTemplateLibrary from django.test import SimpleTestCase from django.test.utils import extend_sys_path -from django.utils import six from .templatetags import custom, inclusion from .utils import ROOT @@ -344,7 +342,6 @@ class TemplateTagLoadingTests(SimpleTestCase): }) engine.from_string(ttext) - @skipUnless(six.PY3, "Python 3 only -- Python 2 doesn't have annotations.") def test_load_annotated_function(self): Engine(libraries={ 'annotated_tag_function': 'template_tests.annotated_tag_function', diff --git a/tests/template_tests/test_loaders.py b/tests/template_tests/test_loaders.py index 6e9b6eb453..c36e1ba6be 100644 --- a/tests/template_tests/test_loaders.py +++ b/tests/template_tests/test_loaders.py @@ -7,7 +7,6 @@ from contextlib import contextmanager from django.template import TemplateDoesNotExist from django.template.engine import Engine from django.test import SimpleTestCase, override_settings -from django.utils import six from django.utils.functional import lazystr from .utils import TEMPLATE_DIR @@ -64,7 +63,6 @@ class CachedLoaderTests(SimpleTestCase): self.assertIsInstance(e, TemplateDoesNotExist) self.assertEqual(e.args[0], 'debug-template-missing.html') - @unittest.skipIf(six.PY2, "Python 2 doesn't set extra exception attributes") def test_cached_exception_no_traceback(self): """ When a TemplateDoesNotExist instance is cached, the cached instance diff --git a/tests/template_tests/test_nodelist.py b/tests/template_tests/test_nodelist.py index 042702e3c0..b69e7c063b 100644 --- a/tests/template_tests/test_nodelist.py +++ b/tests/template_tests/test_nodelist.py @@ -2,7 +2,6 @@ from unittest import TestCase from django.template import Context, Engine from django.template.base import TextNode, VariableNode -from django.utils import six class NodelistTest(TestCase): @@ -38,13 +37,11 @@ class TextNodeTest(TestCase): def test_textnode_repr(self): engine = Engine() for temptext, reprtext in [ - ("Hello, world!", ""), - ("One\ntwo.", ""), + ("Hello, world!", ""), + ("One\ntwo.", ""), ]: template = engine.from_string(temptext) texts = template.nodelist.get_nodes_by_type(TextNode) - if six.PY3: - reprtext = reprtext.replace("u'", "'") self.assertEqual(repr(texts[0]), reprtext) diff --git a/tests/test_runner/test_debug_sql.py b/tests/test_runner/test_debug_sql.py index e230066100..c6b9b01dbc 100644 --- a/tests/test_runner/test_debug_sql.py +++ b/tests/test_runner/test_debug_sql.py @@ -5,7 +5,6 @@ from django.db import connection from django.test import TestCase from django.test.runner import DiscoverRunner from django.utils import six -from django.utils.encoding import force_text from .models import Person @@ -43,8 +42,6 @@ class TestDebugSQL(unittest.TestCase): ).run(suite) runner.teardown_databases(old_config) - if six.PY2: - stream.buflist = [force_text(x) for x in stream.buflist] return stream.getvalue() def test_output_normal(self): diff --git a/tests/test_runner/test_parallel.py b/tests/test_runner/test_parallel.py index b888dc62af..08731f68eb 100644 --- a/tests/test_runner/test_parallel.py +++ b/tests/test_runner/test_parallel.py @@ -2,7 +2,6 @@ import unittest from django.test import SimpleTestCase from django.test.runner import RemoteTestResult -from django.utils import six try: import tblib @@ -28,7 +27,6 @@ class ParallelTestRunnerTest(SimpleTestCase): parallel. """ - @unittest.skipUnless(six.PY3, 'subtests were added in Python 3.4') def test_subtest(self): """ Passing subtests work. @@ -61,12 +59,10 @@ class RemoteTestResultTest(SimpleTestCase): result._confirm_picklable(picklable_error) msg = '__init__() missing 1 required positional argument' - if six.PY2: - msg = '__init__() takes exactly 2 arguments (1 given)' with self.assertRaisesMessage(TypeError, msg): result._confirm_picklable(not_unpicklable_error) - @unittest.skipUnless(six.PY3 and tblib is not None, 'requires tblib to be installed') + @unittest.skipUnless(tblib is not None, 'requires tblib to be installed') def test_add_failing_subtests(self): """ Failing subtests are added correctly using addSubTest(). diff --git a/tests/urlpatterns_reverse/tests.py b/tests/urlpatterns_reverse/tests.py index a84e5bfb24..157479a579 100644 --- a/tests/urlpatterns_reverse/tests.py +++ b/tests/urlpatterns_reverse/tests.py @@ -3,7 +3,6 @@ Unit tests for reverse URL lookups. """ import sys import threading -import unittest from admin_scripts.tests import AdminScriptTestCase @@ -430,7 +429,7 @@ class ResolverTests(SimpleTestCase): [{'type': RegexURLResolver}, {'type': RegexURLPattern, 'name': None}], [{'type': RegexURLResolver}, {'type': RegexURLResolver}], ] - with self.assertRaisesMessage(Resolver404, b'tried' if six.PY2 else 'tried') as cm: + with self.assertRaisesMessage(Resolver404, 'tried') as cm: resolve('/included/non-existent-url', urlconf=urls) e = cm.exception # make sure we at least matched the root ('/') url resolver: @@ -463,7 +462,6 @@ class ResolverTests(SimpleTestCase): self.assertTrue(resolver._is_callback('urlpatterns_reverse.nested_urls.View3')) self.assertFalse(resolver._is_callback('urlpatterns_reverse.nested_urls.blub')) - @unittest.skipIf(six.PY2, "Python 2 doesn't support __qualname__.") def test_view_detail_as_method(self): # Views which have a class name as part of their path. resolver = get_resolver('urlpatterns_reverse.method_view_urls') @@ -503,11 +501,6 @@ class ReverseLazyTest(TestCase): 'Some URL: %s' % reverse_lazy('some-login-page'), 'Some URL: /login/' ) - if six.PY2: - self.assertEqual( - b'Some URL: %s' % reverse_lazy('some-login-page'), - 'Some URL: /login/' - ) class ReverseLazySettingsTest(AdminScriptTestCase): diff --git a/tests/utils_tests/test_encoding.py b/tests/utils_tests/test_encoding.py index 1d6976fc77..b4e7b9c0ac 100644 --- a/tests/utils_tests/test_encoding.py +++ b/tests/utils_tests/test_encoding.py @@ -21,10 +21,8 @@ class TestEncodingUtils(unittest.TestCase): __unicode__ = __str__ - # str(s) raises a TypeError on python 3 if the result is not a text type. - # python 2 fails when it tries converting from str to unicode (via ASCII). - exception = TypeError if six.PY3 else UnicodeError - with self.assertRaises(exception): + # str(s) raises a TypeError if the result is not a text type. + with self.assertRaises(TypeError): force_text(MyString()) def test_force_text_lazy(self): @@ -47,26 +45,15 @@ class TestEncodingUtils(unittest.TestCase): def test_smart_text(self): class Test: - if six.PY3: - def __str__(self): - return 'ŠĐĆŽćžšđ' - else: - def __str__(self): - return 'ŠĐĆŽćžšđ'.encode('utf-8') + def __str__(self): + return 'ŠĐĆŽćžšđ' class TestU: - if six.PY3: - def __str__(self): - return 'ŠĐĆŽćžšđ' + def __str__(self): + return 'ŠĐĆŽćžšđ' - def __bytes__(self): - return b'Foo' - else: - def __str__(self): - return b'Foo' - - def __unicode__(self): - return '\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111' + def __bytes__(self): + return b'Foo' self.assertEqual(smart_text(Test()), '\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111') self.assertEqual(smart_text(TestU()), '\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111') diff --git a/tests/utils_tests/test_functional.py b/tests/utils_tests/test_functional.py index f7b12724af..0ca534445f 100644 --- a/tests/utils_tests/test_functional.py +++ b/tests/utils_tests/test_functional.py @@ -38,18 +38,11 @@ class FunctionalTestCase(unittest.TestCase): def test_lazy_object_to_string(self): class Klazz(object): - if six.PY3: - def __str__(self): - return "Î am ā Ǩlâzz." + def __str__(self): + return "Î am ā Ǩlâzz." - def __bytes__(self): - return b"\xc3\x8e am \xc4\x81 binary \xc7\xa8l\xc3\xa2zz." - else: - def __unicode__(self): - return "Î am ā Ǩlâzz." - - def __str__(self): - return b"\xc3\x8e am \xc4\x81 binary \xc7\xa8l\xc3\xa2zz." + def __bytes__(self): + return b"\xc3\x8e am \xc4\x81 binary \xc7\xa8l\xc3\xa2zz." t = lazy(lambda: Klazz(), Klazz)() self.assertEqual(six.text_type(t), "Î am ā Ǩlâzz.") diff --git a/tests/utils_tests/test_glob.py b/tests/utils_tests/test_glob.py deleted file mode 100644 index 5cd7fbbe28..0000000000 --- a/tests/utils_tests/test_glob.py +++ /dev/null @@ -1,13 +0,0 @@ -from django.test import SimpleTestCase -from django.utils.glob import glob_escape - - -class TestUtilsGlob(SimpleTestCase): - def test_glob_escape(self): - filename = '/my/file?/name[with special chars*' - expected = '/my/file[?]/name[[]with special chars[*]' - filename_b = b'/my/file?/name[with special chars*' - expected_b = b'/my/file[?]/name[[]with special chars[*]' - - self.assertEqual(glob_escape(filename), expected) - self.assertEqual(glob_escape(filename_b), expected_b) diff --git a/tests/utils_tests/test_html.py b/tests/utils_tests/test_html.py index ec818318b4..8f887fd011 100644 --- a/tests/utils_tests/test_html.py +++ b/tests/utils_tests/test_html.py @@ -2,7 +2,7 @@ import os from datetime import datetime from django.test import SimpleTestCase -from django.utils import html, safestring, six +from django.utils import html, safestring from django.utils._os import upath from django.utils.encoding import force_text from django.utils.functional import lazystr @@ -168,12 +168,8 @@ class TestUtilsHtml(SimpleTestCase): def test_html_safe(self): @html.html_safe class HtmlClass(object): - if six.PY2: - def __unicode__(self): - return "

I'm a html class!

" - else: - def __str__(self): - return "

I'm a html class!

" + def __str__(self): + return "

I'm a html class!

" html_obj = HtmlClass() self.assertTrue(hasattr(HtmlClass, '__html__')) @@ -181,34 +177,19 @@ class TestUtilsHtml(SimpleTestCase): self.assertEqual(force_text(html_obj), html_obj.__html__()) def test_html_safe_subclass(self): - if six.PY2: - class BaseClass(object): - def __html__(self): - # defines __html__ on its own - return 'some html content' + class BaseClass(object): + def __html__(self): + # defines __html__ on its own + return 'some html content' - def __unicode__(self): - return 'some non html content' + def __str__(self): + return 'some non html content' - @html.html_safe - class Subclass(BaseClass): - def __unicode__(self): - # overrides __unicode__ and is marked as html_safe - return 'some html safe content' - else: - class BaseClass(object): - def __html__(self): - # defines __html__ on its own - return 'some html content' - - def __str__(self): - return 'some non html content' - - @html.html_safe - class Subclass(BaseClass): - def __str__(self): - # overrides __str__ and is marked as html_safe - return 'some html safe content' + @html.html_safe + class Subclass(BaseClass): + def __str__(self): + # overrides __str__ and is marked as html_safe + return 'some html safe content' subclass_obj = Subclass() self.assertEqual(force_text(subclass_obj), subclass_obj.__html__()) @@ -222,8 +203,7 @@ class TestUtilsHtml(SimpleTestCase): return "

I'm a html class!

" def test_html_safe_doesnt_define_str(self): - method_name = '__unicode__()' if six.PY2 else '__str__()' - msg = "can't apply @html_safe to HtmlClass because it doesn't define %s." % method_name + msg = "can't apply @html_safe to HtmlClass because it doesn't define __str__()." with self.assertRaisesMessage(ValueError, msg): @html.html_safe class HtmlClass(object): diff --git a/tests/utils_tests/test_http.py b/tests/utils_tests/test_http.py index 30a8846021..281fb77fbb 100644 --- a/tests/utils_tests/test_http.py +++ b/tests/utils_tests/test_http.py @@ -1,9 +1,8 @@ -import sys import unittest from datetime import datetime from django.test import ignore_warnings -from django.utils import http, six +from django.utils import http from django.utils.datastructures import MultiValueDict from django.utils.deprecation import RemovedInDjango21Warning @@ -51,15 +50,10 @@ class TestUtilsHttp(unittest.TestCase): # reciprocity works for n in [0, 1, 1000, 1000000]: self.assertEqual(n, http.base36_to_int(http.int_to_base36(n))) - if six.PY2: - self.assertEqual(sys.maxint, http.base36_to_int(http.int_to_base36(sys.maxint))) # bad input with self.assertRaises(ValueError): http.int_to_base36(-1) - if six.PY2: - with self.assertRaises(ValueError): - http.int_to_base36(sys.maxint + 1) for n in ['1', 'foo', {1: 2}, (1, 2, 3), 3.141]: with self.assertRaises(TypeError): http.int_to_base36(n) @@ -132,16 +126,6 @@ class TestUtilsHttp(unittest.TestCase): "%s should be allowed" % good_url, ) - if six.PY2: - # Check binary URLs, regression tests for #26308 - self.assertTrue( - http.is_safe_url(b'https://testserver/', allowed_hosts={'testserver'}), - "binary URLs should be allowed on Python 2" - ) - self.assertFalse(http.is_safe_url(b'\x08//example.com', allowed_hosts={'testserver'})) - self.assertTrue(http.is_safe_url('àview/'.encode('utf-8'), allowed_hosts={'testserver'})) - self.assertFalse(http.is_safe_url('àview'.encode('latin-1'), allowed_hosts={'testserver'})) - # Valid basic auth credentials are allowed. self.assertTrue(http.is_safe_url(r'http://user:pass@testserver/', allowed_hosts={'user:pass@testserver'})) # A path without host is allowed. diff --git a/tests/utils_tests/test_simplelazyobject.py b/tests/utils_tests/test_simplelazyobject.py index 8501b89e92..a83c78bf4c 100644 --- a/tests/utils_tests/test_simplelazyobject.py +++ b/tests/utils_tests/test_simplelazyobject.py @@ -2,7 +2,6 @@ import pickle from django.contrib.auth.models import User from django.test import TestCase -from django.utils import six from django.utils.functional import SimpleLazyObject @@ -21,9 +20,3 @@ class TestUtilsSimpleLazyObjectDjangoTestCase(TestCase): pickle.dumps(x, 0) pickle.dumps(x, 1) pickle.dumps(x, 2) - - if six.PY2: - import cPickle - - # This would fail with "TypeError: expected string or Unicode object, NoneType found". - cPickle.dumps(x) diff --git a/tests/view_tests/tests/py3_test_debug.py b/tests/view_tests/tests/py3_test_debug.py deleted file mode 100644 index 30201bae53..0000000000 --- a/tests/view_tests/tests/py3_test_debug.py +++ /dev/null @@ -1,45 +0,0 @@ -""" -Since this file contains Python 3 specific syntax, it's named without a test_ -prefix so the test runner won't try to import it. Instead, the test class is -imported in test_debug.py, but only on Python 3. - -This filename is also in setup.cfg flake8 exclude since the Python 2 syntax -error (raise ... from ...) can't be silenced using NOQA. -""" -import sys - -from django.test import RequestFactory, TestCase -from django.views.debug import ExceptionReporter - - -class Py3ExceptionReporterTests(TestCase): - - rf = RequestFactory() - - def test_reporting_of_nested_exceptions(self): - request = self.rf.get('/test_view/') - try: - try: - raise AttributeError('Top level') - except AttributeError as explicit: - try: - raise ValueError('Second exception') from explicit - except ValueError: - raise IndexError('Final exception') - except Exception: - # Custom exception handler, just pass it into ExceptionReporter - exc_type, exc_value, tb = sys.exc_info() - - explicit_exc = 'The above exception ({0}) was the direct cause of the following exception:' - implicit_exc = 'During handling of the above exception ({0}), another exception occurred:' - - reporter = ExceptionReporter(request, exc_type, exc_value, tb) - html = reporter.get_traceback_html() - # Both messages are twice on page -- one rendered as html, - # one as plain text (for pastebin) - self.assertEqual(2, html.count(explicit_exc.format("Top level"))) - self.assertEqual(2, html.count(implicit_exc.format("Second exception"))) - - text = reporter.get_traceback_text() - self.assertIn(explicit_exc.format("Top level"), text) - self.assertIn(implicit_exc.format("Second exception"), text) diff --git a/tests/view_tests/tests/test_debug.py b/tests/view_tests/tests/test_debug.py index 19ab456ba5..1d8397c5ef 100644 --- a/tests/view_tests/tests/test_debug.py +++ b/tests/view_tests/tests/test_debug.py @@ -4,7 +4,6 @@ import os import re import sys import tempfile -from unittest import skipIf from django.conf.urls import url from django.core import mail @@ -30,9 +29,6 @@ from ..views import ( sensitive_method_view, sensitive_view, ) -if six.PY3: - from .py3_test_debug import Py3ExceptionReporterTests # NOQA - PY36 = sys.version_info >= (3, 6) @@ -352,6 +348,34 @@ class ExceptionReporterTests(SimpleTestCase): self.assertIn('

Request information

', html) self.assertNotIn('

Request data not supplied

', html) + def test_reporting_of_nested_exceptions(self): + request = self.rf.get('/test_view/') + try: + try: + raise AttributeError('Top level') + except AttributeError as explicit: + try: + raise ValueError('Second exception') from explicit + except ValueError: + raise IndexError('Final exception') + except Exception: + # Custom exception handler, just pass it into ExceptionReporter + exc_type, exc_value, tb = sys.exc_info() + + explicit_exc = 'The above exception ({0}) was the direct cause of the following exception:' + implicit_exc = 'During handling of the above exception ({0}), another exception occurred:' + + reporter = ExceptionReporter(request, exc_type, exc_value, tb) + html = reporter.get_traceback_html() + # Both messages are twice on page -- one rendered as html, + # one as plain text (for pastebin) + self.assertEqual(2, html.count(explicit_exc.format("Top level"))) + self.assertEqual(2, html.count(implicit_exc.format("Second exception"))) + + text = reporter.get_traceback_text() + self.assertIn(explicit_exc.format("Top level"), text) + self.assertIn(implicit_exc.format("Second exception"), text) + def test_request_and_message(self): "A message can be provided in addition to a request" request = self.rf.get('/test_view/') @@ -426,11 +450,10 @@ class ExceptionReporterTests(SimpleTestCase): self.assertEqual(len(html) // 1024 // 128, 0) # still fit in 128Kb self.assertIn('<trimmed %d bytes string>' % (large + repr_of_str_adds,), html) - @skipIf(six.PY2, 'Bug manifests on PY3 only') def test_unfrozen_importlib(self): """ importlib is not a frozen app, but its loader thinks it's frozen which - results in an ImportError on Python 3. Refs #21443. + results in an ImportError. Refs #21443. """ try: request = self.rf.get('/test_view/') @@ -478,10 +501,7 @@ class ExceptionReporterTests(SimpleTestCase): An exception report can be generated for requests with 'items' in request GET, POST, FILES, or COOKIES QueryDicts. """ - if six.PY3: - value = 'items
'Oops'
' - else: - value = 'items
u'Oops'
' + value = 'items
'Oops'
' # GET request = self.rf.get('/test_view/?items=Oops') reporter = ExceptionReporter(request, None, None, None) @@ -597,20 +617,16 @@ class PlainTextReportTests(SimpleTestCase): An exception report can be generated for requests with 'items' in request GET, POST, FILES, or COOKIES QueryDicts. """ - if six.PY3: - value = "items = 'Oops'" - else: - value = "items = u'Oops'" # GET request = self.rf.get('/test_view/?items=Oops') reporter = ExceptionReporter(request, None, None, None) text = reporter.get_traceback_text() - self.assertIn(value, text) + self.assertIn("items = 'Oops'", text) # POST request = self.rf.post('/test_view/', data={'items': 'Oops'}) reporter = ExceptionReporter(request, None, None, None) text = reporter.get_traceback_text() - self.assertIn(value, text) + self.assertIn("items = 'Oops'", text) # FILES fp = six.StringIO('filecontent') request = self.rf.post('/test_view/', data={'name': 'filename', 'items': fp}) diff --git a/tests/view_tests/tests/test_i18n.py b/tests/view_tests/tests/test_i18n.py index fc7be41594..d330b60ff7 100644 --- a/tests/view_tests/tests/test_i18n.py +++ b/tests/view_tests/tests/test_i18n.py @@ -8,7 +8,6 @@ from django.test import ( ) from django.test.selenium import SeleniumTestCase from django.urls import reverse -from django.utils import six from django.utils._os import upath from django.utils.translation import ( LANGUAGE_SESSION_KEY, get_language, override, @@ -193,10 +192,7 @@ class JsI18NTests(SimpleTestCase): for lang_code in ['es', 'fr', 'ru']: with override(lang_code): catalog = gettext.translation('djangojs', locale_dir, [lang_code]) - if six.PY3: - trans_txt = catalog.gettext('this is to be translated') - else: - trans_txt = catalog.ugettext('this is to be translated') + trans_txt = catalog.gettext('this is to be translated') response = self.client.get('/jsi18n/') # response content must include a line like: # "this is to be translated":