Refs #29983 -- Added support for using pathlib.Path in all settings.

This commit is contained in:
Jon Dufresne 2019-11-07 01:26:22 -08:00 committed by Carlton Gibson
parent 367634f976
commit 77aa74cb70
18 changed files with 118 additions and 10 deletions

View File

@ -21,7 +21,7 @@ class Command(LabelCommand):
if verbosity >= 2: if verbosity >= 2:
searched_locations = ( searched_locations = (
"\nLooking in the following locations:\n %s" % "\nLooking in the following locations:\n %s" %
"\n ".join(finders.searched_locations) "\n ".join([str(loc) for loc in finders.searched_locations])
) )
else: else:
searched_locations = '' searched_locations = ''

View File

@ -174,7 +174,9 @@ class DatabaseWrapper(BaseDatabaseWrapper):
"settings.DATABASES is improperly configured. " "settings.DATABASES is improperly configured. "
"Please supply the NAME value.") "Please supply the NAME value.")
kwargs = { kwargs = {
'database': settings_dict['NAME'], # TODO: Remove str() when dropping support for PY36.
# https://bugs.python.org/issue33496
'database': str(settings_dict['NAME']),
'detect_types': Database.PARSE_DECLTYPES | Database.PARSE_COLNAMES, 'detect_types': Database.PARSE_DECLTYPES | Database.PARSE_COLNAMES,
**settings_dict['OPTIONS'], **settings_dict['OPTIONS'],
} }

View File

@ -1,6 +1,7 @@
import os import os
import shutil import shutil
import sys import sys
from pathlib import Path
from django.db.backends.base.creation import BaseDatabaseCreation from django.db.backends.base.creation import BaseDatabaseCreation
@ -9,7 +10,9 @@ class DatabaseCreation(BaseDatabaseCreation):
@staticmethod @staticmethod
def is_in_memory_db(database_name): def is_in_memory_db(database_name):
return database_name == ':memory:' or 'mode=memory' in database_name return not isinstance(database_name, Path) and (
database_name == ':memory:' or 'mode=memory' in database_name
)
def _get_test_db_name(self): def _get_test_db_name(self):
test_database_name = self.connection.settings_dict['TEST']['NAME'] or ':memory:' test_database_name = self.connection.settings_dict['TEST']['NAME'] or ':memory:'

View File

@ -39,7 +39,7 @@ class EngineMixin:
def engine(self): def engine(self):
return self.backend({ return self.backend({
'APP_DIRS': True, 'APP_DIRS': True,
'DIRS': [str(ROOT / self.backend.app_dirname)], 'DIRS': [ROOT / self.backend.app_dirname],
'NAME': 'djangoforms', 'NAME': 'djangoforms',
'OPTIONS': {}, 'OPTIONS': {},
}) })

View File

@ -99,7 +99,7 @@ def get_app_template_dirs(dirname):
installed applications. installed applications.
""" """
template_dirs = [ template_dirs = [
str(Path(app_config.path) / dirname) Path(app_config.path) / dirname
for app_config in apps.get_app_configs() for app_config in apps.get_app_configs()
if app_config.path and (Path(app_config.path) / dirname).is_dir() if app_config.path and (Path(app_config.path) / dirname).is_dir()
] ]

View File

@ -96,7 +96,7 @@ Minor features
:mod:`django.contrib.staticfiles` :mod:`django.contrib.staticfiles`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* ... * The :setting:`STATICFILES_DIRS` setting now supports :class:`pathlib.Path`.
:mod:`django.contrib.syndication` :mod:`django.contrib.syndication`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -226,6 +226,12 @@ Validators
* ... * ...
Miscellaneous
~~~~~~~~~~~~~
* The SQLite backend now supports :class:`pathlib.Path` for the ``NAME``
setting.
.. _backwards-incompatible-3.1: .. _backwards-incompatible-3.1:
Backwards incompatible changes in 3.1 Backwards incompatible changes in 3.1

View File

