From 11b8c30b9e02ef6ecb996ad3280979dfeab700fa Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 21 Jul 2017 21:33:26 +0100 Subject: [PATCH] Ref #23919 -- Replaced some os.path usage with pathlib.Path. --- django/conf/__init__.py | 7 +++--- django/contrib/admindocs/views.py | 10 ++++----- django/contrib/auth/password_validation.py | 10 ++++----- django/contrib/gis/geoip2/base.py | 22 +++++++++---------- django/core/mail/message.py | 9 ++++---- django/core/validators.py | 4 ++-- django/db/utils.py | 4 ++-- django/forms/renderers.py | 6 +++--- django/template/utils.py | 14 ++++++------ django/views/static.py | 25 +++++++++++----------- 10 files changed, 54 insertions(+), 57 deletions(-) diff --git a/django/conf/__init__.py b/django/conf/__init__.py index 6813afb3b8..9b3e350a50 100644 --- a/django/conf/__init__.py +++ b/django/conf/__init__.py @@ -10,6 +10,7 @@ import importlib import os import time import warnings +from pathlib import Path from django.conf import global_settings from django.core.exceptions import ImproperlyConfigured @@ -130,9 +131,9 @@ class Settings: if hasattr(time, 'tzset') and self.TIME_ZONE: # When we can, attempt to validate the timezone. If we can't find # this file, no check happens and it's harmless. - zoneinfo_root = '/usr/share/zoneinfo' - if (os.path.exists(zoneinfo_root) and not - os.path.exists(os.path.join(zoneinfo_root, *(self.TIME_ZONE.split('/'))))): + zoneinfo_root = Path('/usr/share/zoneinfo') + zone_info_file = zoneinfo_root.joinpath(*self.TIME_ZONE.split('/')) + if zoneinfo_root.exists() and not zone_info_file.exists(): raise ValueError("Incorrect timezone setting: %s" % self.TIME_ZONE) # Move the time zone info into os.environ. See ticket #2315 for why # we don't do this unconditionally (breaks Windows). diff --git a/django/contrib/admindocs/views.py b/django/contrib/admindocs/views.py index 2da015b73f..02449f7401 100644 --- a/django/contrib/admindocs/views.py +++ b/django/contrib/admindocs/views.py @@ -1,6 +1,6 @@ import inspect -import os from importlib import import_module +from pathlib import Path from django.apps import apps from django.conf import settings @@ -331,15 +331,15 @@ class TemplateDetailView(BaseAdminDocsView): else: # This doesn't account for template loaders (#24128). for index, directory in enumerate(default_engine.dirs): - template_file = os.path.join(directory, template) - if os.path.exists(template_file): - with open(template_file) as f: + template_file = Path(directory) / template + if template_file.exists(): + with template_file.open() as f: template_contents = f.read() else: template_contents = '' templates.append({ 'file': template_file, - 'exists': os.path.exists(template_file), + 'exists': template_file.exists(), 'contents': template_contents, 'order': index, }) diff --git a/django/contrib/auth/password_validation.py b/django/contrib/auth/password_validation.py index 7a64e62697..d270ab7173 100644 --- a/django/contrib/auth/password_validation.py +++ b/django/contrib/auth/password_validation.py @@ -1,8 +1,8 @@ import functools import gzip -import os import re from difflib import SequenceMatcher +from pathlib import Path from django.conf import settings from django.core.exceptions import ( @@ -165,16 +165,14 @@ class CommonPasswordValidator: The list Django ships with contains 20000 common passwords, created by Royce Williams: https://gist.github.com/roycewilliams/281ce539915a947a23db17137d91aeb7 """ - DEFAULT_PASSWORD_LIST_PATH = os.path.join( - os.path.dirname(os.path.realpath(__file__)), 'common-passwords.txt.gz' - ) + DEFAULT_PASSWORD_LIST_PATH = Path(__file__).resolve().parent / 'common-passwords.txt.gz' def __init__(self, password_list_path=DEFAULT_PASSWORD_LIST_PATH): try: - with gzip.open(password_list_path) as f: + with gzip.open(str(password_list_path)) as f: common_passwords_lines = f.read().decode().splitlines() except IOError: - with open(password_list_path) as f: + with open(str(password_list_path)) as f: common_passwords_lines = f.readlines() self.passwords = {p.strip() for p in common_passwords_lines} diff --git a/django/contrib/gis/geoip2/base.py b/django/contrib/gis/geoip2/base.py index 1d5a46653c..b098632d39 100644 --- a/django/contrib/gis/geoip2/base.py +++ b/django/contrib/gis/geoip2/base.py @@ -1,5 +1,5 @@ -import os import socket +from pathlib import Path import geoip2.database @@ -79,27 +79,27 @@ class GeoIP2: if not isinstance(path, str): raise TypeError('Invalid path type: %s' % type(path).__name__) - if os.path.isdir(path): + path = Path(path) + if path.is_dir(): # Constructing the GeoIP database filenames using the settings # dictionary. If the database files for the GeoLite country # and/or city datasets exist, then try to open them. - country_db = os.path.join(path, country or GEOIP_SETTINGS['GEOIP_COUNTRY']) - if os.path.isfile(country_db): - self._country = geoip2.database.Reader(country_db, mode=cache) + country_db = path / (country or GEOIP_SETTINGS['GEOIP_COUNTRY']) + if country_db.is_file(): + self._country = geoip2.database.Reader(str(country_db), mode=cache) self._country_file = country_db - city_db = os.path.join(path, city or GEOIP_SETTINGS['GEOIP_CITY']) - if os.path.isfile(city_db): - self._city = geoip2.database.Reader(city_db, mode=cache) + city_db = path / (city or GEOIP_SETTINGS['GEOIP_CITY']) + if city_db.is_file(): + self._city = geoip2.database.Reader(str(city_db), mode=cache) self._city_file = city_db - if not self._reader: raise GeoIP2Exception('Could not load a database from %s.' % path) - elif os.path.isfile(path): + elif path.is_file(): # Otherwise, some detective work will be needed to figure out # whether the given database path is for the GeoIP country or city # databases. - reader = geoip2.database.Reader(path, mode=cache) + reader = geoip2.database.Reader(str(path), mode=cache) db_type = reader.metadata().database_type if db_type.endswith('City'): diff --git a/django/core/mail/message.py b/django/core/mail/message.py index c36fdcff09..9a0f0eba45 100644 --- a/django/core/mail/message.py +++ b/django/core/mail/message.py @@ -1,5 +1,4 @@ import mimetypes -import os from email import ( charset as Charset, encoders as Encoders, generator, message_from_string, ) @@ -13,6 +12,7 @@ from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.utils import formatdate, getaddresses, make_msgid, parseaddr from io import BytesIO, StringIO +from pathlib import Path from django.conf import settings from django.core.mail.utils import DNS_NAME @@ -333,11 +333,10 @@ class EmailMessage: as UTF-8. If that fails, set the mimetype to DEFAULT_ATTACHMENT_MIME_TYPE and don't decode the content. """ - filename = os.path.basename(path) - - with open(path, 'rb') as file: + path = Path(path) + with path.open('rb') as file: content = file.read() - self.attach(filename, content, mimetype) + self.attach(path.name, content, mimetype) def _create_message(self, msg): return self._create_attachments(msg) diff --git a/django/core/validators.py b/django/core/validators.py index 965f35cbab..92394a7eae 100644 --- a/django/core/validators.py +++ b/django/core/validators.py @@ -1,6 +1,6 @@ import ipaddress -import os import re +from pathlib import Path from urllib.parse import urlsplit, urlunsplit from django.core.exceptions import ValidationError @@ -480,7 +480,7 @@ class FileExtensionValidator: self.code = code def __call__(self, value): - extension = os.path.splitext(value.name)[1][1:].lower() + extension = Path(value.name).suffix[1:].lower() if self.allowed_extensions is not None and extension not in self.allowed_extensions: raise ValidationError( self.message, diff --git a/django/db/utils.py b/django/db/utils.py index d5174682e1..6202a9a4aa 100644 --- a/django/db/utils.py +++ b/django/db/utils.py @@ -1,6 +1,6 @@ -import os import pkgutil from importlib import import_module +from pathlib import Path from threading import local from django.conf import settings @@ -111,7 +111,7 @@ def load_backend(backend_name): except ImportError as e_user: # The database backend wasn't found. Display a helpful error message # listing all built-in database backends. - backend_dir = os.path.join(os.path.dirname(__file__), 'backends') + backend_dir = str(Path(__file__).parent / 'backends') builtin_backends = [ name for _, name, ispkg in pkgutil.iter_modules([backend_dir]) if ispkg and name not in {'base', 'dummy', 'postgresql_psycopg2'} diff --git a/django/forms/renderers.py b/django/forms/renderers.py index 435a12d852..9fb695bdb2 100644 --- a/django/forms/renderers.py +++ b/django/forms/renderers.py @@ -1,5 +1,5 @@ import functools -import os +from pathlib import Path from django.conf import settings from django.template.backends.django import DjangoTemplates @@ -13,7 +13,7 @@ except ImportError: def Jinja2(params): raise ImportError("jinja2 isn't installed") -ROOT = os.path.dirname(__file__) +ROOT = Path(__file__).parent @functools.lru_cache() @@ -39,7 +39,7 @@ class EngineMixin: def engine(self): return self.backend({ 'APP_DIRS': True, - 'DIRS': [os.path.join(ROOT, self.backend.app_dirname)], + 'DIRS': [str(ROOT / self.backend.app_dirname)], 'NAME': 'djangoforms', 'OPTIONS': {}, }) diff --git a/django/template/utils.py b/django/template/utils.py index 03f0554c42..5a6c414939 100644 --- a/django/template/utils.py +++ b/django/template/utils.py @@ -1,6 +1,6 @@ import functools -import os from collections import Counter, OrderedDict +from pathlib import Path from django.apps import apps from django.conf import settings @@ -98,12 +98,10 @@ def get_app_template_dirs(dirname): dirname is the name of the subdirectory containing templates inside installed applications. """ - template_dirs = [] - for app_config in apps.get_app_configs(): - if not app_config.path: - continue - template_dir = os.path.join(app_config.path, dirname) - if os.path.isdir(template_dir): - template_dirs.append(template_dir) + template_dirs = [ + str(Path(app_config.path) / dirname) + for app_config in apps.get_app_configs() + if app_config.path and (Path(app_config.path) / dirname).is_dir() + ] # Immutable return value because it will be cached and shared by callers. return tuple(template_dirs) diff --git a/django/views/static.py b/django/views/static.py index c93c136993..b691e57e6c 100644 --- a/django/views/static.py +++ b/django/views/static.py @@ -3,10 +3,10 @@ Views and functions for serving static files. These are only to be used during development, and SHOULD NOT be used in a production setting. """ import mimetypes -import os import posixpath import re import stat +from pathlib import Path from django.http import ( FileResponse, Http404, HttpResponse, HttpResponseNotModified, @@ -34,21 +34,21 @@ def serve(request, path, document_root=None, show_indexes=False): ``static/directory_index.html``. """ path = posixpath.normpath(path).lstrip('/') - fullpath = safe_join(document_root, path) - if os.path.isdir(fullpath): + fullpath = Path(safe_join(document_root, path)) + if fullpath.is_dir(): if show_indexes: return directory_index(path, fullpath) raise Http404(_("Directory indexes are not allowed here.")) - if not os.path.exists(fullpath): + if not fullpath.exists(): raise Http404(_('"%(path)s" does not exist') % {'path': fullpath}) # Respect the If-Modified-Since header. - statobj = os.stat(fullpath) + statobj = fullpath.stat() if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'), statobj.st_mtime, statobj.st_size): return HttpResponseNotModified() - content_type, encoding = mimetypes.guess_type(fullpath) + content_type, encoding = mimetypes.guess_type(str(fullpath)) content_type = content_type or 'application/octet-stream' - response = FileResponse(open(fullpath, 'rb'), content_type=content_type) + response = FileResponse(fullpath.open('rb'), content_type=content_type) response["Last-Modified"] = http_date(statobj.st_mtime) if stat.S_ISREG(statobj.st_mode): response["Content-Length"] = statobj.st_size @@ -95,11 +95,12 @@ def directory_index(path, fullpath): else: c = {} files = [] - for f in os.listdir(fullpath): - if not f.startswith('.'): - if os.path.isdir(os.path.join(fullpath, f)): - f += '/' - files.append(f) + for f in fullpath.iterdir(): + if not f.name.startswith('.'): + url = str(f.relative_to(fullpath)) + if f.is_dir(): + url += '/' + files.append(url) c.update({ 'directory': path + '/', 'file_list': files,