Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

[1.5.x] Fixed #19357 -- Allow non-ASCII chars in filesystem paths

Thanks kujiu for the report and Aymeric Augustin for the review.
Backport of c916673 from master.
  • Loading branch information...
commit 4214a22e060f9d692b77294659023633c63e1647 1 parent a0578a1
Claude Paroz authored December 08, 2012

Showing 56 changed files with 228 additions and 147 deletions. Show diff stats Hide diff stats

  1. 3  django/contrib/admindocs/views.py
  2. 3  django/contrib/auth/tests/context_processors.py
  3. 3  django/contrib/auth/tests/forms.py
  4. 3  django/contrib/auth/tests/views.py
  5. 5  django/contrib/formtools/tests/__init__.py
  6. 15  django/contrib/formtools/tests/wizard/wizardtests/tests.py
  7. 3  django/contrib/gis/geometry/test_data.py
  8. 3  django/contrib/gis/tests/geo3d/tests.py
  9. 3  django/contrib/gis/tests/geogapp/tests.py
  10. 3  django/contrib/gis/tests/layermap/tests.py
  11. 5  django/contrib/sitemaps/tests/http.py
  12. 3  django/contrib/staticfiles/storage.py
  13. 7  django/core/management/__init__.py
  14. 5  django/core/management/commands/compilemessages.py
  15. 5  django/core/management/commands/loaddata.py
  16. 6  django/core/management/commands/makemessages.py
  17. 3  django/core/management/sql.py
  18. 4  django/core/urlresolvers.py
  19. 5  django/db/models/loading.py
  20. 3  django/db/utils.py
  21. 23  django/utils/_os.py
  22. 7  django/utils/translation/trans_real.py
  23. 3  django/views/i18n.py
  24. 12  tests/modeltests/fixtures/tests.py
  25. 5  tests/modeltests/model_forms/tests.py
  26. 3  tests/modeltests/proxy_model_inheritance/tests.py
  27. 15  tests/regressiontests/admin_scripts/tests.py
  28. 3  tests/regressiontests/admin_scripts/urls.py
  29. 3  tests/regressiontests/admin_views/tests.py
  30. 3  tests/regressiontests/app_loading/tests.py
  31. 3  tests/regressiontests/bug639/tests.py
  32. 7  tests/regressiontests/file_storage/tests.py
  33. 8  tests/regressiontests/fixtures_regress/tests.py
  34. 11  tests/regressiontests/forms/tests/fields.py
  35. 5  tests/regressiontests/httpwrappers/tests.py
  36. 7  tests/regressiontests/i18n/commands/compilation.py
  37. 31  tests/regressiontests/i18n/commands/extraction.py
  38. 3  tests/regressiontests/i18n/contenttypes/tests.py
  39. 5  tests/regressiontests/i18n/patterns/tests.py
  40. 7  tests/regressiontests/i18n/tests.py
  41. 5  tests/regressiontests/logging_tests/tests.py
  42. 5  tests/regressiontests/model_fields/imagefield.py
  43. 3  tests/regressiontests/model_forms_regress/models.py
  44. 3  tests/regressiontests/servers/tests.py
  45. 14  tests/regressiontests/staticfiles_tests/tests.py
  46. 7  tests/regressiontests/templates/loaders.py
  47. 3  tests/regressiontests/templates/response.py
  48. 7  tests/regressiontests/templates/tests.py
  49. 5  tests/regressiontests/test_client_regress/tests.py
  50. 2  tests/regressiontests/urlpatterns_reverse/tests.py
  51. 3  tests/regressiontests/utils/archive.py
  52. 3  tests/regressiontests/utils/module_loading.py
  53. 46  tests/regressiontests/views/tests/debug.py
  54. 3  tests/regressiontests/views/tests/i18n.py
  55. 3  tests/regressiontests/views/urls.py
  56. 9  tests/runtests.py
3  django/contrib/admindocs/views.py
@@ -14,6 +14,7 @@
14 14
 from django.contrib.admindocs import utils
15 15
 from django.contrib.sites.models import Site
16 16
 from django.utils.importlib import import_module
  17
+from django.utils._os import upath
17 18
 from django.utils import six
18 19
 from django.utils.translation import ugettext as _
19 20
 from django.utils.safestring import mark_safe
@@ -311,7 +312,7 @@ def load_all_installed_template_libraries():
311 312
         try:
312 313
             libraries = [
313 314
                 os.path.splitext(p)[0]
314  
-                for p in os.listdir(os.path.dirname(mod.__file__))
  315
+                for p in os.listdir(os.path.dirname(upath(mod.__file__)))
315 316
                 if p.endswith('.py') and p[0].isalpha()
316 317
             ]
317 318
         except OSError:
3  django/contrib/auth/tests/context_processors.py
@@ -9,6 +9,7 @@
9 9
 from django.db.models import Q
10 10
 from django.test import TestCase
11 11
 from django.test.utils import override_settings
  12
+from django.utils._os import upath
12 13
 
13 14
 
14 15
 class MockUser(object):
@@ -63,7 +64,7 @@ def test_permlookupdict_in(self):
63 64
 @skipIfCustomUser