@ -1,11 +1,14 @@
import os
import re import re
import tempfile
import threading import threading
import unittest import unittest
from pathlib import Path
from sqlite3 import dbapi2 from sqlite3 import dbapi2
from unittest import mock from unittest import mock
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.db import connection, transaction from django.db import ConnectionHandler, connection, transaction
from django.db.models import Avg, StdDev, Sum, Variance from django.db.models import Avg, StdDev, Sum, Variance
from django.db.models.aggregates import Aggregate from django.db.models.aggregates import Aggregate
from django.db.models.fields import CharField from django.db.models.fields import CharField
@ -89,6 +92,19 @@ class Tests(TestCase):
value = bool(value) if value in {0, 1} else value value = bool(value) if value in {0, 1} else value
self.assertIs(value, expected) self.assertIs(value, expected)
def test_pathlib_name(self):
with tempfile.TemporaryDirectory() as tmp:
settings_dict = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': Path(tmp) / 'test.db',
},
}
connections = ConnectionHandler(settings_dict)
connections['default'].ensure_connection()
connections['default'].close()
self.assertTrue(os.path.isfile(os.path.join(tmp, 'test.db')))
@unittest.skipUnless(connection.vendor == 'sqlite', 'SQLite tests') @unittest.skipUnless(connection.vendor == 'sqlite', 'SQLite tests')
@isolate_apps('backends') @isolate_apps('backends')

View File

@ -227,3 +227,12 @@ class AppCompilationTest(ProjectAndAppTests):
call_command('compilemessages', locale=[self.LOCALE], stdout=StringIO()) call_command('compilemessages', locale=[self.LOCALE], stdout=StringIO())
self.assertTrue(os.path.exists(self.PROJECT_MO_FILE)) self.assertTrue(os.path.exists(self.PROJECT_MO_FILE))
self.assertTrue(os.path.exists(self.APP_MO_FILE)) self.assertTrue(os.path.exists(self.APP_MO_FILE))
class PathLibLocaleCompilationTests(MessageCompilationTests):
work_subdir = 'exclude'
def test_locale_paths_pathlib(self):
with override_settings(LOCALE_PATHS=[Path(self.test_dir) / 'canned_locale']):
call_command('compilemessages', locale=['fr'], stdout=StringIO())
self.assertTrue(os.path.exists('canned_locale/fr/LC_MESSAGES/django.mo'))

View File

@ -5,6 +5,7 @@ import tempfile
import time import time
import warnings import warnings
from io import StringIO from io import StringIO
from pathlib import Path
from unittest import mock, skipIf, skipUnless from unittest import mock, skipIf, skipUnless
from admin_scripts.tests import AdminScriptTestCase from admin_scripts.tests import AdminScriptTestCase
@ -735,11 +736,17 @@ class CustomLayoutExtractionTests(ExtractorTests):
management.call_command('makemessages', locale=LOCALE, verbosity=0) management.call_command('makemessages', locale=LOCALE, verbosity=0)
def test_project_locale_paths(self): def test_project_locale_paths(self):
self._test_project_locale_paths(os.path.join(self.test_dir, 'project_locale'))
def test_project_locale_paths_pathlib(self):
self._test_project_locale_paths(Path(self.test_dir) / 'project_locale')
def _test_project_locale_paths(self, locale_path):
""" """
* translations for an app containing a locale folder are stored in that folder * translations for an app containing a locale folder are stored in that folder
* translations outside of that app are in LOCALE_PATHS[0] * translations outside of that app are in LOCALE_PATHS[0]
""" """
with override_settings(LOCALE_PATHS=[os.path.join(self.test_dir, 'project_locale')]): with override_settings(LOCALE_PATHS=[locale_path]):
management.call_command('makemessages', locale=[LOCALE], verbosity=0) management.call_command('makemessages', locale=[LOCALE], verbosity=0)
project_de_locale = os.path.join( project_de_locale = os.path.join(
self.test_dir, 'project_locale', 'de', 'LC_MESSAGES', 'django.po') self.test_dir, 'project_locale', 'de', 'LC_MESSAGES', 'django.po')

View File

@ -1,6 +1,8 @@
import os import os
import sys import sys
import tempfile
import unittest import unittest
from pathlib import Path
from django.core.files import temp from django.core.files import temp
from django.core.files.base import ContentFile from django.core.files.base import ContentFile
@ -94,3 +96,10 @@ class FileFieldTests(TestCase):
# open() doesn't write to disk. # open() doesn't write to disk.
d.myfile.file = ContentFile(b'', name='bla') d.myfile.file = ContentFile(b'', name='bla')
self.assertEqual(d.myfile, d.myfile.open()) self.assertEqual(d.myfile, d.myfile.open())
def test_media_root_pathlib(self):
with tempfile.TemporaryDirectory() as tmp_dir:
with override_settings(MEDIA_ROOT=Path(tmp_dir)):
with TemporaryUploadedFile('foo.txt', 'text/plain', 1, 'utf-8') as tmp_file:
Document.objects.create(myfile=tmp_file)
self.assertTrue(os.path.exists(os.path.join(tmp_dir, 'unused', 'foo.txt')))

View File

