summaryrefslogtreecommitdiff
path: root/django/core
diff options
context:
space:
mode:
authorAndrew Godwin <andrew@aeracode.org>2012-09-05 09:39:03 -0400
committerAndrew Godwin <andrew@aeracode.org>2012-09-05 09:39:03 -0400
commitb546e7eb633022ee1962570387f22fb2bcea46ed (patch)
treef87f4a2d68fb66afae39148fa35489930710b623 /django/core
parentcd583d6dbd222ae61331a6965b0e1fc86c974c50 (diff)
parentcff911f4ba3b3e6393c58da5131ce8b188a68f0c (diff)
Merge branch 'master' into schema-alteration
Diffstat (limited to 'django/core')
-rw-r--r--django/core/cache/backends/base.py6
-rw-r--r--django/core/cache/backends/db.py7
-rw-r--r--django/core/cache/backends/filebased.py3
-rw-r--r--django/core/cache/backends/memcached.py5
-rw-r--r--django/core/context_processors.py11
-rw-r--r--django/core/files/base.py18
-rw-r--r--django/core/files/move.py2
-rw-r--r--django/core/files/storage.py11
-rw-r--r--django/core/files/uploadedfile.py4
-rw-r--r--django/core/files/uploadhandler.py4
-rw-r--r--django/core/handlers/base.py25
-rw-r--r--django/core/handlers/wsgi.py4
-rw-r--r--django/core/mail/message.py7
-rw-r--r--django/core/management/__init__.py7
-rw-r--r--django/core/management/base.py4
-rw-r--r--django/core/management/commands/inspectdb.py116
-rw-r--r--django/core/management/commands/loaddata.py12
-rw-r--r--django/core/management/commands/makemessages.py60
-rw-r--r--django/core/management/commands/shell.py18
-rw-r--r--django/core/management/commands/syncdb.py2
-rw-r--r--django/core/management/sql.py5
-rw-r--r--django/core/management/templates.py6
-rw-r--r--django/core/management/validation.py4
-rw-r--r--django/core/serializers/base.py6
-rw-r--r--django/core/serializers/json.py8
-rw-r--r--django/core/serializers/pyyaml.py1
-rw-r--r--django/core/servers/basehttp.py10
-rw-r--r--django/core/signing.py47
-rw-r--r--django/core/urlresolvers.py40
-rw-r--r--django/core/validators.py15
30 files changed, 271 insertions, 197 deletions
diff --git a/django/core/cache/backends/base.py b/django/core/cache/backends/base.py
index d527e44d8b..06e8952bfb 100644
--- a/django/core/cache/backends/base.py
+++ b/django/core/cache/backends/base.py
@@ -1,9 +1,9 @@
"Base Cache class."
+from __future__ import unicode_literals
import warnings
from django.core.exceptions import ImproperlyConfigured, DjangoRuntimeWarning
-from django.utils.encoding import smart_bytes
from django.utils.importlib import import_module
class InvalidCacheBackendError(ImproperlyConfigured):
@@ -23,7 +23,7 @@ def default_key_func(key, key_prefix, version):
the `key_prefix'. KEY_FUNCTION can be used to specify an alternate
function with custom key making behavior.
"""
- return ':'.join([key_prefix, str(version), smart_bytes(key)])
+ return ':'.join([key_prefix, str(version), key])
def get_key_func(key_func):
"""
@@ -62,7 +62,7 @@ class BaseCache(object):
except (ValueError, TypeError):
self._cull_frequency = 3
- self.key_prefix = smart_bytes(params.get('KEY_PREFIX', ''))
+ self.key_prefix = params.get('KEY_PREFIX', '')
self.version = params.get('VERSION', 1)
self.key_func = get_key_func(params.get('KEY_FUNCTION', None))
diff --git a/django/core/cache/backends/db.py b/django/core/cache/backends/db.py
index f60b4e0cd1..348b03f733 100644
--- a/django/core/cache/backends/db.py
+++ b/django/core/cache/backends/db.py
@@ -12,6 +12,7 @@ from django.conf import settings
from django.core.cache.backends.base import BaseCache
from django.db import connections, router, transaction, DatabaseError
from django.utils import timezone
+from django.utils.encoding import force_bytes
class Options(object):
@@ -72,7 +73,7 @@ class DatabaseCache(BaseDatabaseCache):
transaction.commit_unless_managed(using=db)
return default
value = connections[db].ops.process_clob(row[1])
- return pickle.loads(base64.decodestring(value))
+ return pickle.loads(base64.b64decode(force_bytes(value)))
def set(self, key, value, timeout=None, version=None):
key = self.make_key(key, version=version)
@@ -103,7 +104,7 @@ class DatabaseCache(BaseDatabaseCache):
if num > self._max_entries:
self._cull(db, cursor, now)
pickled = pickle.dumps(value, pickle.HIGHEST_PROTOCOL)
- encoded = base64.encodestring(pickled).strip()
+ encoded = base64.b64encode(pickled).strip()
cursor.execute("SELECT cache_key, expires FROM %s "
"WHERE cache_key = %%s" % table, [key])
try:
@@ -166,7 +167,7 @@ class DatabaseCache(BaseDatabaseCache):
cursor.execute("SELECT COUNT(*) FROM %s" % table)
num = cursor.fetchone()[0]
if num > self._max_entries:
- cull_num = num / self._cull_frequency
+ cull_num = num // self._cull_frequency
cursor.execute(
connections[db].ops.cache_key_culling_sql() % table,
[cull_num])
diff --git a/django/core/cache/backends/filebased.py b/django/core/cache/backends/filebased.py
index 1170996a76..96194d458f 100644
--- a/django/core/cache/backends/filebased.py
+++ b/django/core/cache/backends/filebased.py
@@ -10,6 +10,7 @@ except ImportError:
import pickle
from django.core.cache.backends.base import BaseCache
+from django.utils.encoding import force_bytes
class FileBasedCache(BaseCache):
def __init__(self, dir, params):
@@ -136,7 +137,7 @@ class FileBasedCache(BaseCache):
Thus, a cache key of "foo" gets turnned into a file named
``{cache-dir}ac/bd/18db4cc2f85cedef654fccc4a4d8``.
"""
- path = hashlib.md5(key).hexdigest()
+ path = hashlib.md5(force_bytes(key)).hexdigest()
path = os.path.join(path[:2], path[2:4], path[4:])
return os.path.join(self._dir, path)
diff --git a/django/core/cache/backends/memcached.py b/django/core/cache/backends/memcached.py
index e7724f1b91..426a0a15c0 100644
--- a/django/core/cache/backends/memcached.py
+++ b/django/core/cache/backends/memcached.py
@@ -6,6 +6,7 @@ from threading import local
from django.core.cache.backends.base import BaseCache, InvalidCacheBackendError
from django.utils import six
+from django.utils.encoding import force_str
class BaseMemcachedCache(BaseCache):
def __init__(self, server, params, library, value_not_found_exception):
@@ -50,6 +51,10 @@ class BaseMemcachedCache(BaseCache):
timeout += int(time.time())
return int(timeout)
+ def make_key(self, key, version=None):
+ # Python 2 memcache requires the key to be a byte string.
+ return force_str(super(BaseMemcachedCache, self).make_key(key, version))
+
def add(self, key, value, timeout=0, version=None):
key = self.make_key(key, version=version)
return self._cache.add(key, value, self._get_memcache_timeout(timeout))
diff --git a/django/core/context_processors.py b/django/core/context_processors.py
index a503270cf4..ca1ac68f55 100644
--- a/django/core/context_processors.py
+++ b/django/core/context_processors.py
@@ -6,12 +6,15 @@ and returns a dictionary to add to the context.
These are referenced from the setting TEMPLATE_CONTEXT_PROCESSORS and used by
RequestContext.
"""
+from __future__ import unicode_literals
from django.conf import settings
from django.middleware.csrf import get_token
-from django.utils.encoding import smart_bytes
+from django.utils import six
+from django.utils.encoding import smart_text
from django.utils.functional import lazy
+
def csrf(request):
"""
Context processor that provides a CSRF token, or the string 'NOTPROVIDED' if
@@ -23,10 +26,10 @@ def csrf(request):
# In order to be able to provide debugging info in the
# case of misconfiguration, we use a sentinel value
# instead of returning an empty dict.
- return b'NOTPROVIDED'
+ return 'NOTPROVIDED'
else:
- return smart_bytes(token)
- _get_val = lazy(_get_val, str)
+ return smart_text(token)
+ _get_val = lazy(_get_val, six.text_type)
return {'csrf_token': _get_val() }
diff --git a/django/core/files/base.py b/django/core/files/base.py
index d0b25250a5..b81e180292 100644
--- a/django/core/files/base.py
+++ b/django/core/files/base.py
@@ -1,11 +1,14 @@
from __future__ import unicode_literals
import os
-from io import BytesIO
+from io import BytesIO, StringIO, UnsupportedOperation
-from django.utils.encoding import smart_bytes, smart_text
+from django.utils.encoding import smart_text
from django.core.files.utils import FileProxyMixin
+from django.utils import six
+from django.utils.encoding import python_2_unicode_compatible
+@python_2_unicode_compatible
class File(FileProxyMixin):
DEFAULT_CHUNK_SIZE = 64 * 2**10
@@ -18,9 +21,6 @@ class File(FileProxyMixin):
self.mode = file.mode
def __str__(self):
- return smart_bytes(self.name or '')
-
- def __unicode__(self):
return smart_text(self.name or '')
def __repr__(self):
@@ -65,8 +65,10 @@ class File(FileProxyMixin):
if not chunk_size:
chunk_size = self.DEFAULT_CHUNK_SIZE
- if hasattr(self, 'seek'):
+ try:
self.seek(0)
+ except (AttributeError, UnsupportedOperation):
+ pass
while True:
data = self.read(chunk_size)
@@ -124,13 +126,15 @@ class File(FileProxyMixin):
def close(self):
self.file.close()
+@python_2_unicode_compatible
class ContentFile(File):
"""
A File-like object that takes just raw content, rather than an actual file.
"""
def __init__(self, content, name=None):
content = content or b''
- super(ContentFile, self).__init__(BytesIO(content), name=name)
+ stream_class = StringIO if isinstance(content, six.text_type) else BytesIO
+ super(ContentFile, self).__init__(stream_class(content), name=name)
self.size = len(content)
def __str__(self):
diff --git a/django/core/files/move.py b/django/core/files/move.py
index f9060fd3d8..3af02634fe 100644
--- a/django/core/files/move.py
+++ b/django/core/files/move.py
@@ -66,7 +66,7 @@ def file_move_safe(old_file_name, new_file_name, chunk_size = 1024*64, allow_ove
try:
locks.lock(fd, locks.LOCK_EX)
current_chunk = None
- while current_chunk != '':
+ while current_chunk != b'':
current_chunk = old_file.read(chunk_size)
os.write(fd, current_chunk)
finally:
diff --git a/django/core/files/storage.py b/django/core/files/storage.py
index 7542dcda46..0b300cd31e 100644
--- a/django/core/files/storage.py
+++ b/django/core/files/storage.py
@@ -195,11 +195,18 @@ class FileSystemStorage(Storage):
fd = os.open(full_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, 'O_BINARY', 0))
try:
locks.lock(fd, locks.LOCK_EX)
+ _file = None
for chunk in content.chunks():
- os.write(fd, chunk)
+ if _file is None:
+ mode = 'wb' if isinstance(chunk, bytes) else 'wt'
+ _file = os.fdopen(fd, mode)
+ _file.write(chunk)
finally:
locks.unlock(fd)
- os.close(fd)
+ if _file is not None:
+ _file.close()
+ else:
+ os.close(fd)
except OSError as e:
if e.errno == errno.EEXIST:
# Ooops, the file exists. We need a new file name.
diff --git a/django/core/files/uploadedfile.py b/django/core/files/uploadedfile.py
index 3a6c632975..39b99ff78f 100644
--- a/django/core/files/uploadedfile.py
+++ b/django/core/files/uploadedfile.py
@@ -8,7 +8,7 @@ from io import BytesIO
from django.conf import settings
from django.core.files.base import File
from django.core.files import temp as tempfile
-from django.utils.encoding import smart_bytes
+from django.utils.encoding import force_str
__all__ = ('UploadedFile', 'TemporaryUploadedFile', 'InMemoryUploadedFile',
'SimpleUploadedFile')
@@ -30,7 +30,7 @@ class UploadedFile(File):
self.charset = charset
def __repr__(self):
- return smart_bytes("<%s: %s (%s)>" % (
+ return force_str("<%s: %s (%s)>" % (
self.__class__.__name__, self.name, self.content_type))
def _get_name(self):
diff --git a/django/core/files/uploadhandler.py b/django/core/files/uploadhandler.py
index 68d540e595..c422945d6f 100644
--- a/django/core/files/uploadhandler.py
+++ b/django/core/files/uploadhandler.py
@@ -10,6 +10,7 @@ from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.core.files.uploadedfile import TemporaryUploadedFile, InMemoryUploadedFile
from django.utils import importlib
+from django.utils.encoding import python_2_unicode_compatible
__all__ = ['UploadFileException','StopUpload', 'SkipFile', 'FileUploadHandler',
'TemporaryFileUploadHandler', 'MemoryFileUploadHandler',
@@ -21,6 +22,7 @@ class UploadFileException(Exception):
"""
pass
+@python_2_unicode_compatible
class StopUpload(UploadFileException):
"""
This exception is raised when an upload must abort.
@@ -33,7 +35,7 @@ class StopUpload(UploadFileException):
"""
self.connection_reset = connection_reset
- def __unicode__(self):
+ def __str__(self):
if self.connection_reset:
return 'StopUpload: Halt current upload.'
else:
diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py
index 5a6825f0a7..791382bac0 100644
--- a/django/core/handlers/base.py
+++ b/django/core/handlers/base.py
@@ -1,6 +1,7 @@
from __future__ import unicode_literals
import sys
+import types
from django import http
from django.core import signals
@@ -125,10 +126,10 @@ class BaseHandler(object):
# Complain if the view returned None (a common error).
if response is None:
- try:
- view_name = callback.func_name # If it's a function
- except AttributeError:
- view_name = callback.__class__.__name__ + '.__call__' # If it's a class
+ if isinstance(callback, types.FunctionType): # FBV
+ view_name = callback.__name__
+ else: # CBV
+ view_name = callback.__class__.__name__ + '.__call__'
raise ValueError("The view %s.%s didn't return an HttpResponse object." % (callback.__module__, view_name))
# If the response supports deferred rendering, apply template
@@ -152,10 +153,8 @@ class BaseHandler(object):
callback, param_dict = resolver.resolve404()
response = callback(request, **param_dict)
except:
- try:
- response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
- finally:
- signals.got_request_exception.send(sender=self.__class__, request=request)
+ signals.got_request_exception.send(sender=self.__class__, request=request)
+ response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
except exceptions.PermissionDenied:
logger.warning(
'Forbidden (Permission denied): %s', request.path,
@@ -167,12 +166,10 @@ class BaseHandler(object):
callback, param_dict = resolver.resolve403()
response = callback(request, **param_dict)
except:
- try:
- response = self.handle_uncaught_exception(request,
- resolver, sys.exc_info())
- finally:
- signals.got_request_exception.send(
+ signals.got_request_exception.send(
sender=self.__class__, request=request)
+ response = self.handle_uncaught_exception(request,
+ resolver, sys.exc_info())
except SystemExit:
# Allow sys.exit() to actually exit. See tickets #1023 and #4701
raise
@@ -225,7 +222,7 @@ class BaseHandler(object):
# If Http500 handler is not installed, re-raise last exception
if resolver.urlconf_module is None:
- six.reraise(exc_info[1], None, exc_info[2])
+ six.reraise(*exc_info)
# Return an HttpResponse that displays a friendly error message.
callback, param_dict = resolver.resolve500()
return callback(request, **param_dict)
diff --git a/django/core/handlers/wsgi.py b/django/core/handlers/wsgi.py
index 70b23f8515..e445d07a61 100644
--- a/django/core/handlers/wsgi.py
+++ b/django/core/handlers/wsgi.py
@@ -9,7 +9,7 @@ from django.core import signals
from django.core.handlers import base
from django.core.urlresolvers import set_script_prefix
from django.utils import datastructures
-from django.utils.encoding import force_text, smart_bytes, iri_to_uri
+from django.utils.encoding import force_str, force_text, iri_to_uri
from django.utils.log import getLogger
logger = getLogger('django.request')
@@ -246,5 +246,5 @@ class WSGIHandler(base.BaseHandler):
response_headers = [(str(k), str(v)) for k, v in response.items()]
for c in response.cookies.values():
response_headers.append((str('Set-Cookie'), str(c.output(header=''))))
- start_response(smart_bytes(status), response_headers)
+ start_response(force_str(status), response_headers)
return response
diff --git a/django/core/mail/message.py b/django/core/mail/message.py
index b332ffba04..db9023a0bb 100644
--- a/django/core/mail/message.py
+++ b/django/core/mail/message.py
@@ -99,7 +99,12 @@ def sanitize_address(addr, encoding):
if isinstance(addr, six.string_types):
addr = parseaddr(force_text(addr))
nm, addr = addr
- nm = Header(nm, encoding).encode()
+ # This try-except clause is needed on Python 3 < 3.2.4
+ # http://bugs.python.org/issue14291
+ try:
+ nm = Header(nm, encoding).encode()
+ except UnicodeEncodeError:
+ nm = Header(nm, 'utf-8').encode()
try:
addr.encode('ascii')
except UnicodeEncodeError: # IDN
diff --git a/django/core/management/__init__.py b/django/core/management/__init__.py
index 268cd63c38..98f75e0310 100644
--- a/django/core/management/__init__.py
+++ b/django/core/management/__init__.py
@@ -51,14 +51,19 @@ def find_management_module(app_name):
# module, we need look for the case where the project name is part
# of the app_name but the project directory itself isn't on the path.
try:
- f, path, descr = imp.find_module(part,path)
+ f, path, descr = imp.find_module(part, path)
except ImportError as e:
if os.path.basename(os.getcwd()) != part:
raise e
+ else:
+ if f:
+ f.close()
while parts:
part = parts.pop()
f, path, descr = imp.find_module(part, path and [path] or None)
+ if f:
+ f.close()
return path
def load_command_class(app_name, name):
diff --git a/django/core/management/base.py b/django/core/management/base.py
index 5e630d5207..895753ea12 100644
--- a/django/core/management/base.py
+++ b/django/core/management/base.py
@@ -12,7 +12,7 @@ import traceback
import django
from django.core.exceptions import ImproperlyConfigured
from django.core.management.color import color_style
-from django.utils.encoding import smart_str
+from django.utils.encoding import force_str
from django.utils.six import StringIO
@@ -65,7 +65,7 @@ class OutputWrapper(object):
msg += ending
style_func = [f for f in (style_func, self.style_func, lambda x:x)
if f is not None][0]
- self._out.write(smart_str(style_func(msg)))
+ self._out.write(force_str(style_func(msg)))
class BaseCommand(object):
diff --git a/django/core/management/commands/inspectdb.py b/django/core/management/commands/inspectdb.py
index 7c868e4b60..936d5e89ab 100644
--- a/django/core/management/commands/inspectdb.py
+++ b/django/core/management/commands/inspectdb.py
@@ -1,4 +1,7 @@
+from __future__ import unicode_literals
+
import keyword
+import re
from optparse import make_option
from django.core.management.base import NoArgsCommand, CommandError
@@ -31,6 +34,7 @@ class Command(NoArgsCommand):
table_name_filter = options.get('table_name_filter')
table2model = lambda table_name: table_name.title().replace('_', '').replace(' ', '').replace('-', '')
+ strip_prefix = lambda s: s.startswith("u'") and s[1:] or s
cursor = connection.cursor()
yield "# This is an auto-generated Django model module."
@@ -41,6 +45,7 @@ class Command(NoArgsCommand):
yield "#"
yield "# Also note: You'll have to insert the output of 'django-admin.py sqlcustom [appname]'"
yield "# into your database."
+ yield "from __future__ import unicode_literals"
yield ''
yield 'from %s import models' % self.db_module
yield ''
@@ -59,16 +64,19 @@ class Command(NoArgsCommand):
indexes = connection.introspection.get_indexes(cursor, table_name)
except NotImplementedError:
indexes = {}
+ used_column_names = [] # Holds column names used in the table so far
for i, row in enumerate(connection.introspection.get_table_description(cursor, table_name)):
- column_name = row[0]
- att_name = column_name.lower()
comment_notes = [] # Holds Field notes, to be displayed in a Python comment.
extra_params = {} # Holds Field parameters such as 'db_column'.
+ column_name = row[0]
+ is_relation = i in relations
+
+ att_name, params, notes = self.normalize_col_name(
+ column_name, used_column_names, is_relation)
+ extra_params.update(params)
+ comment_notes.extend(notes)
- # If the column name can't be used verbatim as a Python
- # attribute, set the "db_column" for this Field.
- if ' ' in att_name or '-' in att_name or keyword.iskeyword(att_name) or column_name != att_name:
- extra_params['db_column'] = column_name
+ used_column_names.append(att_name)
# Add primary_key and unique, if necessary.
if column_name in indexes:
@@ -77,30 +85,12 @@ class Command(NoArgsCommand):
elif indexes[column_name]['unique']:
extra_params['unique'] = True
- # Modify the field name to make it Python-compatible.
- if ' ' in att_name:
- att_name = att_name.replace(' ', '_')
- comment_notes.append('Field renamed to remove spaces.')
-
- if '-' in att_name:
- att_name = att_name.replace('-', '_')
- comment_notes.append('Field renamed to remove dashes.')
-
- if column_name != att_name:
- comment_notes.append('Field name made lowercase.')
-
- if i in relations:
+ if is_relation:
rel_to = relations[i][1] == table_name and "'self'" or table2model(relations[i][1])
-
if rel_to in known_models:
field_type = 'ForeignKey(%s' % rel_to
else:
field_type = "ForeignKey('%s'" % rel_to
-
- if att_name.endswith('_id'):
- att_name = att_name[:-3]
- else:
- extra_params['db_column'] = column_name
else:
# Calling `get_field_type` to get the field type string and any
# additional paramters and notes.
@@ -110,16 +100,6 @@ class Command(NoArgsCommand):
field_type += '('
- if keyword.iskeyword(att_name):
- att_name += '_field'
- comment_notes.append('Field renamed because it was a Python reserved word.')
-
- if att_name[0].isdigit():
- att_name = 'number_%s' % att_name
- extra_params['db_column'] = six.text_type(column_name)
- comment_notes.append("Field renamed because it wasn't a "
- "valid Python identifier.")
-
# Don't output 'id = meta.AutoField(primary_key=True)', because
# that's assumed if it doesn't exist.
if att_name == 'id' and field_type == 'AutoField(' and extra_params == {'primary_key': True}:
@@ -136,7 +116,9 @@ class Command(NoArgsCommand):
if extra_params:
if not field_desc.endswith('('):
field_desc += ', '
- field_desc += ', '.join(['%s=%r' % (k, v) for k, v in extra_params.items()])
+ field_desc += ', '.join([
+ '%s=%s' % (k, strip_prefix(repr(v)))
+ for k, v in extra_params.items()])
field_desc += ')'
if comment_notes:
field_desc += ' # ' + ' '.join(comment_notes)
@@ -144,6 +126,62 @@ class Command(NoArgsCommand):
for meta_line in self.get_meta(table_name):
yield meta_line
+ def normalize_col_name(self, col_name, used_column_names, is_relation):
+ """
+ Modify the column name to make it Python-compatible as a field name
+ """
+ field_params = {}
+ field_notes = []
+
+ new_name = col_name.lower()
+ if new_name != col_name:
+ field_notes.append('Field name made lowercase.')
+
+ if is_relation:
+ if new_name.endswith('_id'):
+ new_name = new_name[:-3]
+ else:
+ field_params['db_column'] = col_name
+
+ new_name, num_repl = re.subn(r'\W', '_', new_name)
+ if num_repl > 0:
+ field_notes.append('Field renamed to remove unsuitable characters.')
+
+ if new_name.find('__') >= 0:
+ while new_name.find('__') >= 0:
+ new_name = new_name.replace('__', '_')
+ if col_name.lower().find('__') >= 0:
+ # Only add the comment if the double underscore was in the original name
+ field_notes.append("Field renamed because it contained more than one '_' in a row.")
+
+ if new_name.startswith('_'):
+ new_name = 'field%s' % new_name
+ field_notes.append("Field renamed because it started with '_'.")
+
+ if new_name.endswith('_'):
+ new_name = '%sfield' % new_name
+ field_notes.append("Field renamed because it ended with '_'.")
+
+ if keyword.iskeyword(new_name):
+ new_name += '_field'
+ field_notes.append('Field renamed because it was a Python reserved word.')
+
+ if new_name[0].isdigit():
+ new_name = 'number_%s' % new_name
+ field_notes.append("Field renamed because it wasn't a valid Python identifier.")
+
+ if new_name in used_column_names:
+ num = 0
+ while '%s_%d' % (new_name, num) in used_column_names:
+ num += 1
+ new_name = '%s_%d' % (new_name, num)
+ field_notes.append('Field renamed because of name conflict.')
+
+ if col_name != new_name and field_notes:
+ field_params['db_column'] = col_name
+
+ return new_name, field_params, field_notes
+
def get_field_type(self, connection, table_name, row):
"""
Given the database connection, the table name, and the cursor row
@@ -181,6 +219,6 @@ class Command(NoArgsCommand):
to construct the inner Meta class for the model corresponding
to the given database table name.
"""
- return [' class Meta:',
- ' db_table = %r' % table_name,
- '']
+ return [" class Meta:",
+ " db_table = '%s'" % table_name,
+ ""]
diff --git a/django/core/management/commands/loaddata.py b/django/core/management/commands/loaddata.py
index 1896e53cee..30cf740cdf 100644
--- a/django/core/management/commands/loaddata.py
+++ b/django/core/management/commands/loaddata.py
@@ -196,6 +196,10 @@ class Command(BaseCommand):
loaded_object_count += loaded_objects_in_fixture
fixture_object_count += objects_in_fixture
label_found = True
+ except Exception as e:
+ if not isinstance(e, CommandError):
+ e.args = ("Problem installing fixture '%s': %s" % (full_path, e),)
+ raise
finally:
fixture.close()
@@ -209,7 +213,11 @@ class Command(BaseCommand):
# Since we disabled constraint checks, we must manually check for
# any invalid keys that might have been added
table_names = [model._meta.db_table for model in models]
- connection.check_constraints(table_names=table_names)
+ try:
+ connection.check_constraints(table_names=table_names)
+ except Exception as e:
+ e.args = ("Problem installing fixtures: %s" % e,)
+ raise
except (SystemExit, KeyboardInterrupt):
raise
@@ -217,8 +225,6 @@ class Command(BaseCommand):
if commit:
transaction.rollback(using=using)
transaction.leave_transaction_management(using=using)
- if not isinstance(e, CommandError):
- e.args = ("Problem installing fixture '%s': %s" % (full_path, e),)
raise
# If we found even one object in a fixture, we need to reset the
diff --git a/django/core/management/commands/makemessages.py b/django/core/management/commands/makemessages.py
index d8e8f6cff7..81c4fdf8cc 100644
--- a/django/core/management/commands/makemessages.py
+++ b/django/core/management/commands/makemessages.py
@@ -47,32 +47,27 @@ def _popen(cmd):
output, errors = p.communicate()
return output, errors, p.returncode
-def walk(root, topdown=True, onerror=None, followlinks=False,
- ignore_patterns=None, verbosity=0, stdout=sys.stdout):
+def find_files(root, ignore_patterns, verbosity, stdout=sys.stdout, symlinks=False):
"""
- A version of os.walk that can follow symlinks for Python < 2.6
+ Helper function to get all files in the given root.
"""
- if ignore_patterns is None:
- ignore_patterns = []
dir_suffix = '%s*' % os.sep
- norm_patterns = map(lambda p: p.endswith(dir_suffix)
- and p[:-len(dir_suffix)] or p, ignore_patterns)
- for dirpath, dirnames, filenames in os.walk(root, topdown, onerror):
- remove_dirs = []
- for dirname in dirnames:
+ norm_patterns = [p[:-len(dir_suffix)] if p.endswith(dir_suffix) else p for p in ignore_patterns]
+ all_files = []
+ for dirpath, dirnames, filenames in os.walk(root, topdown=True, followlinks=symlinks):
+ for dirname in dirnames[:]:
if is_ignored(os.path.normpath(os.path.join(dirpath, dirname)), norm_patterns):
- remove_dirs.append(dirname)
- for dirname in remove_dirs:
- dirnames.remove(dirname)
- if verbosity > 1:
- stdout.write('ignoring directory %s\n' % dirname)
- yield (dirpath, dirnames, filenames)
- if followlinks:
- for d in dirnames:
- p = os.path.join(dirpath, d)
- if os.path.islink(p):
- for link_dirpath, link_dirnames, link_filenames in walk(p):
- yield (link_dirpath, link_dirnames, link_filenames)
+ dirnames.remove(dirname)
+ if verbosity > 1:
+ stdout.write('ignoring directory %s\n' % dirname)
+ for filename in filenames:
+ if is_ignored(os.path.normpath(os.path.join(dirpath, filename)), ignore_patterns):
+ if verbosity > 1:
+ stdout.write('ignoring file %s in %s\n' % (filename, dirpath))
+ else:
+ all_files.extend([(dirpath, filename)])
+ all_files.sort()
+ return all_files
def is_ignored(path, ignore_patterns):
"""
@@ -83,23 +78,6 @@ def is_ignored(path, ignore_patterns):
return True
return False
-def find_files(root, ignore_patterns, verbosity, stdout=sys.stdout, symlinks=False):
- """
- Helper function to get all files in the given root.
- """
- all_files = []
- for (dirpath, dirnames, filenames) in walk(root, followlinks=symlinks,
- ignore_patterns=ignore_patterns, verbosity=verbosity, stdout=stdout):
- for filename in filenames:
- norm_filepath = os.path.normpath(os.path.join(dirpath, filename))
- if is_ignored(norm_filepath, ignore_patterns):
- if verbosity > 1:
- stdout.write('ignoring file %s in %s\n' % (filename, dirpath))
- else:
- all_files.extend([(dirpath, filename)])
- all_files.sort()
- return all_files
-
def copy_plural_forms(msgs, locale, domain, verbosity, stdout=sys.stdout):
"""
Copies plural forms header contents from a Django catalog of locale to
@@ -144,7 +122,7 @@ def write_pot_file(potfile, msgs, file, work_file, is_templatized):
msgs = '\n'.join(dropwhile(len, msgs.split('\n')))
else:
msgs = msgs.replace('charset=CHARSET', 'charset=UTF-8')
- with open(potfile, 'ab') as fp:
+ with open(potfile, 'a') as fp:
fp.write(msgs)
def process_file(file, dirpath, potfile, domain, verbosity,
@@ -252,7 +230,7 @@ def write_po_file(pofile, potfile, domain, locale, verbosity, stdout,
msgs = copy_plural_forms(msgs, locale, domain, verbosity, stdout)
msgs = msgs.replace(
"#. #-#-#-#-# %s.pot (PACKAGE VERSION) #-#-#-#-#\n" % domain, "")
- with open(pofile, 'wb') as fp:
+ with open(pofile, 'w') as fp:
fp.write(msgs)
os.unlink(potfile)
if no_obsolete:
diff --git a/django/core/management/commands/shell.py b/django/core/management/commands/shell.py
index 4e7d1dbbf4..52a8cab7e4 100644
--- a/django/core/management/commands/shell.py
+++ b/django/core/management/commands/shell.py
@@ -80,14 +80,14 @@ class Command(NoArgsCommand):
readline.parse_and_bind("tab:complete")
# We want to honor both $PYTHONSTARTUP and .pythonrc.py, so follow system
- # conventions and get $PYTHONSTARTUP first then import user.
+ # conventions and get $PYTHONSTARTUP first then .pythonrc.py.
if not use_plain:
- pythonrc = os.environ.get("PYTHONSTARTUP")
- if pythonrc and os.path.isfile(pythonrc):
- try:
- execfile(pythonrc)
- except NameError:
- pass
- # This will import .pythonrc.py as a side-effect
- import user
+ for pythonrc in (os.environ.get("PYTHONSTARTUP"),
+ os.path.expanduser('~/.pythonrc.py')):
+ if pythonrc and os.path.isfile(pythonrc):
+ try:
+ with open(pythonrc) as handle:
+ exec(compile(handle.read(), pythonrc, 'exec'))
+ except NameError:
+ pass
code.interact(local=imported_objects)
diff --git a/django/core/management/commands/syncdb.py b/django/core/management/commands/syncdb.py
index cf26e754ae..cceec07be8 100644
--- a/django/core/management/commands/syncdb.py
+++ b/django/core/management/commands/syncdb.py
@@ -75,7 +75,7 @@ class Command(NoArgsCommand):
(opts.auto_created and converter(opts.auto_created._meta.db_table) in tables))
manifest = SortedDict(
- (app_name, filter(model_installed, model_list))
+ (app_name, list(filter(model_installed, model_list)))
for app_name, model_list in all_models
)
diff --git a/django/core/management/sql.py b/django/core/management/sql.py
index 7579cbe8ab..b02a548314 100644
--- a/django/core/management/sql.py
+++ b/django/core/management/sql.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
+import codecs
import os
import re
@@ -168,10 +169,10 @@ def custom_sql_for_model(model, style, connection):
os.path.join(app_dir, "%s.sql" % opts.object_name.lower())]
for sql_file in sql_files:
if os.path.exists(sql_file):
- with open(sql_file, 'U') as fp:
+ with codecs.open(sql_file, 'U', encoding=settings.FILE_CHARSET) as fp:
# Some backends can't execute more than one SQL statement at a time,
# so split into separate statements.
- output.extend(_split_statements(fp.read().decode(settings.FILE_CHARSET)))
+ output.extend(_split_statements(fp.read()))
return output
diff --git a/django/core/management/templates.py b/django/core/management/templates.py
index 52d0e5c89d..aa65593e9c 100644
--- a/django/core/management/templates.py
+++ b/django/core/management/templates.py
@@ -8,6 +8,8 @@ import shutil
import stat
import sys
import tempfile
+import codecs
+
try:
from urllib.request import urlretrieve
except ImportError: # Python 2
@@ -154,12 +156,12 @@ class TemplateCommand(BaseCommand):
# Only render the Python files, as we don't want to
# accidentally render Django templates files
- with open(old_path, 'r') as template_file:
+ with codecs.open(old_path, 'r', 'utf-8') as template_file:
content = template_file.read()
if filename.endswith(extensions) or filename in extra_files:
template = Template(content)
content = template.render(context)
- with open(new_path, 'w') as new_file:
+ with codecs.open(new_path, 'w', 'utf-8') as new_file:
new_file.write(content)
if self.verbosity >= 2:
diff --git a/django/core/management/validation.py b/django/core/management/validation.py
index 274f98ee79..6cd66f3a6a 100644
--- a/django/core/management/validation.py
+++ b/django/core/management/validation.py
@@ -1,7 +1,7 @@
import sys
from django.core.management.color import color_style
-from django.utils.encoding import smart_str
+from django.utils.encoding import force_str
from django.utils.itercompat import is_iterable
from django.utils import six
@@ -13,7 +13,7 @@ class ModelErrorCollection:
def add(self, context, error):
self.errors.append((context, error))
- self.outfile.write(self.style.ERROR(smart_str("%s: %s\n" % (context, error))))
+ self.outfile.write(self.style.ERROR(force_str("%s: %s\n" % (context, error))))
def get_validation_errors(outfile, app=None):
"""
diff --git a/django/core/serializers/base.py b/django/core/serializers/base.py
index bdb43db9f3..276f9a4738 100644
--- a/django/core/serializers/base.py
+++ b/django/core/serializers/base.py
@@ -2,8 +2,6 @@
Module for abstract serializer/unserializer base classes.
"""
-from io import BytesIO
-
from django.db import models
from django.utils.encoding import smart_text
from django.utils import six
@@ -35,7 +33,7 @@ class Serializer(object):
"""
self.options = options
- self.stream = options.pop("stream", BytesIO())
+ self.stream = options.pop("stream", six.StringIO())
self.selected_fields = options.pop("fields", None)
self.use_natural_keys = options.pop("use_natural_keys", False)
@@ -125,7 +123,7 @@ class Deserializer(object):
"""
self.options = options
if isinstance(stream_or_string, six.string_types):
- self.stream = BytesIO(stream_or_string)
+ self.stream = six.StringIO(stream_or_string)
else:
self.stream = stream_or_string
# hack to make sure that the models have all been loaded before
diff --git a/django/core/serializers/json.py b/django/core/serializers/json.py
index 3bac24d33a..ed65f2922c 100644
--- a/django/core/serializers/json.py
+++ b/django/core/serializers/json.py
@@ -12,7 +12,6 @@ import json
from django.core.serializers.base import DeserializationError
from django.core.serializers.python import Serializer as PythonSerializer
from django.core.serializers.python import Deserializer as PythonDeserializer
-from django.utils.encoding import smart_bytes
from django.utils import six
from django.utils.timezone import is_aware
@@ -61,13 +60,12 @@ def Deserializer(stream_or_string, **options):
"""
Deserialize a stream or string of JSON data.
"""
+ if not isinstance(stream_or_string, (bytes, six.string_types)):
+ stream_or_string = stream_or_string.read()
if isinstance(stream_or_string, bytes):
stream_or_string = stream_or_string.decode('utf-8')
try:
- if isinstance(stream_or_string, six.string_types):
- objects = json.loads(stream_or_string)
- else:
- objects = json.load(stream_or_string)
+ objects = json.loads(stream_or_string)
for obj in PythonDeserializer(objects, **options):
yield obj
except GeneratorExit:
diff --git a/django/core/serializers/pyyaml.py b/django/core/serializers/pyyaml.py
index 9be1ea4492..4c11626bad 100644
--- a/django/core/serializers/pyyaml.py
+++ b/django/core/serializers/pyyaml.py
@@ -12,7 +12,6 @@ from django.db import models
from django.core.serializers.base import DeserializationError
from django.core.serializers.python import Serializer as PythonSerializer
from django.core.serializers.python import Deserializer as PythonDeserializer
-from django.utils.encoding import smart_bytes
from django.utils import six
diff --git a/django/core/servers/basehttp.py b/django/core/servers/basehttp.py
index 2db4e5ef6a..19b287af87 100644
--- a/django/core/servers/basehttp.py
+++ b/django/core/servers/basehttp.py
@@ -7,6 +7,8 @@ This is a simple server for use in testing or debugging Django apps. It hasn't
been reviewed for security issues. DON'T USE IT FOR PRODUCTION USE!
"""
+from __future__ import unicode_literals
+
import os
import socket
import sys
@@ -71,12 +73,12 @@ class WSGIServerException(Exception):
class ServerHandler(simple_server.ServerHandler, object):
- error_status = "500 INTERNAL SERVER ERROR"
+ error_status = str("500 INTERNAL SERVER ERROR")
def write(self, data):
- """'write()' callable as specified by PEP 333"""
+ """'write()' callable as specified by PEP 3333"""
- assert isinstance(data, str), "write() argument must be string"
+ assert isinstance(data, bytes), "write() argument must be bytestring"
if not self.status:
raise AssertionError("write() before start_response()")
@@ -200,7 +202,7 @@ class WSGIRequestHandler(simple_server.WSGIRequestHandler, object):
def run(addr, port, wsgi_handler, ipv6=False, threading=False):
server_address = (addr, port)
if threading:
- httpd_cls = type('WSGIServer', (socketserver.ThreadingMixIn, WSGIServer), {})
+ httpd_cls = type(str('WSGIServer'), (socketserver.ThreadingMixIn, WSGIServer), {})
else:
httpd_cls = WSGIServer
httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
diff --git a/django/core/signing.py b/django/core/signing.py
index 9ab8c5b8b0..147e54780c 100644
--- a/django/core/signing.py
+++ b/django/core/signing.py
@@ -32,6 +32,9 @@ start of the base64 JSON.
There are 65 url-safe characters: the 64 used by url-safe base64 and the ':'.
These functions make use of all of them.
"""
+
+from __future__ import unicode_literals
+
import base64
import json
import time
@@ -41,7 +44,7 @@ from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.utils import baseconv
from django.utils.crypto import constant_time_compare, salted_hmac
-from django.utils.encoding import force_text, smart_bytes
+from django.utils.encoding import force_bytes, force_str, force_text
from django.utils.importlib import import_module
@@ -60,11 +63,11 @@ class SignatureExpired(BadSignature):
def b64_encode(s):
- return base64.urlsafe_b64encode(s).strip('=')
+ return base64.urlsafe_b64encode(s).strip(b'=')
def b64_decode(s):
- pad = '=' * (-len(s) % 4)
+ pad = b'=' * (-len(s) % 4)
return base64.urlsafe_b64decode(s + pad)
@@ -114,7 +117,7 @@ def dumps(obj, key=None, salt='django.core.signing', serializer=JSONSerializer,
value or re-using a salt value across different parts of your
application without good cause is a security risk.
"""
- data = serializer().dumps(obj)
+ data = force_bytes(serializer().dumps(obj))
# Flag for if it's been compressed or not
is_compressed = False
@@ -127,7 +130,7 @@ def dumps(obj, key=None, salt='django.core.signing', serializer=JSONSerializer,
is_compressed = True
base64d = b64_encode(data)
if is_compressed:
- base64d = '.' + base64d
+ base64d = b'.' + base64d
return TimestampSigner(key, salt=salt).sign(base64d)
@@ -135,35 +138,40 @@ def loads(s, key=None, salt='django.core.signing', serializer=JSONSerializer, ma
"""
Reverse of dumps(), raises BadSignature if signature fails
"""
- base64d = smart_bytes(
- TimestampSigner(key, salt=salt).unsign(s, max_age=max_age))
+ # TimestampSigner.unsign always returns unicode but base64 and zlib
+ # compression operate on bytes.
+ base64d = force_bytes(TimestampSigner(key, salt=salt).unsign(s, max_age=max_age))
decompress = False
- if base64d[0] == '.':
+ if base64d[0] == b'.':
# It's compressed; uncompress it first
base64d = base64d[1:]
decompress = True
data = b64_decode(base64d)
if decompress:
data = zlib.decompress(data)
- return serializer().loads(data)
+ return serializer().loads(force_str(data))
class Signer(object):
+
def __init__(self, key=None, sep=':', salt=None):
- self.sep = sep
- self.key = key or settings.SECRET_KEY
- self.salt = salt or ('%s.%s' %
- (self.__class__.__module__, self.__class__.__name__))
+ # Use of native strings in all versions of Python
+ self.sep = str(sep)
+ self.key = str(key or settings.SECRET_KEY)
+ self.salt = str(salt or
+ '%s.%s' % (self.__class__.__module__, self.__class__.__name__))
def signature(self, value):
- return base64_hmac(self.salt + 'signer', value, self.key)
+ signature = base64_hmac(self.salt + 'signer', value, self.key)
+ # Convert the signature from bytes to str only on Python 3
+ return force_str(signature)
def sign(self, value):
- value = smart_bytes(value)
- return '%s%s%s' % (value, self.sep, self.signature(value))
+ value = force_str(value)
+ return str('%s%s%s') % (value, self.sep, self.signature(value))
def unsign(self, signed_value):
- signed_value = smart_bytes(signed_value)
+ signed_value = force_str(signed_value)
if not self.sep in signed_value:
raise BadSignature('No "%s" found in value' % self.sep)
value, sig = signed_value.rsplit(self.sep, 1)
@@ -178,8 +186,9 @@ class TimestampSigner(Signer):
return baseconv.base62.encode(int(time.time()))
def sign(self, value):
- value = smart_bytes('%s%s%s' % (value, self.sep, self.timestamp()))
- return '%s%s%s' % (value, self.sep, self.signature(value))
+ value = force_str(value)
+ value = str('%s%s%s') % (value, self.sep, self.timestamp())
+ return super(TimestampSigner, self).sign(value)
def unsign(self, value, max_age=None):
result = super(TimestampSigner, self).unsign(value)
diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py
index 2fe744e8eb..af3df83d0a 100644
--- a/django/core/urlresolvers.py
+++ b/django/core/urlresolvers.py
@@ -14,7 +14,7 @@ from threading import local
from django.http import Http404
from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
from django.utils.datastructures import MultiValueDict
-from django.utils.encoding import iri_to_uri, force_text, smart_bytes
+from django.utils.encoding import force_str, force_text, iri_to_uri
from django.utils.functional import memoize, lazy
from django.utils.importlib import import_module
from django.utils.module_loading import module_has_submodule
@@ -89,18 +89,11 @@ def get_callable(lookup_view, can_fail=False):
"""
if not callable(lookup_view):
mod_name, func_name = get_mod_func(lookup_view)
+ if func_name == '':
+ return lookup_view
+
try:
- if func_name != '':
- lookup_view = getattr(import_module(mod_name), func_name)
- if not callable(lookup_view):
- raise ViewDoesNotExist(
- "Could not import %s.%s. View is not callable." %
- (mod_name, func_name))
- except AttributeError:
- if not can_fail:
- raise ViewDoesNotExist(
- "Could not import %s. View does not exist in module %s." %
- (lookup_view, mod_name))
+ mod = import_module(mod_name)
except ImportError:
parentmod, submod = get_mod_func(mod_name)
if (not can_fail and submod != '' and
@@ -110,6 +103,18 @@ def get_callable(lookup_view, can_fail=False):
(lookup_view, mod_name))
if not can_fail:
raise
+ else:
+ try:
+ lookup_view = getattr(mod, func_name)
+ if not callable(lookup_view):
+ raise ViewDoesNotExist(
+ "Could not import %s.%s. View is not callable." %
+ (mod_name, func_name))
+ except AttributeError:
+ if not can_fail:
+ raise ViewDoesNotExist(
+ "Could not import %s. View does not exist in module %s." %
+ (lookup_view, mod_name))
return lookup_view
get_callable = memoize(get_callable, _callable_cache, 1)
@@ -190,7 +195,7 @@ class RegexURLPattern(LocaleRegexProvider):
self.name = name
def __repr__(self):
- return smart_bytes('<%s %s %s>' % (self.__class__.__name__, self.name, self.regex.pattern))
+ return force_str('<%s %s %s>' % (self.__class__.__name__, self.name, self.regex.pattern))
def add_prefix(self, prefix):
"""
@@ -240,7 +245,14 @@ class RegexURLResolver(LocaleRegexProvider):
self._app_dict = {}
def __repr__(self):
- return smart_bytes('<%s %s (%s:%s) %s>' % (self.__class__.__name__, self.urlconf_name, self.app_name, self.namespace, self.regex.pattern))
+ if isinstance(self.urlconf_name, list) and len(self.urlconf_name):
+ # Don't bother to output the whole list, it can be huge
+ urlconf_repr = '<%s list>' % self.urlconf_name[0].__class__.__name__
+ else:
+ urlconf_repr = repr(self.urlconf_name)
+ return force_str('<%s %s (%s:%s) %s>' % (
+ self.__class__.__name__, urlconf_repr, self.app_name,
+ self.namespace, self.regex.pattern))
def _populate(self):
lookups = MultiValueDict()
diff --git a/django/core/validators.py b/django/core/validators.py
index 456fa3cec5..317e3880bf 100644
--- a/django/core/validators.py
+++ b/django/core/validators.py
@@ -8,7 +8,7 @@ except ImportError: # Python 2
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
-from django.utils.encoding import smart_text
+from django.utils.encoding import force_text
from django.utils.ipv6 import is_valid_ipv6_address
from django.utils import six
@@ -36,7 +36,7 @@ class RegexValidator(object):
"""
Validates that the input matches the regular expression.
"""
- if not self.regex.search(smart_text(value)):
+ if not self.regex.search(force_text(value)):
raise ValidationError(self.message, code=self.code)
class URLValidator(RegexValidator):
@@ -44,7 +44,8 @@ class URLValidator(RegexValidator):
r'^(?:http|ftp)s?://' # http:// or https://
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' #domain...
r'localhost|' #localhost...
- r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
+ r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|' # ...or ipv4
+ r'\[?[A-F0-9]*:[A-F0-9:]+\]?)' # ...or ipv6
r'(?::\d+)?' # optional port
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
@@ -54,10 +55,10 @@ class URLValidator(RegexValidator):
except ValidationError as e:
# Trivial case failed. Try for possible IDN domain
if value:
- value = smart_text(value)
+ value = force_text(value)
scheme, netloc, path, query, fragment = urlsplit(value)
try:
- netloc = netloc.encode('idna') # IDN -> ACE
+ netloc = netloc.encode('idna').decode('ascii') # IDN -> ACE
except UnicodeError: # invalid domain part
raise e
url = urlunsplit((scheme, netloc, path, query, fragment))
@@ -84,7 +85,7 @@ class EmailValidator(RegexValidator):
if value and '@' in value:
parts = value.split('@')
try:
- parts[-1] = parts[-1].encode('idna')
+ parts[-1] = parts[-1].encode('idna').decode('ascii')
except UnicodeError:
raise e
super(EmailValidator, self).__call__('@'.join(parts))
@@ -99,7 +100,7 @@ email_re = re.compile(
r'|\[(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\]$', re.IGNORECASE) # literal form, ipv4 address (SMTP 4.1.3)
validate_email = EmailValidator(email_re, _('Enter a valid e-mail address.'), 'invalid')
-slug_re = re.compile(r'^[-\w]+$')
+slug_re = re.compile(r'^[-a-zA-Z0-9_]+$')
validate_slug = RegexValidator(slug_re, _("Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."), 'invalid')
ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$')