64 65
 @override_settings(
65 66
     TEMPLATE_DIRS=(
66  
-            os.path.join(os.path.dirname(__file__), 'templates'),
  67
+            os.path.join(os.path.dirname(upath(__file__)), 'templates'),
67 68
         ),
68 69
     USE_TZ=False,                           # required for loading the fixture
69 70
     PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
3  django/contrib/auth/tests/forms.py
@@ -11,6 +11,7 @@
11 11
 from django.test import TestCase
12 12
 from django.test.utils import override_settings
13 13
 from django.utils.encoding import force_text
  14
+from django.utils._os import upath
14 15
 from django.utils import translation
15 16
 from django.utils.translation import ugettext as _
16 17
 
@@ -331,7 +332,7 @@ def test_cleaned_data(self):
331 332
         self.assertEqual(form.cleaned_data['email'], email)
332 333
 
333 334
     def test_custom_email_subject(self):
334  
-        template_path = os.path.join(os.path.dirname(__file__), 'templates')
  335
+        template_path = os.path.join(os.path.dirname(upath(__file__)), 'templates')
335 336
         with self.settings(TEMPLATE_DIRS=(template_path,)):
336 337
             data = {'email': 'testclient@example.com'}
337 338
             form = PasswordResetForm(data)
3  django/contrib/auth/tests/views.py
@@ -11,6 +11,7 @@
11 11
 from django.utils.encoding import force_text
12 12
 from django.utils.html import escape
13 13
 from django.utils.http import urlquote
  14
+from django.utils._os import upath
14 15
 from django.test import TestCase
15 16
 from django.test.utils import override_settings
16 17
 
@@ -27,7 +28,7 @@
27 28
     LANGUAGE_CODE='en',
28 29
     TEMPLATE_LOADERS=global_settings.TEMPLATE_LOADERS,
29 30
     TEMPLATE_DIRS=(
30  
-        os.path.join(os.path.dirname(__file__), 'templates'),
  31
+        os.path.join(os.path.dirname(upath(__file__)), 'templates'),
31 32
     ),
32 33
     USE_TZ=False,
33 34
     PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
5  django/contrib/formtools/tests/__init__.py
@@ -14,6 +14,7 @@
14 14
 from django.test import TestCase
15 15
 from django.test.html import parse_html
16 16
 from django.test.utils import override_settings
  17
+from django.utils._os import upath
17 18
 from django.utils import unittest
18 19
 
19 20
 from django.contrib.formtools.tests.wizard import *
@@ -36,7 +37,7 @@ def done(self, request, cleaned_data):
36 37
 
37 38
 @override_settings(
38 39
     TEMPLATE_DIRS=(
39  
-        os.path.join(os.path.dirname(__file__), 'templates'),
  40
+        os.path.join(os.path.dirname(upath(__file__)), 'templates'),
40 41
     ),
41 42
 )
42 43
 class PreviewTests(TestCase):
@@ -214,7 +215,7 @@ def __init__(self, POST=None):
214 215
 @override_settings(
215 216
     SECRET_KEY="123",
216 217
     TEMPLATE_DIRS=(
217  
-        os.path.join(os.path.dirname(__file__), 'templates'),
  218
+        os.path.join(os.path.dirname(upath(__file__)), 'templates'),
218 219
     ),
219 220
 )
220 221
 class WizardTests(TestCase):
15  django/contrib/formtools/tests/wizard/wizardtests/tests.py
@@ -9,6 +9,7 @@
9 9
 from django.contrib.auth.models import User
10 10
 from django.contrib.formtools.wizard.views import CookieWizardView
11 11
 from django.contrib.formtools.tests.wizard.forms import UserForm, UserFormSet
  12
+from django.utils._os import upath
12 13
 
13 14
 
14 15
 class WizardTests(object):
@@ -86,7 +87,7 @@ def test_form_finish(self):
86 87
         self.assertEqual(response.context['wizard']['steps'].current, 'form2')
87 88
 
88 89
         post_data = self.wizard_step_data[1]
89  
-        post_data['form2-file1'] = open(__file__, 'rb')
  90
+        post_data['form2-file1'] = open(upath(__file__), 'rb')
90 91
         response = self.client.post(self.wizard_url, post_data)
91 92
         self.assertEqual(response.status_code, 200)
92 93
         self.assertEqual(response.context['wizard']['steps'].current, 'form3')
@@ -99,7 +100,7 @@ def test_form_finish(self):
99 100
         self.assertEqual(response.status_code, 200)
100 101
 
101 102
         all_data = response.context['form_list']
102  
-        with open(__file__, 'rb') as f:
  103
+        with open(upath(__file__), 'rb') as f:
103 104
             self.assertEqual(all_data[1]['file1'].read(), f.read())
104 105
         all_data[1]['file1'].close()
105 106
         del all_data[1]['file1']
@@ -118,7 +119,7 @@ def test_cleaned_data(self):
118 119
         self.assertEqual(response.status_code, 200)
119 120
 
120 121
         post_data = self.wizard_step_data[1]
121  
-        with open(__file__, 'rb') as post_file:
  122
+        with open(upath(__file__), 'rb') as post_file:
122 123
             post_data['form2-file1'] = post_file
123 124
             response = self.client.post(self.wizard_url, post_data)
124 125
         self.assertEqual(response.status_code, 200)
@@ -130,7 +131,7 @@ def test_cleaned_data(self):
130 131
         self.assertEqual(response.status_code, 200)
131 132
 
132 133
         all_data = response.context['all_cleaned_data']
133  
-        with open(__file__, 'rb') as f:
  134
+        with open(upath(__file__), 'rb') as f:
134 135
             self.assertEqual(all_data['file1'].read(), f.read())
135 136
         all_data['file1'].close()
136 137
         del all_data['file1']
@@ -150,7 +151,7 @@ def test_manipulated_data(self):
150 151
 
151 152
         post_data = self.wizard_step_data[1]
152 153
         post_data['form2-file1'].close()
153  
-        post_data['form2-file1'] = open(__file__, 'rb')
  154
+        post_data['form2-file1'] = open(upath(__file__), 'rb')
154 155
         response = self.client.post(self.wizard_url, post_data)
155 156
         self.assertEqual(response.status_code, 200)
156 157
 
@@ -178,7 +179,7 @@ def test_form_refresh(self):
178 179
 
179 180
         post_data = self.wizard_step_data[1]
180 181
         post_data['form2-file1'].close()
181  
-        post_data['form2-file1'] = open(__file__, 'rb')
  182
+        post_data['form2-file1'] = open(upath(__file__), 'rb')
182 183
         response = self.client.post(self.wizard_url, post_data)
183 184
         self.assertEqual(response.status_code, 200)
184 185
         self.assertEqual(response.context['wizard']['steps'].current, 'form3')
@@ -291,7 +292,7 @@ def setUp(self):
291 292
         self.wizard_step_data[0]['form1-user'] = self.testuser.pk
292 293
 
293 294
     def test_template(self):
294  
-        templates = os.path.join(os.path.dirname(__file__), 'templates')
  295
+        templates = os.path.join(os.path.dirname(upath(__file__)), 'templates')
295 296
         with self.settings(
296 297
                 TEMPLATE_DIRS=list(settings.TEMPLATE_DIRS) + [templates]):
297 298
             response = self.client.get(self.wizard_url)
3  django/contrib/gis/geometry/test_data.py
@@ -7,13 +7,14 @@
7 7
 
8 8
 from django.contrib import gis
9 9
 from django.utils import six
  10
+from django.utils._os import upath
10 11
 
11 12
 
12 13
 # This global used to store reference geometry data.
13 14
 GEOMETRIES = None
14 15
 
15 16
 # Path where reference test data is located.
16  
-TEST_DATA = os.path.join(os.path.dirname(gis.__file__), 'tests', 'data')
  17
+TEST_DATA = os.path.join(os.path.dirname(upath(gis.__file__)), 'tests', 'data')
17 18
 
18 19
 
19 20
 def tuplize(seq):
3  django/contrib/gis/tests/geo3d/tests.py
@@ -7,12 +7,13 @@
7 7
 from django.contrib.gis.geos import GEOSGeometry, LineString, Point, Polygon
8 8
 from django.contrib.gis.utils import LayerMapping, LayerMapError
9 9
 from django.test import TestCase
  10
+from django.utils._os import upath
10 11
 
11 12
 from .models import (City3D, Interstate2D, Interstate3D, InterstateProj2D,
12 13
     InterstateProj3D, Point2D, Point3D, MultiPoint3D, Polygon2D, Polygon3D)
13 14
 
14 15
 
15  
-data_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', 'data'))
  16
+data_path = os.path.realpath(os.path.join(os.path.dirname(upath(__file__)), '..', 'data'))
16 17
 city_file = os.path.join(data_path, 'cities', 'cities.shp')
17 18
 vrt_file = os.path.join(data_path, 'test_vrt', 'test_vrt.vrt')
18 19
 
3  django/contrib/gis/tests/geogapp/tests.py
@@ -8,6 +8,7 @@
8 8
 from django.contrib.gis import gdal
9 9
 from django.contrib.gis.measure import D
10 10
 from django.test import TestCase
  11
+from django.utils._os import upath
11 12
 
12 13
 from .models import City, County, Zipcode
13 14
 
@@ -61,7 +62,7 @@ def test05_geography_layermapping(self):
61 62
         from django.contrib.gis.utils import LayerMapping
62 63
 
63 64
         # Getting the shapefile and mapping dictionary.
64  
-        shp_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', 'data'))
  65
+        shp_path = os.path.realpath(os.path.join(os.path.dirname(upath(__file__)), '..', 'data'))
65 66
         co_shp = os.path.join(shp_path, 'counties', 'counties.shp')
66 67
         co_mapping = {'name' : 'Name',
67 68
                       'state' : 'State',
3  django/contrib/gis/tests/layermap/tests.py
@@ -13,13 +13,14 @@
13 13
 from django.conf import settings
14 14
 from django.test import TestCase
15 15
 from django.utils import unittest
  16
+from django.utils._os import upath
16 17
 
17 18
 from .models import (
18 19
     City, County, CountyFeat, Interstate, ICity1, ICity2, Invalid, State,
19 20
     city_mapping, co_mapping, cofeat_mapping, inter_mapping)
20 21
 
21 22
 
22  
-shp_path = os.path.realpath(os.path.join(os.path.dirname(__file__), os.pardir, 'data'))
  23
+shp_path = os.path.realpath(os.path.join(os.path.dirname(upath(__file__)), os.pardir, 'data'))
23 24
 city_shp = os.path.join(shp_path, 'cities', 'cities.shp')
24 25
 co_shp = os.path.join(shp_path, 'counties', 'counties.shp')
25 26
 inter_shp = os.path.join(shp_path, 'interstates', 'interstates.shp')
5  django/contrib/sitemaps/tests/http.py
@@ -11,6 +11,7 @@
11 11
 from django.test.utils import override_settings
12 12
 from django.utils.unittest import skipUnless
13 13
 from django.utils.formats import localize
  14
+from django.utils._os import upath
14 15
 from django.utils.translation import activate, deactivate
15 16
 
16 17
 from .base import SitemapTestsBase
@@ -29,7 +30,7 @@ def test_simple_sitemap_index(self):
29 30
         self.assertXMLEqual(response.content.decode('utf-8'), expected_content)
30 31
 
31 32
     @override_settings(
32  
-        TEMPLATE_DIRS=(os.path.join(os.path.dirname(__file__), 'templates'),)
  33
+        TEMPLATE_DIRS=(os.path.join(os.path.dirname(upath(__file__)), 'templates'),)
33 34
     )
34 35
     def test_simple_sitemap_custom_index(self):
35 36
         "A simple sitemap index can be rendered with a custom template"
@@ -64,7 +65,7 @@ def test_simple_sitemap(self):
64 65
         self.assertXMLEqual(response.content.decode('utf-8'), expected_content)
65 66
 
66 67
     @override_settings(
67  
-        TEMPLATE_DIRS=(os.path.join(os.path.dirname(__file__), 'templates'),)
  68
+        TEMPLATE_DIRS=(os.path.join(os.path.dirname(upath(__file__)), 'templates'),)
68 69
     )
69 70
     def test_simple_custom_sitemap(self):
70 71
         "A simple sitemap can be rendered with a custom template"
3  django/contrib/staticfiles/storage.py
@@ -19,6 +19,7 @@
19 19
 from django.utils.encoding import force_bytes, force_text
20 20
 from django.utils.functional import LazyObject
21 21
 from django.utils.importlib import import_module
  22
+from django.utils._os import upath
22 23
 
23 24
 from django.contrib.staticfiles.utils import check_settings, matches_patterns
24 25
 
@@ -296,7 +297,7 @@ def __init__(self, app, *args, **kwargs):
296 297
         """
297 298
         # app is the actual app module
298 299
         mod = import_module(app)
299  
-        mod_path = os.path.dirname(mod.__file__)
  300
+        mod_path = os.path.dirname(upath(mod.__file__))
300 301
         location = os.path.join(mod_path, self.source_dir)
301 302
         super(AppStaticStorage, self).__init__(location, *args, **kwargs)
302 303
 
7  django/core/management/__init__.py
@@ -9,6 +9,7 @@
9 9
 from django.core.management.base import BaseCommand, CommandError, handle_default_options
10 10
 from django.core.management.color import color_style
11 11
 from django.utils.importlib import import_module
  12
+from django.utils._os import upath
12 13
 from django.utils import six
13 14
 
14 15
 # For backwards compatibility: get_version() used to be in this module.
@@ -410,10 +411,10 @@ def setup_environ(settings_mod, original_settings_path=None):
410 411
     # Add this project to sys.path so that it's importable in the conventional
411 412
     # way. For example, if this file (manage.py) lives in a directory
412 413
     # "myproject", this code would add "/path/to/myproject" to sys.path.
413  
-    if '__init__.py' in settings_mod.__file__:
414  
-        p = os.path.dirname(settings_mod.__file__)
  414
+    if '__init__.py' in upath(settings_mod.__file__):
  415
+        p = os.path.dirname(upath(settings_mod.__file__))
415 416
     else:
416  
-        p = settings_mod.__file__
  417
+        p = upath(settings_mod.__file__)
417 418
     project_directory, settings_filename = os.path.split(p)
418 419
     if project_directory == os.curdir or not project_directory:
419 420
         project_directory = os.getcwd()
5  django/core/management/commands/compilemessages.py
@@ -5,6 +5,7 @@
5 5
 import sys
6 6
 from optparse import make_option
7 7
 from django.core.management.base import BaseCommand, CommandError
  8
+from django.utils._os import npath
8 9
 
9 10
 def has_bom(fn):
10 11
     with open(fn, 'rb') as f:
@@ -41,8 +42,8 @@ def compile_messages(stderr, locale=None):
41 42
                     # command, so that we can take advantage of shell quoting, to
42 43
                     # quote any malicious characters/escaping.
43 44
                     # See http://cyberelk.net/tim/articles/cmdline/ar01s02.html
44  
-                    os.environ['djangocompilemo'] = pf + '.mo'
45  
-                    os.environ['djangocompilepo'] = pf + '.po'
  45
+                    os.environ['djangocompilemo'] = npath(pf + '.mo')
  46
+                    os.environ['djangocompilepo'] = npath(pf + '.po')
46 47
                     if sys.platform == 'win32': # Different shell-variable syntax
47 48
                         cmd = 'msgfmt --check-format -o "%djangocompilemo%" "%djangocompilepo%"'
48 49
                     else:
5  django/core/management/commands/loaddata.py
@@ -15,6 +15,7 @@
15 15
       IntegrityError, DatabaseError)
16 16
 from django.db.models import get_apps
17 17
 from django.utils.encoding import force_text
  18
+from django.utils._os import upath
18 19
 from itertools import product
19 20
 
20 21
 try:
@@ -102,10 +103,10 @@ def read(self):
102 103
             if hasattr(app, '__path__'):
103 104
                 # It's a 'models/' subpackage
104 105
                 for path in app.__path__:
105  
-                    app_module_paths.append(path)
  106
+                    app_module_paths.append(upath(path))
106 107
             else:
107 108
                 # It's a models.py module
108  
-                app_module_paths.append(app.__file__)
  109
+                app_module_paths.append(upath(app.__file__))
109 110
 
110 111
         app_fixtures = [os.path.join(os.path.dirname(path), 'fixtures') for path in app_module_paths]
111 112
 
6  django/core/management/commands/makemessages.py
@@ -301,7 +301,7 @@ def make_messages(locale=None, domain='django', verbosity=1, all=False,
301 301
 
302 302
     locales = []
303 303
     if locale is not None:
304  
-        locales.append(locale)
  304
+        locales.append(str(locale))
305 305
     elif all:
306 306
         locale_dirs = filter(os.path.isdir, glob.glob('%s/*' % localedir))
307 307
         locales = [os.path.basename(l) for l in locale_dirs]
@@ -316,8 +316,8 @@ def make_messages(locale=None, domain='django', verbosity=1, all=False,
316 316
         if not os.path.isdir(basedir):
317 317
             os.makedirs(basedir)
318 318
 
319  
-        pofile = os.path.join(basedir, '%s.po' % domain)
320  
-        potfile = os.path.join(basedir, '%s.pot' % domain)
  319
+        pofile = os.path.join(basedir, '%s.po' % str(domain))
  320
+        potfile = os.path.join(basedir, '%s.pot' % str(domain))
321 321
 
322 322
         if os.path.exists(potfile):
323 323
             os.unlink(potfile)
3  django/core/management/sql.py
@@ -8,6 +8,7 @@
8 8
 from django.core.management.base import CommandError
9 9
 from django.db import models
10 10
 from django.db.models import get_models
  11
+from django.utils._os import upath
11 12
 
12 13
 
13 14
 def sql_create(app, style, connection):
@@ -159,7 +160,7 @@ def _split_statements(content):
159 160
 
160 161
 def custom_sql_for_model(model, style, connection):
161 162
     opts = model._meta
162  
-    app_dir = os.path.normpath(os.path.join(os.path.dirname(models.get_app(model._meta.app_label).__file__), 'sql'))
  163
+    app_dir = os.path.normpath(os.path.join(os.path.dirname(upath(models.get_app(model._meta.app_label).__file__)), 'sql'))
163 164
     output = []
164 165
 
165 166
     # Post-creation SQL should come before any initial SQL data is loaded.
4  django/core/urlresolvers.py
@@ -251,9 +251,9 @@ def __repr__(self):
251 251
             urlconf_repr = '<%s list>' % self.urlconf_name[0].__class__.__name__
252 252
         else:
253 253
             urlconf_repr = repr(self.urlconf_name)
254  
-        return force_str('<%s %s (%s:%s) %s>' % (
  254
+        return str('<%s %s (%s:%s) %s>') % (
255 255
             self.__class__.__name__, urlconf_repr, self.app_name,
256  
-            self.namespace, self.regex.pattern))
  256
+            self.namespace, self.regex.pattern)
257 257
 
258 258
     def _populate(self):
259 259
         lookups = MultiValueDict()
5  django/db/models/loading.py
@@ -5,6 +5,7 @@
5 5
 from django.utils.datastructures import SortedDict
6 6
 from django.utils.importlib import import_module
7 7
 from django.utils.module_loading import module_has_submodule
  8
+from django.utils._os import upath
8 9
 from django.utils import six
9 10
 
10 11
 import imp
@@ -244,8 +245,8 @@ def register_models(self, app_label, *models):
244 245
                 # The same model may be imported via different paths (e.g.
245 246
                 # appname.models and project.appname.models). We use the source
246 247
                 # filename as a means to detect identity.
247  
-                fname1 = os.path.abspath(sys.modules[model.__module__].__file__)
248  
-                fname2 = os.path.abspath(sys.modules[model_dict[model_name].__module__].__file__)
  248
+                fname1 = os.path.abspath(upath(sys.modules[model.__module__].__file__))
  249
+                fname2 = os.path.abspath(upath(sys.modules[model_dict[model_name].__module__].__file__))
249 250
                 # Since the filename extension could be .py the first time and
250 251
                 # .pyc or .pyo the second time, ignore the extension when
251 252
                 # comparing.
3  django/db/utils.py
@@ -5,6 +5,7 @@
5 5
 from django.conf import settings
6 6
 from django.core.exceptions import ImproperlyConfigured
7 7
 from django.utils.importlib import import_module
  8
+from django.utils._os import upath
8 9
 from django.utils import six
9 10
 
10 11
 
@@ -27,7 +28,7 @@ def load_backend(backend_name):
27 28
     except ImportError as e_user:
28 29
         # The database backend wasn't found. Display a helpful error message
29 30
         # listing all possible (built-in) database backends.
30  
-        backend_dir = os.path.join(os.path.dirname(__file__), 'backends')
  31
+        backend_dir = os.path.join(os.path.dirname(upath(__file__)), 'backends')
31 32
         try:
32 33
             builtin_backends = [
33 34
                 name for _, name, ispkg in pkgutil.iter_modules([backend_dir])
23  django/utils/_os.py
... ...
@@ -1,6 +1,8 @@
1 1
 import os
2 2
 import stat
  3
+import sys
3 4
 from os.path import join, normcase, normpath, abspath, isabs, sep, dirname
  5
+
4 6
 from django.utils.encoding import force_text
5 7
 from django.utils import six
6 8
 
@@ -10,6 +12,9 @@
10 12
     class WindowsError(Exception):
11 13
         pass
12 14
 
  15
+if not six.PY3:
  16
+    fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
  17
+
13 18
 
14 19
 # Under Python 2, define our own abspath function that can handle joining
15 20
 # unicode paths to a current working directory that has non-ASCII characters
@@ -29,6 +34,23 @@ def abspathu(path):
29 34
             path = join(os.getcwdu(), path)
30 35
         return normpath(path)
31 36
 
  37
+def upath(path):
  38
+    """
  39
+    Always return a unicode path.
  40
+    """
  41
+    if not six.PY3:
  42
+        return path.decode(fs_encoding)
  43
+    return path
  44
+
  45
+def npath(path):
  46
+    """
  47
+    Always return a native path, that is unicode on Python 3 and bytestring on
  48
+    Python 2.
  49
+    """
  50
+    if not six.PY3 and not isinstance(path, bytes):
  51
+        return path.encode(fs_encoding)
  52
+    return path
  53
+
32 54
 def safe_join(base, *paths):
33 55
     """
34 56
     Joins one or more path components to the base path component intelligently.
@@ -74,4 +96,3 @@ def rmtree_errorhandler(func, path, exc_info):
74 96
     os.chmod(path, stat.S_IWRITE)
75 97
     # use the original function to repeat the operation
76 98
     func(path)
77  
-
7  django/utils/translation/trans_real.py
@@ -10,6 +10,7 @@
10 10
 
11 11
 from django.utils.importlib import import_module
12 12
 from django.utils.encoding import force_str, force_text
  13
+from django.utils._os import upath
13 14
 from django.utils.safestring import mark_safe, SafeData
14 15
 from django.utils import six
15 16
 from django.utils.six import StringIO
@@ -109,7 +110,7 @@ def translation(language):
109 110
 
110 111
     from django.conf import settings
111 112
 
112  
-    globalpath = os.path.join(os.path.dirname(sys.modules[settings.__module__].__file__), 'locale')
  113
+    globalpath = os.path.join(os.path.dirname(upath(sys.modules[settings.__module__].__file__)), 'locale')
113 114
 
114 115
     def _fetch(lang, fallback=None):
115 116
 
@@ -151,7 +152,7 @@ def _merge(path):
151 152
 
152 153
         for appname in reversed(settings.INSTALLED_APPS):
153 154
             app = import_module(appname)
154  
-            apppath = os.path.join(os.path.dirname(app.__file__), 'locale')
  155
+            apppath = os.path.join(os.path.dirname(upath(app.__file__)), 'locale')
155 156
 
156 157
             if os.path.isdir(apppath):
157 158
                 res = _merge(apppath)
@@ -337,7 +338,7 @@ def all_locale_paths():
337 338
     """
338 339
     from django.conf import settings
339 340
     globalpath = os.path.join(
340  
-        os.path.dirname(sys.modules[settings.__module__].__file__), 'locale')
  341
+        os.path.dirname(upath(sys.modules[settings.__module__].__file__)), 'locale')
341 342
     return [globalpath] + list(settings.LOCALE_PATHS)
342 343
 
343 344
 def check_for_language(lang_code):
3  django/views/i18n.py
@@ -8,6 +8,7 @@
8 8
 from django.utils.text import javascript_quote
9 9
 from django.utils.encoding import smart_text
10 10
 from django.utils.formats import get_format_modules, get_format
  11
+from django.utils._os import upath
11 12
 from django.utils import six
12 13
 
13 14
 def set_language(request):
@@ -197,7 +198,7 @@ def javascript_catalog(request, domain='djangojs', packages=None):
197 198
     # paths of requested packages
198 199
     for package in packages:
199 200
         p = importlib.import_module(package)
200  
-        path = os.path.join(os.path.dirname(p.__file__), 'locale')
  201
+        path = os.path.join(os.path.dirname(upath(p.__file__)), 'locale')
201 202
         paths.append(path)
202 203
     # add the filesystem paths listed in the LOCALE_PATHS setting
203 204
     paths.extend(list(reversed(settings.LOCALE_PATHS)))
12  tests/modeltests/fixtures/tests.py
@@ -226,9 +226,9 @@ def test_compressed_loading(self):
226 226
 
227 227
     def test_ambiguous_compressed_fixture(self):
228 228
         # The name "fixture5" is ambigous, so loading it will raise an error
229  
-        with six.assertRaisesRegex(self, management.CommandError,
230  
-                "Multiple fixtures named 'fixture5'"):
  229
+        with self.assertRaises(management.CommandError) as cm:
231 230
             management.call_command('loaddata', 'fixture5', verbosity=0, commit=False)
  231
+            self.assertIn("Multiple fixtures named 'fixture5'", cm.exception.args[0])
232 232
 
233 233
     def test_db_loading(self):
234 234
         # Load db fixtures 1 and 2. These will load using the 'default' database identifier implicitly
@@ -250,9 +250,9 @@ def test_loaddata_error_message(self):
250 250
         # is closed at the end of each test.
251 251
         if connection.vendor == 'mysql':
252 252
             connection.cursor().execute("SET sql_mode = 'TRADITIONAL'")
253  
-        with six.assertRaisesRegex(self, IntegrityError,
254  
-                "Could not load fixtures.Article\(pk=1\): .*$"):
  253
+        with self.assertRaises(IntegrityError) as cm:
255 254
             management.call_command('loaddata', 'invalid.json', verbosity=0, commit=False)
  255
+            self.assertIn("Could not load fixtures.Article(pk=1):", cm.exception.args[0])
256 256
 
257 257
     def test_loading_using(self):
258 258
         # Load db fixtures 1 and 2. These will load using the 'default' database identifier explicitly
@@ -308,9 +308,9 @@ def test_format_discovery(self):
308 308
 
309 309
         # Try to load fixture 2 using format discovery; this will fail
310 310
         # because there are two fixture2's in the fixtures directory
311  
-        with six.assertRaisesRegex(self, management.CommandError,
312  
-                "Multiple fixtures named 'fixture2'"):
  311
+        with self.assertRaises(management.CommandError) as cm:
313 312
             management.call_command('loaddata', 'fixture2', verbosity=0)
  313
+            self.assertIn("Multiple fixtures named 'fixture2'", cm.exception.args[0])
314 314
 
315 315
         # object list is unaffected
316 316
         self.assertQuerysetEqual(Article.objects.all(), [
5  tests/modeltests/model_forms/tests.py
@@ -10,6 +10,7 @@
10 10
 from django.db import connection
11 11
 from django.db.models.query import EmptyQuerySet
12 12
 from django.forms.models import model_to_dict
  13
+from django.utils._os import upath
13 14
 from django.utils.unittest import skipUnless
14 15
 from django.test import TestCase
15 16
 from django.utils import six
@@ -1282,9 +1283,9 @@ def test_image_field(self):
1282 1283
         # it comes to validation. This specifically tests that #6302 is fixed for
1283 1284
         # both file fields and image fields.
1284 1285
 
1285  
-        with open(os.path.join(os.path.dirname(__file__), "test.png"), 'rb') as fp:
  1286
+        with open(os.path.join(os.path.dirname(upath(__file__)), "test.png"), 'rb') as fp:
1286 1287
             image_data = fp.read()
1287  
-        with open(os.path.join(os.path.dirname(__file__), "test2.png"), 'rb') as fp:
  1288
+        with open(os.path.join(os.path.dirname(upath(__file__)), "test2.png"), 'rb') as fp:
1288 1289
             image_data2 = fp.read()
1289 1290
 
1290 1291
         f = ImageFileForm(
3  tests/modeltests/proxy_model_inheritance/tests.py
@@ -8,6 +8,7 @@
8 8
 from django.db.models.loading import cache, load_app
9 9
 from django.test import TestCase, TransactionTestCase
10 10
 from django.test.utils import override_settings
  11
+from django.utils._os import upath
11 12
 
12 13
 from .models import (ConcreteModel, ConcreteModelSubclass,
13 14
     ConcreteModelSubclassProxy)
@@ -23,7 +24,7 @@ class ProxyModelInheritanceTests(TransactionTestCase):
23 24
 
24 25
     def setUp(self):
25 26
         self.old_sys_path = sys.path[:]
26  
-        sys.path.append(os.path.dirname(os.path.abspath(__file__)))
  27
+        sys.path.append(os.path.dirname(os.path.abspath(upath(__file__))))
27 28
         for app in settings.INSTALLED_APPS:
28 29
             load_app(app)
29 30
 
15  tests/regressiontests/admin_scripts/tests.py
@@ -19,13 +19,15 @@
19 19
 from django.db import connection
20 20
 from django.test.simple import DjangoTestSuiteRunner
21 21
 from django.utils import unittest
  22
+from django.utils.encoding import force_str, force_text
  23
+from django.utils._os import upath
22 24
 from django.test import LiveServerTestCase
23 25
 
24  
-test_dir = os.path.dirname(os.path.dirname(__file__))
  26
+test_dir = os.path.dirname(os.path.dirname(upath(__file__)))
25 27
 
26 28
 class AdminScriptTestCase(unittest.TestCase):
27 29
     def write_settings(self, filename, apps=None, is_dir=False, sdict=None):
28  
-        test_dir = os.path.dirname(os.path.dirname(__file__))
  30
+        test_dir = os.path.dirname(os.path.dirname(upath(__file__)))
29 31
         if is_dir:
30 32
             settings_dir = os.path.join(test_dir, filename)
31 33
             os.mkdir(settings_dir)
@@ -94,6 +96,7 @@ def _ext_backend_paths(self):
94 96
         return paths
95 97
 
96 98
     def run_test(self, script, args, settings_file=None, apps=None):
  99
+        test_dir = os.path.dirname(os.path.dirname(__file__))
97 100
         project_dir = os.path.dirname(test_dir)
98 101
         base_dir = os.path.dirname(project_dir)
99 102
         ext_backend_base_dirs = self._ext_backend_paths()
@@ -134,7 +137,7 @@ def run_test(self, script, args, settings_file=None, apps=None):
134 137
         return out, err
135 138
 
136 139
     def run_django_admin(self, args, settings_file=None):
137  
-        bin_dir = os.path.abspath(os.path.dirname(bin.__file__))
  140
+        bin_dir = os.path.abspath(os.path.dirname(upath(bin.__file__)))
138 141
         return self.run_test(os.path.join(bin_dir, 'django-admin.py'), args, settings_file)
139 142
 
140 143
     def run_manage(self, args, settings_file=None):
@@ -144,7 +147,7 @@ def safe_remove(path):
144 147
             except OSError:
145 148
                 pass
146 149
 
147  
-        conf_dir = os.path.dirname(conf.__file__)
  150
+        conf_dir = os.path.dirname(upath(conf.__file__))
148 151
         template_manage_py = os.path.join(conf_dir, 'project_template', 'manage.py')
149 152
 
150 153
         test_manage_py = os.path.join(test_dir, 'manage.py')
@@ -166,10 +169,12 @@ def assertNoOutput(self, stream):
166 169
 
167 170
     def assertOutput(self, stream, msg):
168 171
         "Utility assertion: assert that the given message exists in the output"
  172
+        stream = force_text(stream)
169 173
         self.assertTrue(msg in stream, "'%s' does not match actual output text '%s'" % (msg, stream))
170 174
 
171 175
     def assertNotInOutput(self, stream, msg):
172 176
         "Utility assertion: assert that the given message doesn't exist in the output"
  177
+        stream = force_text(stream)
173 178
         self.assertFalse(msg in stream, "'%s' matches actual output text '%s'" % (msg, stream))
174 179
 
175 180
 ##########################################################################
@@ -1553,7 +1558,7 @@ def test_custom_project_template_context_variables(self):
1553 1558
         self.assertNoOutput(err)
1554 1559
         test_manage_py = os.path.join(testproject_dir, 'manage.py')
1555 1560
         with open(test_manage_py, 'r') as fp:
1556  
-            content = fp.read()
  1561
+            content = force_text(fp.read())
1557 1562
             self.assertIn("project_name = 'another_project'", content)
1558 1563
             self.assertIn("project_directory = '%s'" % testproject_dir, content)
1559 1564
 
3  tests/regressiontests/admin_scripts/urls.py
... ...
@@ -1,7 +1,8 @@
1 1
 import os
2 2
 from django.conf.urls import patterns
  3
+from django.utils._os import upath
3 4
 
4  
-here = os.path.dirname(__file__)
  5
+here = os.path.dirname(upath(__file__))
5 6
 
6 7
 urlpatterns = patterns('',
7 8
     (r'^custom_templates/(?P<path>.*)$', 'django.views.static.serve', {
3  tests/regressiontests/admin_views/tests.py
@@ -33,6 +33,7 @@
33 33
 from django.utils.encoding import iri_to_uri, force_bytes
34 34
 from django.utils.html import escape
35 35
 from django.utils.http import urlencode
  36
+from django.utils._os import upath
36 37
 from django.utils import six
37 38
 from django.test.utils import override_settings
38 39
 
@@ -633,7 +634,7 @@ def test_filter_with_custom_template(self):
633 634
         Refs #17515.
634 635
         """
635 636
         template_dirs = settings.TEMPLATE_DIRS + (
636  
-            os.path.join(os.path.dirname(__file__), 'templates'),)
  637
+            os.path.join(os.path.dirname(upath(__file__)), 'templates'),)
637 638
         with self.settings(TEMPLATE_DIRS=template_dirs):
638 639
             response = self.client.get("/test_admin/admin/admin_views/color2/")
639 640
             self.assertTrue('custom_filter_template.html' in [t.name for t in response.templates])
3  tests/regressiontests/app_loading/tests.py
@@ -7,13 +7,14 @@
7 7
 
8 8
 from django.conf import Settings
9 9
 from django.db.models.loading import cache, load_app, get_model, get_models
  10
+from django.utils._os import upath
10 11
 from django.utils.unittest import TestCase
11 12
 
12 13
 class EggLoadingTest(TestCase):
13 14
 
14 15
     def setUp(self):
15 16
         self.old_path = sys.path[:]
16  
-        self.egg_dir = '%s/eggs' % os.path.dirname(__file__)
  17
+        self.egg_dir = '%s/eggs' % os.path.dirname(upath(__file__))
17 18
 
18 19
         # This test adds dummy applications to the app cache. These
19 20
         # need to be removed in order to prevent bad interactions
3  tests/regressiontests/bug639/tests.py
@@ -11,6 +11,7 @@
11 11
 
12 12
 from django.core.files.uploadedfile import SimpleUploadedFile
13 13
 from django.utils import unittest
  14
+from django.utils._os import upath
14 15
 
15 16
 from .models import Photo, PhotoForm, temp_storage_dir
16 17
 
@@ -23,7 +24,7 @@ def testBug639(self):
23 24
         called.
24 25
         """
25 26
         # Grab an image for testing.
26  
-        filename = os.path.join(os.path.dirname(__file__), "test.jpg")
  27
+        filename = os.path.join(os.path.dirname(upath(__file__)), "test.jpg")
27 28
         with open(filename, "rb") as fp:
28 29
             img = fp.read()
29 30
 
7  tests/regressiontests/file_storage/tests.py
@@ -24,6 +24,7 @@
24 24
 from django.test import SimpleTestCase
25 25
 from django.utils import six
26 26
 from django.utils import unittest
  27
+from django.utils._os import upath
27 28
 from django.test.utils import override_settings
28 29
 from ..servers.tests import LiveServerBase
29 30
 
@@ -104,7 +105,7 @@ def test_emtpy_location(self):
104 105
         """
105 106
         storage = self.storage_class(location='')
106 107
         self.assertEqual(storage.base_location, '')
107  
-        self.assertEqual(storage.location, os.getcwd())
  108
+        self.assertEqual(storage.location, upath(os.getcwd()))
108 109
 
109 110
     def test_file_access_options(self):
110 111
         """
@@ -534,7 +535,7 @@ def catching_open(*args):
534 535
         from django.core.files import images
535 536
         images.open = catching_open
536 537
         try:
537  
-            get_image_dimensions(os.path.join(os.path.dirname(__file__), "test1.png"))
  538
+            get_image_dimensions(os.path.join(os.path.dirname(upath(__file__)), "test1.png"))
538 539
         finally:
539 540
             del images.open
540 541
         self.assertTrue(FileWrapper._closed)
@@ -551,7 +552,7 @@ def test_multiple_calls(self):
551 552
         """
552 553
         from django.core.files.images import ImageFile
553 554
 
554  
-        img_path = os.path.join(os.path.dirname(__file__), "test.png")
  555
+        img_path = os.path.join(os.path.dirname(upath(__file__)), "test.png")
555 556
         image = ImageFile(open(img_path, 'rb'))
556 557
         image_pil = Image.open(img_path)
557 558
         size_1, size_2 = get_image_dimensions(image), get_image_dimensions(image)
8  tests/regressiontests/fixtures_regress/tests.py
@@ -14,6 +14,8 @@
14 14
 from django.test import (TestCase, TransactionTestCase, skipIfDBFeature,
15 15
     skipUnlessDBFeature)
16 16
 from django.test.utils import override_settings
  17
+from django.utils.encoding import force_text
  18
+from django.utils._os import upath
17 19
 from django.utils import six
18 20
 from django.utils.six import PY3, StringIO
19 21
 
@@ -126,7 +128,7 @@ def test_absolute_path(self):
126 128
         fixture directory.
127 129
         """
128 130
         load_absolute_path = os.path.join(
129  
-            os.path.dirname(__file__),
  131
+            os.path.dirname(upath(__file__)),
130 132
             'fixtures',
131 133
             'absolute.json'
132 134
         )
@@ -388,7 +390,7 @@ def test_loaddata_raises_error_when_fixture_has_invalid_foreign_key(self):
388 390
                 commit=False,
389 391
             )
390 392
 
391  
-    _cur_dir = os.path.dirname(os.path.abspath(__file__))
  393
+    _cur_dir = os.path.dirname(os.path.abspath(upath(__file__)))
392 394
 
393 395
     @override_settings(FIXTURE_DIRS=[os.path.join(_cur_dir, 'fixtures_1'),
394 396
                                      os.path.join(_cur_dir, 'fixtures_2')])
@@ -430,7 +432,7 @@ def test_loaddata_not_existant_fixture_file(self):
430 432
             stdout=stdout_output,
431 433
         )
432 434
         self.assertTrue("No xml fixture 'this_fixture_doesnt_exist' in" in
433  
-            stdout_output.getvalue())
  435
+            force_text(stdout_output.getvalue()))
434 436
 
435 437
 
436 438
 class NaturalKeyFixtureTests(TestCase):
11  tests/regressiontests/forms/tests/fields.py
@@ -36,6 +36,7 @@
36 36
 from django.forms import *
37 37
 from django.test import SimpleTestCase
38 38
 from django.utils import six
  39
+from django.utils._os import upath
39 40
 
40 41
 
41 42
 def fix_os_paths(x):
@@ -928,12 +929,12 @@ def test_combofield_2(self):
928 929
     # FilePathField ###############################################################
929 930
 
930 931
     def test_filepathfield_1(self):
931  
-        path = os.path.abspath(forms.__file__)
  932
+        path = os.path.abspath(upath(forms.__file__))
932 933
         path = os.path.dirname(path) + '/'
933 934
         self.assertTrue(fix_os_paths(path).endswith('/django/forms/'))
934 935
 
935 936
     def test_filepathfield_2(self):
936  
-        path = forms.__file__
  937
+        path = upath(forms.__file__)
937 938
         path = os.path.dirname(os.path.abspath(path)) + '/'
938 939
         f = FilePathField(path=path)
939 940
         f.choices = [p for p in f.choices if p[0].endswith('.py')]
@@ -954,7 +955,7 @@ def test_filepathfield_2(self):
954 955
         assert fix_os_paths(f.clean(path + 'fields.py')).endswith('/django/forms/fields.py')
955 956
 
956 957
     def test_filepathfield_3(self):
957  
-        path = forms.__file__
  958
+        path = upath(forms.__file__)
958 959
         path = os.path.dirname(os.path.abspath(path)) + '/'
959 960
         f = FilePathField(path=path, match='^.*?\.py$')
960 961
         f.choices.sort()
@@ -972,7 +973,7 @@ def test_filepathfield_3(self):
972 973
             self.assertTrue(got[0].endswith(exp[0]))
973 974
 
974 975
     def test_filepathfield_4(self):
975  
-        path = os.path.abspath(forms.__file__)
  976
+        path = os.path.abspath(upath(forms.__file__))
976 977
         path = os.path.dirname(path) + '/'
977 978
         f = FilePathField(path=path, recursive=True, match='^.*?\.py$')
978 979
         f.choices.sort()
@@ -992,7 +993,7 @@ def test_filepathfield_4(self):
992 993
             self.assertTrue(got[0].endswith(exp[0]))
993 994
 
994 995
     def test_filepathfield_folders(self):
995  
-        path = os.path.dirname(__file__) + '/filepath_test_files/'
  996
+        path = os.path.dirname(upath(__file__)) + '/filepath_test_files/'
996 997
         f = FilePathField(path=path, allow_folders=True, allow_files=False)
997 998
         f.choices.sort()
998 999
         expected = [
5  tests/regressiontests/httpwrappers/tests.py
@@ -14,6 +14,7 @@
14 14
                          parse_cookie)
15 15
 from django.test import TestCase
16 16
 from django.utils.encoding import smart_str
  17
+from django.utils._os import upath
17 18
 from django.utils import six
18 19
 from django.utils import unittest
19 20
 
@@ -483,7 +484,7 @@ def test_streaming_response(self):
483 484
 
484 485
 class FileCloseTests(TestCase):
485 486
     def test_response(self):
486  
-        filename = os.path.join(os.path.dirname(__file__), 'abc.txt')
  487
+        filename = os.path.join(os.path.dirname(upath(__file__)), 'abc.txt')
487 488
 
488 489
         # file isn't closed until we close the response.
489 490
         file1 = open(filename)
@@ -516,7 +517,7 @@ def test_response(self):
516 517
         self.assertTrue(file2.closed)
517 518
 
518 519
     def test_streaming_response(self):
519  
-        filename = os.path.join(os.path.dirname(__file__), 'abc.txt')
  520
+        filename = os.path.join(os.path.dirname(upath(__file__)), 'abc.txt')
520 521
 
521 522
         # file isn't closed until we close the response.
522 523
         file1 = open(filename)
7  tests/regressiontests/i18n/commands/compilation.py
@@ -4,9 +4,10 @@
4 4
 from django.test import TestCase
5 5
 from django.test.utils import override_settings
6 6
 from django.utils import translation, six
  7
+from django.utils._os import upath
7 8
 from django.utils.six import StringIO
8 9
 
9  
-test_dir = os.path.abspath(os.path.dirname(__file__))
  10
+test_dir = os.path.abspath(os.path.dirname(upath(__file__)))
10 11
 
11 12
 
12 13
 class MessageCompilationTests(TestCase):
@@ -25,9 +26,9 @@ class PoFileTests(MessageCompilationTests):
25 26
 
26 27
     def test_bom_rejection(self):
27 28
         os.chdir(test_dir)
28  
-        with six.assertRaisesRegex(self, CommandError,
29  
-                "file has a BOM \(Byte Order Mark\)"):
  29
+        with self.assertRaises(CommandError) as cm:
30 30
             call_command('compilemessages', locale=self.LOCALE, stderr=StringIO())
  31
+        self.assertIn("file has a BOM (Byte Order Mark)", cm.exception.args[0])
31 32
         self.assertFalse(os.path.exists(self.MO_FILE))
32 33
 
33 34
 
31  tests/regressiontests/i18n/commands/extraction.py
... ...
@@ -1,4 +1,5 @@
1 1
 # -*- encoding: utf-8 -*-
  2
+from __future__ import unicode_literals
2 3
 
3 4
 import os
4 5
 import re
@@ -6,6 +7,8 @@
6 7
 
7 8
 from django.core import management
8 9
 from django.test import TestCase
  10
+from django.utils.encoding import force_text
  11
+from django.utils._os import upath
9 12
 from django.utils.six import StringIO
10 13
 
11 14
 
@@ -17,7 +20,7 @@ class ExtractorTests(TestCase):
17 20
 
18 21
     def setUp(self):
19 22
         self._cwd = os.getcwd()
20  
-        self.test_dir = os.path.abspath(os.path.dirname(__file__))
  23
+        self.test_dir = os.path.abspath(os.path.dirname(upath(__file__)))
21 24
 
22 25
     def _rmrf(self, dname):
23 26
         if os.path.commonprefix([self.test_dir, os.path.abspath(dname)]) != self.test_dir:
@@ -55,7 +58,7 @@ def test_comments_extractor(self):
55 58
         management.call_command('makemessages', locale=LOCALE, verbosity=0)
56 59
         self.assertTrue(os.path.exists(self.PO_FILE))
57 60
         with open(self.PO_FILE, 'r') as fp:
58  
-            po_contents = fp.read()
  61
+            po_contents = force_text(fp.read())
59 62
             self.assertTrue('#. Translators: This comment should be extracted' in po_contents)
60 63
             self.assertTrue('This comment should not be extracted' not in po_contents)
61 64
             # Comments in templates
@@ -83,7 +86,7 @@ def test_templatize_trans_tag(self):
83 86
         management.call_command('makemessages', locale=LOCALE, verbosity=0)
84 87
         self.assertTrue(os.path.exists(self.PO_FILE))
85 88
         with open(self.PO_FILE, 'r') as fp:
86  
-            po_contents = fp.read()
  89
+            po_contents = force_text(fp.read())
87 90
             self.assertMsgId('Literal with a percent symbol at the end %%', po_contents)
88 91
             self.assertMsgId('Literal with a percent %% symbol in the middle', po_contents)
89 92
             self.assertMsgId('Completed 50%% of all the tasks', po_contents)
@@ -99,7 +102,7 @@ def test_templatize_blocktrans_tag(self):
99 102
         management.call_command('makemessages', locale=LOCALE, verbosity=0)
100 103
         self.assertTrue(os.path.exists(self.PO_FILE))
101 104
         with open(self.PO_FILE, 'r') as fp:
102  
-            po_contents = fp.read()
  105
+            po_contents = force_text(fp.read())
103 106
             self.assertMsgId('I think that 100%% is more that 50%% of anything.', po_contents)
104 107
             self.assertMsgId('I think that 100%% is more that 50%% of %(obj)s.', po_contents)
105 108
             self.assertMsgId("Blocktrans extraction shouldn't double escape this: %%, a=%(a)s", po_contents)
@@ -123,7 +126,7 @@ def test_extraction_warning(self):
123 126
         stdout = StringIO()
124 127
         management.call_command('makemessages', locale=LOCALE, stdout=stdout)
125 128
         os.remove('./code_sample.py')
126  
-        self.assertIn("code_sample.py:4", stdout.getvalue())
  129
+        self.assertIn("code_sample.py:4", force_text(stdout.getvalue()))
127 130
 
128 131
     def test_template_message_context_extractor(self):
129 132
         """
@@ -135,7 +138,7 @@ def test_template_message_context_extractor(self):
135 138
         management.call_command('makemessages', locale=LOCALE, verbosity=0)
136 139
         self.assertTrue(os.path.exists(self.PO_FILE))
137 140
         with open(self.PO_FILE, 'r') as fp:
138  
-            po_contents = fp.read()
  141
+            po_contents = force_text(fp.read())
139 142
             # {% trans %}
140 143
             self.assertTrue('msgctxt "Special trans context #1"' in po_contents)
141 144
             self.assertTrue("Translatable literal #7a" in po_contents)
@@ -161,7 +164,7 @@ def test_context_in_single_quotes(self):
161 164
         management.call_command('makemessages', locale=LOCALE, verbosity=0)
162 165
         self.assertTrue(os.path.exists(self.PO_FILE))
163 166
         with open(self.PO_FILE, 'r') as fp:
164  
-            po_contents = fp.read()
  167
+            po_contents = force_text(fp.read())
165 168
             # {% trans %}
166 169
             self.assertTrue('msgctxt "Context wrapped in double quotes"' in po_contents)
167 170
             self.assertTrue('msgctxt "Context wrapped in single quotes"' in po_contents)
@@ -216,7 +219,7 @@ class SymlinkExtractorTests(ExtractorTests):
216 219
 
217 220
     def setUp(self):
218 221
         self._cwd = os.getcwd()
219  
-        self.test_dir = os.path.abspath(os.path.dirname(__file__))
  222
+        self.test_dir = os.path.abspath(os.path.dirname(upath(__file__)))
220 223
         self.symlinked_dir = os.path.join(self.test_dir, 'templates_symlinked')
221 224
 
222 225
     def tearDown(self):
@@ -238,7 +241,7 @@ def test_symlink(self):
238 241
             management.call_command('makemessages', locale=LOCALE, verbosity=0, symlinks=True)
239 242
             self.assertTrue(os.path.exists(self.PO_FILE))
240 243
             with open(self.PO_FILE, 'r') as fp:
241  
-                po_contents = fp.read()
  244
+                po_contents = force_text(fp.read())
242 245
                 self.assertMsgId('This literal should be included.', po_contents)
243 246
                 self.assertTrue('templates_symlinked/test.html' in po_contents)
244 247
 
@@ -250,7 +253,7 @@ def test_copy_plural_forms(self):
250 253
         management.call_command('makemessages', locale=LOCALE, verbosity=0)
251 254
         self.assertTrue(os.path.exists(self.PO_FILE))
252 255
         with open(self.PO_FILE, 'r') as fp:
253  
-            po_contents = fp.read()