@ -6,6 +6,7 @@ import tempfile
import unittest import unittest
from datetime import timedelta from datetime import timedelta
from http import cookies from http import cookies
from pathlib import Path
from django.conf import settings from django.conf import settings
from django.contrib.sessions.backends.base import UpdateError from django.contrib.sessions.backends.base import UpdateError
@ -521,7 +522,7 @@ class FileSessionTests(SessionTestsMixin, unittest.TestCase):
def setUp(self): def setUp(self):
# Do file session tests in an isolated directory, and kill it after we're done. # Do file session tests in an isolated directory, and kill it after we're done.
self.original_session_file_path = settings.SESSION_FILE_PATH self.original_session_file_path = settings.SESSION_FILE_PATH
self.temp_session_store = settings.SESSION_FILE_PATH = tempfile.mkdtemp() self.temp_session_store = settings.SESSION_FILE_PATH = self.mkdtemp()
# Reset the file session backend's internal caches # Reset the file session backend's internal caches
if hasattr(self.backend, '_storage_path'): if hasattr(self.backend, '_storage_path'):
del self.backend._storage_path del self.backend._storage_path
@ -532,6 +533,9 @@ class FileSessionTests(SessionTestsMixin, unittest.TestCase):
settings.SESSION_FILE_PATH = self.original_session_file_path settings.SESSION_FILE_PATH = self.original_session_file_path
shutil.rmtree(self.temp_session_store) shutil.rmtree(self.temp_session_store)
def mkdtemp(self):
return tempfile.mkdtemp()
@override_settings( @override_settings(
SESSION_FILE_PATH='/if/this/directory/exists/you/have/a/weird/computer', SESSION_FILE_PATH='/if/this/directory/exists/you/have/a/weird/computer',
) )
@ -598,6 +602,12 @@ class FileSessionTests(SessionTestsMixin, unittest.TestCase):
self.assertEqual(1, count_sessions()) self.assertEqual(1, count_sessions())
class FileSessionPathLibTests(FileSessionTests):
def mkdtemp(self):
tmp_dir = super().mkdtemp()
return Path(tmp_dir)
class CacheSessionTests(SessionTestsMixin, unittest.TestCase): class CacheSessionTests(SessionTestsMixin, unittest.TestCase):
backend = CacheSession backend = CacheSession

View File

@ -64,7 +64,7 @@ class CollectionTestCase(BaseStaticFilesMixin, SimpleTestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
temp_dir = tempfile.mkdtemp() temp_dir = self.mkdtemp()
# Override the STATIC_ROOT for all tests from setUp to tearDown # Override the STATIC_ROOT for all tests from setUp to tearDown
# rather than as a context manager # rather than as a context manager
self.patched_settings = self.settings(STATIC_ROOT=temp_dir) self.patched_settings = self.settings(STATIC_ROOT=temp_dir)
@ -78,6 +78,9 @@ class CollectionTestCase(BaseStaticFilesMixin, SimpleTestCase):
self.patched_settings.disable() self.patched_settings.disable()
super().tearDown() super().tearDown()
def mkdtemp(self):
return tempfile.mkdtemp()
def run_collectstatic(self, *, verbosity=0, **kwargs): def run_collectstatic(self, *, verbosity=0, **kwargs):
call_command('collectstatic', interactive=False, verbosity=verbosity, call_command('collectstatic', interactive=False, verbosity=verbosity,
ignore_patterns=['*.ignoreme'], **kwargs) ignore_patterns=['*.ignoreme'], **kwargs)

View File

@ -0,0 +1 @@
pathlib

View File

@ -1,4 +1,5 @@
import os.path import os.path
from pathlib import Path
TEST_ROOT = os.path.dirname(__file__) TEST_ROOT = os.path.dirname(__file__)
@ -10,6 +11,7 @@ TEST_SETTINGS = {
'STATICFILES_DIRS': [ 'STATICFILES_DIRS': [
os.path.join(TEST_ROOT, 'project', 'documents'), os.path.join(TEST_ROOT, 'project', 'documents'),
('prefix', os.path.join(TEST_ROOT, 'project', 'prefixed')), ('prefix', os.path.join(TEST_ROOT, 'project', 'prefixed')),
Path(TEST_ROOT) / 'project' / 'pathlib',
], ],
'STATICFILES_FINDERS': [ 'STATICFILES_FINDERS': [
'django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.FileSystemFinder',

View File

@ -4,6 +4,7 @@ import shutil
import tempfile import tempfile
import unittest import unittest
from io import StringIO from io import StringIO
from pathlib import Path
from unittest import mock from unittest import mock
from admin_scripts.tests import AdminScriptTestCase from admin_scripts.tests import AdminScriptTestCase
@ -102,6 +103,7 @@ class TestFindStatic(TestDefaults, CollectionTestCase):
# FileSystemFinder searched locations # FileSystemFinder searched locations
self.assertIn(TEST_SETTINGS['STATICFILES_DIRS'][1][1], searched_locations) self.assertIn(TEST_SETTINGS['STATICFILES_DIRS'][1][1], searched_locations)
self.assertIn(TEST_SETTINGS['STATICFILES_DIRS'][0], searched_locations) self.assertIn(TEST_SETTINGS['STATICFILES_DIRS'][0], searched_locations)
self.assertIn(str(TEST_SETTINGS['STATICFILES_DIRS'][2]), searched_locations)
# DefaultStorageFinder searched locations # DefaultStorageFinder searched locations
self.assertIn( self.assertIn(
os.path.join('staticfiles_tests', 'project', 'site_media', 'media'), os.path.join('staticfiles_tests', 'project', 'site_media', 'media'),
@ -174,6 +176,15 @@ class TestCollection(TestDefaults, CollectionTestCase):
self.assertFileNotFound('test/backup~') self.assertFileNotFound('test/backup~')
self.assertFileNotFound('test/CVS') self.assertFileNotFound('test/CVS')
def test_pathlib(self):
self.assertFileContains('pathlib.txt', 'pathlib')
class TestCollectionPathLib(TestCollection):
def mkdtemp(self):
tmp_dir = super().mkdtemp()
return Path(tmp_dir)
class TestCollectionVerbosity(CollectionTestCase): class TestCollectionVerbosity(CollectionTestCase):
copying_msg = 'Copying ' copying_msg = 'Copying '

View File

@ -1,3 +1,5 @@
from pathlib import Path
from template_tests.test_response import test_processor_name from template_tests.test_response import test_processor_name
from django.template import Context, EngineHandler, RequestContext from django.template import Context, EngineHandler, RequestContext
@ -164,3 +166,13 @@ class DjangoTemplatesTests(TemplateStringsTests):
def test_debug_default_template_loaders(self): def test_debug_default_template_loaders(self):
engine = DjangoTemplates({'DIRS': [], 'APP_DIRS': True, 'NAME': 'django', 'OPTIONS': {}}) engine = DjangoTemplates({'DIRS': [], 'APP_DIRS': True, 'NAME': 'django', 'OPTIONS': {}})
self.assertEqual(engine.engine.loaders, self.default_loaders) self.assertEqual(engine.engine.loaders, self.default_loaders)
def test_dirs_pathlib(self):
engine = DjangoTemplates({
'DIRS': [Path(__file__).parent / 'templates' / 'template_backends'],
'APP_DIRS': False,
'NAME': 'django',
'OPTIONS': {},
})
template = engine.get_template('hello.html')
self.assertEqual(template.render({'name': 'Joe'}), 'Hello Joe!\n')

View File

@ -1,3 +1,4 @@
from pathlib import Path
from unittest import skipIf from unittest import skipIf
from django.template import TemplateSyntaxError from django.template import TemplateSyntaxError
@ -85,3 +86,13 @@ class Jinja2Tests(TemplateStringsTests):
with self.settings(STATIC_URL='/s/'): with self.settings(STATIC_URL='/s/'):
content = template.render(request=request) content = template.render(request=request)
self.assertEqual(content, 'Static URL: /s/') self.assertEqual(content, 'Static URL: /s/')
def test_dirs_pathlib(self):
engine = Jinja2({
'DIRS': [Path(__file__).parent / 'templates' / 'template_backends'],
'APP_DIRS': False,
'NAME': 'jinja2',
'OPTIONS': {},
})
template = engine.get_template('hello.html')
self.assertEqual(template.render({'name': 'Joe'}), 'Hello Joe!')

View File

@ -1,4 +1,5 @@
import os import os
from pathlib import Path
from unittest import mock from unittest import mock
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
@ -52,3 +53,8 @@ class LocaleRegexDescriptorTests(SimpleTestCase):
def test_access_locale_regex_descriptor(self): def test_access_locale_regex_descriptor(self):
self.assertIsInstance(RegexPattern.regex, LocaleRegexDescriptor) self.assertIsInstance(RegexPattern.regex, LocaleRegexDescriptor)
@override_settings(LOCALE_PATHS=[Path(here) / 'translations' / 'locale'])
class LocaleRegexDescriptorPathLibTests(LocaleRegexDescriptorTests):
pass