From 550cb3a365dee4edfdd1563224d5304de2a57fda Mon Sep 17 00:00:00 2001 From: Mads Jensen Date: Thu, 9 Mar 2017 16:17:41 +0100 Subject: [PATCH] Fixed #27818 -- Replaced try/except/pass with contextlib.suppress(). --- django/contrib/admin/models.py | 5 ++- django/contrib/admin/sites.py | 9 ++--- django/contrib/auth/middleware.py | 6 ++-- django/contrib/contenttypes/fields.py | 5 ++- django/contrib/contenttypes/models.py | 5 ++- django/contrib/contenttypes/views.py | 10 +++--- .../gis/db/backends/spatialite/schema.py | 10 +++--- django/contrib/gis/db/models/fields.py | 5 ++- django/contrib/gis/gdal/layer.py | 5 ++- django/contrib/gis/geos/factory.py | 5 +-- django/contrib/gis/utils/__init__.py | 8 ++--- django/contrib/messages/storage/cookie.py | 5 ++- django/contrib/sessions/backends/base.py | 5 ++- django/contrib/sessions/backends/file.py | 10 ++---- django/contrib/sitemaps/__init__.py | 9 ++--- django/contrib/sites/models.py | 10 +++--- .../management/commands/collectstatic.py | 5 ++- django/contrib/syndication/views.py | 9 ++--- django/core/cache/backends/filebased.py | 15 +++----- django/core/cache/backends/locmem.py | 20 ++++------- django/core/files/move.py | 10 +++--- django/core/files/storage.py | 19 +++++------ django/core/management/__init__.py | 13 +++---- django/core/management/base.py | 9 +++-- django/core/management/commands/flush.py | 5 ++- django/core/management/commands/shell.py | 9 ++--- django/core/validators.py | 10 +++--- django/db/backends/postgresql/client.py | 9 +++-- django/db/backends/sqlite3/operations.py | 9 +++-- django/db/migrations/autodetector.py | 5 ++- django/db/migrations/questioner.py | 5 +-- django/db/migrations/state.py | 6 ++-- django/db/migrations/writer.py | 5 ++- django/db/models/expressions.py | 5 ++- django/db/models/options.py | 17 +++------- django/db/models/query.py | 11 +++--- django/forms/fields.py | 5 ++- django/forms/forms.py | 5 ++- django/forms/formsets.py | 6 ++-- django/forms/models.py | 5 ++- django/forms/widgets.py | 10 +++--- django/http/response.py | 13 +++---- django/template/backends/base.py | 11 +++--- django/template/defaultfilters.py | 9 ++--- django/test/signals.py | 13 +++---- django/urls/base.py | 9 ++--- django/utils/autoreload.py | 21 ++++-------- django/utils/datastructures.py | 5 ++- django/utils/dateformat.py | 11 +++--- django/utils/formats.py | 18 ++++------ django/utils/http.py | 5 ++- django/utils/translation/__init__.py | 8 ++--- django/utils/translation/trans_real.py | 13 +++---- django/views/debug.py | 9 ++--- tests/admin_scripts/tests.py | 9 ++--- tests/backends/tests.py | 5 ++- tests/bash_completion/tests.py | 5 ++- tests/handlers/views.py | 6 ++-- tests/mail/tests.py | 9 +++-- tests/postgres_tests/test_aggregates.py | 5 ++- tests/postgres_tests/test_array.py | 5 ++- tests/postgres_tests/test_hstore.py | 5 ++- tests/postgres_tests/test_json.py | 5 ++- tests/postgres_tests/test_ranges.py | 5 ++- tests/runtests.py | 9 ++--- tests/staticfiles_tests/storage.py | 5 ++- tests/transaction_hooks/tests.py | 34 ++++++------------- 67 files changed, 223 insertions(+), 368 deletions(-) diff --git a/django/contrib/admin/models.py b/django/contrib/admin/models.py index 82b3cc0585d..4443f13dacb 100644 --- a/django/contrib/admin/models.py +++ b/django/contrib/admin/models.py @@ -1,4 +1,5 @@ import json +from contextlib import suppress from django.conf import settings from django.contrib.admin.utils import quote @@ -137,8 +138,6 @@ class LogEntry(models.Model): """ if self.content_type and self.object_id: url_name = 'admin:%s_%s_change' % (self.content_type.app_label, self.content_type.model) - try: + with suppress(NoReverseMatch): return reverse(url_name, args=(quote(self.object_id),)) - except NoReverseMatch: - pass return None diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py index 868c3f27f91..fcce8dba111 100644 --- a/django/contrib/admin/sites.py +++ b/django/contrib/admin/sites.py @@ -1,3 +1,4 @@ +from contextlib import suppress from functools import update_wrapper from weakref import WeakSet @@ -427,15 +428,11 @@ class AdminSite: 'perms': perms, } if perms.get('change'): - try: + with suppress(NoReverseMatch): model_dict['admin_url'] = reverse('admin:%s_%s_changelist' % info, current_app=self.name) - except NoReverseMatch: - pass if perms.get('add'): - try: + with suppress(NoReverseMatch): model_dict['add_url'] = reverse('admin:%s_%s_add' % info, current_app=self.name) - except NoReverseMatch: - pass if app_label in app_dict: app_dict[app_label]['models'].append(model_dict) diff --git a/django/contrib/auth/middleware.py b/django/contrib/auth/middleware.py index f0102c4455f..0b3365cb3a5 100644 --- a/django/contrib/auth/middleware.py +++ b/django/contrib/auth/middleware.py @@ -1,3 +1,5 @@ +from contextlib import suppress + from django.conf import settings from django.contrib import auth from django.contrib.auth import load_backend @@ -89,10 +91,8 @@ class RemoteUserMiddleware(MiddlewareMixin): """ backend_str = request.session[auth.BACKEND_SESSION_KEY] backend = auth.load_backend(backend_str) - try: + with suppress(AttributeError): # Backend has no clean_username method. username = backend.clean_username(username) - except AttributeError: # Backend has no clean_username method. - pass return username def _remove_invalid_user(self, request): diff --git a/django/contrib/contenttypes/fields.py b/django/contrib/contenttypes/fields.py index 9394182aeee..57022ad1efe 100644 --- a/django/contrib/contenttypes/fields.py +++ b/django/contrib/contenttypes/fields.py @@ -1,4 +1,5 @@ from collections import defaultdict +from contextlib import suppress from django.contrib.contenttypes.models import ContentType from django.core import checks @@ -237,10 +238,8 @@ class GenericForeignKey: if ct_id is not None: ct = self.get_content_type(id=ct_id, using=instance._state.db) - try: + with suppress(ObjectDoesNotExist): rel_obj = ct.get_object_for_this_type(pk=pk_val) - except ObjectDoesNotExist: - pass setattr(instance, self.cache_attr, rel_obj) return rel_obj diff --git a/django/contrib/contenttypes/models.py b/django/contrib/contenttypes/models.py index ad2e0bc904b..066474e49fb 100644 --- a/django/contrib/contenttypes/models.py +++ b/django/contrib/contenttypes/models.py @@ -1,4 +1,5 @@ from collections import defaultdict +from contextlib import suppress from django.apps import apps from django.db import models @@ -38,10 +39,8 @@ class ContentTypeManager(models.Manager): for the same model don't hit the database. """ opts = self._get_opts(model, for_concrete_model) - try: + with suppress(KeyError): return self._get_from_cache(opts) - except KeyError: - pass # The ContentType entry was not found in the cache, therefore we # proceed to load or create it. diff --git a/django/contrib/contenttypes/views.py b/django/contrib/contenttypes/views.py index d67f0715694..c5276872505 100644 --- a/django/contrib/contenttypes/views.py +++ b/django/contrib/contenttypes/views.py @@ -1,3 +1,5 @@ +from contextlib import suppress + from django.apps import apps from django.contrib.contenttypes.models import ContentType from django.contrib.sites.requests import RequestSite @@ -53,12 +55,10 @@ def shortcut(request, content_type_id, object_id): # First, look for an many-to-many relationship to Site. for field in opts.many_to_many: if field.remote_field.model is Site: - try: + with suppress(IndexError): # Caveat: In the case of multiple related Sites, this just # selects the *first* one, which is arbitrary. object_domain = getattr(obj, field.name).all()[0].domain - except IndexError: - pass if object_domain is not None: break @@ -77,10 +77,8 @@ def shortcut(request, content_type_id, object_id): # Fall back to the current site (if possible). if object_domain is None: - try: + with suppress(Site.DoesNotExist): object_domain = Site.objects.get_current(request).domain - except Site.DoesNotExist: - pass else: # Fall back to the current request's site. diff --git a/django/contrib/gis/db/backends/spatialite/schema.py b/django/contrib/gis/db/backends/spatialite/schema.py index 6f4e3380db0..3122b79e7e6 100644 --- a/django/contrib/gis/db/backends/spatialite/schema.py +++ b/django/contrib/gis/db/backends/spatialite/schema.py @@ -1,3 +1,5 @@ +from contextlib import suppress + from django.db.backends.sqlite3.schema import DatabaseSchemaEditor from django.db.utils import DatabaseError @@ -89,15 +91,13 @@ class SpatialiteSchemaEditor(DatabaseSchemaEditor): self.remove_geometry_metadata(model, field) # Make sure all geom stuff is gone for geom_table in self.geometry_tables: - try: + with suppress(DatabaseError): self.execute( self.sql_discard_geometry_columns % { "geom_table": geom_table, "table": self.quote_name(model._meta.db_table), } ) - except DatabaseError: - pass super().delete_model(model, **kwargs) def add_field(self, model, field): @@ -138,7 +138,7 @@ class SpatialiteSchemaEditor(DatabaseSchemaEditor): super().alter_db_table(model, old_db_table, new_db_table) # Repoint any straggler names for geom_table in self.geometry_tables: - try: + with suppress(DatabaseError): self.execute( self.sql_update_geometry_columns % { "geom_table": geom_table, @@ -146,8 +146,6 @@ class SpatialiteSchemaEditor(DatabaseSchemaEditor): "new_table": self.quote_name(new_db_table), } ) - except DatabaseError: - pass # Re-add geometry-ness and rename spatial index tables for field in model._meta.local_fields: if isinstance(field, GeometryField): diff --git a/django/contrib/gis/db/models/fields.py b/django/contrib/gis/db/models/fields.py index 925087a4c77..b98c576c836 100644 --- a/django/contrib/gis/db/models/fields.py +++ b/django/contrib/gis/db/models/fields.py @@ -1,4 +1,5 @@ from collections import defaultdict, namedtuple +from contextlib import suppress from django.contrib.gis import forms, gdal from django.contrib.gis.db.models.proxy import SpatialProxy @@ -171,10 +172,8 @@ class BaseSpatialField(Field): if isinstance(value, gdal.GDALRaster): return value elif is_candidate: - try: + with suppress(GDALException): return gdal.GDALRaster(value) - except GDALException: - pass elif isinstance(value, dict): try: return gdal.GDALRaster(value) diff --git a/django/contrib/gis/gdal/layer.py b/django/contrib/gis/gdal/layer.py index fcb4d07a3d8..384bc3a074e 100644 --- a/django/contrib/gis/gdal/layer.py +++ b/django/contrib/gis/gdal/layer.py @@ -1,3 +1,4 @@ +from contextlib import suppress from ctypes import byref, c_double from django.contrib.gis.gdal.base import GDALBase @@ -78,10 +79,8 @@ class Layer(GDALBase): """ if self._random_read: # If the Layer supports random reading, return. - try: + with suppress(GDALException): return Feature(capi.get_feature(self.ptr, feat_id), self) - except GDALException: - pass else: # Random access isn't supported, have to increment through # each feature until the given feature ID is encountered. diff --git a/django/contrib/gis/geos/factory.py b/django/contrib/gis/geos/factory.py index 3f46f259eea..46ed1f0c519 100644 --- a/django/contrib/gis/geos/factory.py +++ b/django/contrib/gis/geos/factory.py @@ -17,10 +17,11 @@ def fromfile(file_h): if isinstance(buf, bytes): try: decoded = buf.decode() - if wkt_regex.match(decoded) or hex_regex.match(decoded): - return GEOSGeometry(decoded) except UnicodeDecodeError: pass + else: + if wkt_regex.match(decoded) or hex_regex.match(decoded): + return GEOSGeometry(decoded) else: return GEOSGeometry(buf) diff --git a/django/contrib/gis/utils/__init__.py b/django/contrib/gis/utils/__init__.py index e66d3b2bd23..88de2a4773c 100644 --- a/django/contrib/gis/utils/__init__.py +++ b/django/contrib/gis/utils/__init__.py @@ -1,14 +1,14 @@ """ This module contains useful utilities for GeoDjango. """ +from contextlib import suppress + from django.contrib.gis.utils.ogrinfo import ogrinfo # NOQA from django.contrib.gis.utils.ogrinspect import mapping, ogrinspect # NOQA from django.contrib.gis.utils.srs import add_srs_entry # NOQA from django.core.exceptions import ImproperlyConfigured -try: +with suppress(ImproperlyConfigured): # LayerMapping requires DJANGO_SETTINGS_MODULE to be set, - # so this needs to be in try/except. + # and ImproperlyConfigured is raised if that's not the case. from django.contrib.gis.utils.layermapping import LayerMapping, LayerMapError # NOQA -except ImproperlyConfigured: - pass diff --git a/django/contrib/messages/storage/cookie.py b/django/contrib/messages/storage/cookie.py index 49270cac171..6d5df1af9db 100644 --- a/django/contrib/messages/storage/cookie.py +++ b/django/contrib/messages/storage/cookie.py @@ -1,4 +1,5 @@ import json +from contextlib import suppress from django.conf import settings from django.contrib.messages.storage.base import BaseStorage, Message @@ -153,12 +154,10 @@ class CookieStorage(BaseStorage): if len(bits) == 2: hash, value = bits if constant_time_compare(hash, self._hash(value)): - try: + with suppress(ValueError): # If we get here (and the JSON decode works), everything is # good. In any other case, drop back and return None. return json.loads(value, cls=MessageDecoder) - except ValueError: - pass # Mark the data as used (so it gets removed) since something was wrong # with the data. self.used = True diff --git a/django/contrib/sessions/backends/base.py b/django/contrib/sessions/backends/base.py index 64955b8bb71..3ff79b7a393 100644 --- a/django/contrib/sessions/backends/base.py +++ b/django/contrib/sessions/backends/base.py @@ -1,6 +1,7 @@ import base64 import logging import string +from contextlib import suppress from datetime import datetime, timedelta from django.conf import settings @@ -262,10 +263,8 @@ class SessionBase: """ if value is None: # Remove any custom expiration for this session. - try: + with suppress(KeyError): del self['_session_expiry'] - except KeyError: - pass return if isinstance(value, timedelta): value = timezone.now() + value diff --git a/django/contrib/sessions/backends/file.py b/django/contrib/sessions/backends/file.py index 818c90e3ba9..54ab4f2e4dd 100644 --- a/django/contrib/sessions/backends/file.py +++ b/django/contrib/sessions/backends/file.py @@ -3,6 +3,7 @@ import logging import os import shutil import tempfile +from contextlib import suppress from django.conf import settings from django.contrib.sessions.backends.base import ( @@ -155,7 +156,7 @@ class SessionStore(SessionBase): # See ticket #8616. dir, prefix = os.path.split(session_file_name) - try: + with suppress(OSError, IOError, EOFError): output_file_fd, output_file_name = tempfile.mkstemp(dir=dir, prefix=prefix + '_out_') renamed = False try: @@ -173,9 +174,6 @@ class SessionStore(SessionBase): if not renamed: os.unlink(output_file_name) - except (OSError, IOError, EOFError): - pass - def exists(self, session_key): return os.path.exists(self._key_to_file(session_key)) @@ -184,10 +182,8 @@ class SessionStore(SessionBase): if self.session_key is None: return session_key = self.session_key - try: + with suppress(OSError): os.unlink(self._key_to_file(session_key)) - except OSError: - pass def clean(self): pass diff --git a/django/contrib/sitemaps/__init__.py b/django/contrib/sitemaps/__init__.py index bd1139ff701..8f984ae3dc1 100644 --- a/django/contrib/sitemaps/__init__.py +++ b/django/contrib/sitemaps/__init__.py @@ -1,3 +1,4 @@ +from contextlib import suppress from urllib.parse import urlencode from urllib.request import urlopen @@ -36,11 +37,9 @@ def _get_sitemap_full_url(sitemap_url): # First, try to get the "index" sitemap URL. sitemap_url = reverse('django.contrib.sitemaps.views.index') except NoReverseMatch: - try: + with suppress(NoReverseMatch): # Next, try for the "global" sitemap URL. sitemap_url = reverse('django.contrib.sitemaps.views.sitemap') - except NoReverseMatch: - pass if sitemap_url is None: raise SitemapNotFound("You didn't provide a sitemap_url, and the sitemap URL couldn't be auto-detected.") @@ -89,10 +88,8 @@ class Sitemap: if site is None: if django_apps.is_installed('django.contrib.sites'): Site = django_apps.get_model('sites.Site') - try: + with suppress(Site.DoesNotExist): site = Site.objects.get_current() - except Site.DoesNotExist: - pass if site is None: raise ImproperlyConfigured( "To use sitemaps, either enable the sites framework or pass " diff --git a/django/contrib/sites/models.py b/django/contrib/sites/models.py index 19f52e44871..9112efbfb5d 100644 --- a/django/contrib/sites/models.py +++ b/django/contrib/sites/models.py @@ -1,4 +1,5 @@ import string +from contextlib import suppress from django.core.exceptions import ImproperlyConfigured, ValidationError from django.db import models @@ -107,14 +108,11 @@ def clear_site_cache(sender, **kwargs): """ instance = kwargs['instance'] using = kwargs['using'] - try: + with suppress(KeyError): del SITE_CACHE[instance.pk] - except KeyError: - pass - try: + + with suppress(KeyError, Site.DoesNotExist): del SITE_CACHE[Site.objects.using(using).get(pk=instance.pk).domain] - except (KeyError, Site.DoesNotExist): - pass pre_save.connect(clear_site_cache, sender=Site) diff --git a/django/contrib/staticfiles/management/commands/collectstatic.py b/django/contrib/staticfiles/management/commands/collectstatic.py index e5ae48f9fe1..7e81ef19ee9 100644 --- a/django/contrib/staticfiles/management/commands/collectstatic.py +++ b/django/contrib/staticfiles/management/commands/collectstatic.py @@ -1,5 +1,6 @@ import os from collections import OrderedDict +from contextlib import suppress from django.apps import apps from django.contrib.staticfiles.finders import get_finders @@ -312,10 +313,8 @@ class Command(BaseCommand): else: self.log("Linking '%s'" % source_path, level=1) full_path = self.storage.path(prefixed_path) - try: + with suppress(OSError): os.makedirs(os.path.dirname(full_path)) - except OSError: - pass try: if os.path.lexists(full_path): os.unlink(full_path) diff --git a/django/contrib/syndication/views.py b/django/contrib/syndication/views.py index a8b98c84ae1..f0dcdb3e62f 100644 --- a/django/contrib/syndication/views.py +++ b/django/contrib/syndication/views.py @@ -1,4 +1,5 @@ from calendar import timegm +from contextlib import suppress from django.conf import settings from django.contrib.sites.shortcuts import get_current_site @@ -152,17 +153,13 @@ class Feed: title_tmp = None if self.title_template is not None: - try: + with suppress(TemplateDoesNotExist): title_tmp = loader.get_template(self.title_template) - except TemplateDoesNotExist: - pass description_tmp = None if self.description_template is not None: - try: + with suppress(TemplateDoesNotExist): description_tmp = loader.get_template(self.description_template) - except TemplateDoesNotExist: - pass for item in self._get_dynamic_attr('items', obj): context = self.get_context_data(item=item, site=current_site, diff --git a/django/core/cache/backends/filebased.py b/django/core/cache/backends/filebased.py index 56d2cf5b6d0..09a68b34bb6 100644 --- a/django/core/cache/backends/filebased.py +++ b/django/core/cache/backends/filebased.py @@ -7,6 +7,7 @@ import random import tempfile import time import zlib +from contextlib import suppress from django.core.cache.backends.base import DEFAULT_TIMEOUT, BaseCache from django.core.files.move import file_move_safe @@ -29,12 +30,10 @@ class FileBasedCache(BaseCache): def get(self, key, default=None, version=None): fname = self._key_to_file(key, version) - try: + with suppress(FileNotFoundError): with open(fname, 'rb') as f: if not self._is_expired(f): return pickle.loads(zlib.decompress(f.read())) - except FileNotFoundError: - pass return default def set(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): @@ -60,11 +59,9 @@ class FileBasedCache(BaseCache): def _delete(self, fname): if not fname.startswith(self._dir) or not os.path.exists(fname): return - try: - os.remove(fname) - except FileNotFoundError: + with suppress(FileNotFoundError): # The file may have been removed by another process. - pass + os.remove(fname) def has_key(self, key, version=None): fname = self._key_to_file(key, version) @@ -93,10 +90,8 @@ class FileBasedCache(BaseCache): def _createdir(self): if not os.path.exists(self._dir): - try: + with suppress(FileExistsError): os.makedirs(self._dir, 0o700) - except FileExistsError: - pass def _key_to_file(self, key, version=None): """ diff --git a/django/core/cache/backends/locmem.py b/django/core/cache/backends/locmem.py index cf86dcaa0c4..20b6dd4212c 100644 --- a/django/core/cache/backends/locmem.py +++ b/django/core/cache/backends/locmem.py @@ -1,8 +1,7 @@ "Thread-safe in-memory cache backend." - import pickle import time -from contextlib import contextmanager +from contextlib import contextmanager, suppress from django.core.cache.backends.base import DEFAULT_TIMEOUT, BaseCache from django.utils.synch import RWLock @@ -51,11 +50,9 @@ class LocMemCache(BaseCache): return default with (self._lock.writer() if acquire_lock else dummy()): - try: + with suppress(KeyError): del self._cache[key] del self._expire_info[key] - except KeyError: - pass return default def _set(self, key, value, timeout=DEFAULT_TIMEOUT): @@ -90,11 +87,9 @@ class LocMemCache(BaseCache): return True with self._lock.writer(): - try: + with suppress(KeyError): del self._cache[key] del self._expire_info[key] - except KeyError: - pass return False def _has_expired(self, key): @@ -112,14 +107,11 @@ class LocMemCache(BaseCache): self._delete(k) def _delete(self, key): - try: + with suppress(KeyError): del self._cache[key] - except KeyError: - pass - try: + + with suppress(KeyError): del self._expire_info[key] - except KeyError: - pass def delete(self, key, version=None): key = self.make_key(key, version=version) diff --git a/django/core/files/move.py b/django/core/files/move.py index bf0f1fe4e89..310e064a2d1 100644 --- a/django/core/files/move.py +++ b/django/core/files/move.py @@ -7,6 +7,7 @@ Move a file in the safest way possible:: import errno import os +from contextlib import suppress from shutil import copystat from django.core.files import locks @@ -41,17 +42,14 @@ def file_move_safe(old_file_name, new_file_name, chunk_size=1024 * 64, allow_ove if _samefile(old_file_name, new_file_name): return - try: - # If the destination file exists and allow_overwrite is False then raise an IOError + # OSError happens with os.rename() if moving to another filesystem or when + # moving opened files on certain operating systems. + with suppress(OSError): if not allow_overwrite and os.access(new_file_name, os.F_OK): raise IOError("Destination file %s exists and allow_overwrite is False" % new_file_name) os.rename(old_file_name, new_file_name) return - except OSError: - # This will happen with os.rename if moving to another filesystem - # or when moving opened files on certain operating systems - pass # first open the old file, so that it won't go away with open(old_file_name, 'rb') as old_file: diff --git a/django/core/files/storage.py b/django/core/files/storage.py index ff51d89cfa9..fa98bb198a8 100644 --- a/django/core/files/storage.py +++ b/django/core/files/storage.py @@ -1,4 +1,5 @@ import os +from contextlib import suppress from datetime import datetime from urllib.parse import urljoin @@ -223,7 +224,10 @@ class FileSystemStorage(Storage): # Create any intermediate directories that do not exist. directory = os.path.dirname(full_path) if not os.path.exists(directory): - try: + # There's a race between os.path.exists() and os.makedirs(). + # If os.makedirs() fails with FileNotFoundError, the directory + # was created concurrently. + with suppress(FileNotFoundError): if self.directory_permissions_mode is not None: # os.makedirs applies the global umask, so we reset it, # for consistency with file_permissions_mode behavior. @@ -234,11 +238,6 @@ class FileSystemStorage(Storage): os.umask(old_umask) else: os.makedirs(directory) - except FileNotFoundError: - # There's a race between os.path.exists() and os.makedirs(). - # If os.makedirs() fails with FileNotFoundError, the directory - # was created concurrently. - pass if not os.path.isdir(directory): raise IOError("%s exists and is not a directory." % directory) @@ -294,15 +293,13 @@ class FileSystemStorage(Storage): assert name, "The name argument is not allowed to be empty." name = self.path(name) # If the file or directory exists, delete it from the filesystem. - try: + # FileNotFoundError is raised if the file or directory was removed + # concurrently. + with suppress(FileNotFoundError): if os.path.isdir(name): os.rmdir(name) else: os.remove(name) - except FileNotFoundError: - # If removal fails, the file or directory may have been removed - # concurrently. - pass def exists(self, name): return os.path.exists(self.path(name)) diff --git a/django/core/management/__init__.py b/django/core/management/__init__.py index 872189d9f19..25991144899 100644 --- a/django/core/management/__init__.py +++ b/django/core/management/__init__.py @@ -3,6 +3,7 @@ import os import pkgutil import sys from collections import OrderedDict, defaultdict +from contextlib import suppress from importlib import import_module import django @@ -258,14 +259,12 @@ class ManagementUtility: subcommand_cls = self.fetch_command(cwords[0]) # special case: add the names of installed apps to options if cwords[0] in ('dumpdata', 'sqlmigrate', 'sqlsequencereset', 'test'): - try: + # Fail silently if DJANGO_SETTINGS_MODULE isn't set. The + # user will find out once they execute the command. + with suppress(ImportError): app_configs = apps.get_app_configs() # Get the last part of the dotted path as the app name. options.extend((app_config.label, 0) for app_config in app_configs) - except ImportError: - # Fail silently if DJANGO_SETTINGS_MODULE isn't set. The - # user will find out once they execute the command. - pass parser = subcommand_cls.create_parser('', cwords[0]) options.extend( (min(s_opt.option_strings), s_opt.nargs != 0) @@ -304,11 +303,9 @@ class ManagementUtility: parser.add_argument('--settings') parser.add_argument('--pythonpath') parser.add_argument('args', nargs='*') # catch-all - try: + with suppress(CommandError): # Ignore any option errors at this point. options, args = parser.parse_known_args(self.argv[2:]) handle_default_options(options) - except CommandError: - pass # Ignore any option errors at this point. try: settings.INSTALLED_APPS diff --git a/django/core/management/base.py b/django/core/management/base.py index 41b6b0fa91f..b53b414fdb5 100644 --- a/django/core/management/base.py +++ b/django/core/management/base.py @@ -5,6 +5,7 @@ be executed through ``django-admin`` or ``manage.py``). import os import sys from argparse import ArgumentParser +from contextlib import suppress from io import TextIOBase import django @@ -297,12 +298,10 @@ class BaseCommand: self.stderr.write('%s: %s' % (e.__class__.__name__, e)) sys.exit(1) finally: - try: + # Ignore if connections aren't setup at this point (e.g. no + # configured settings). + with suppress(ImproperlyConfigured): connections.close_all() - except ImproperlyConfigured: - # Ignore if connections aren't setup at this point (e.g. no - # configured settings). - pass def execute(self, *args, **options): """ diff --git a/django/core/management/commands/flush.py b/django/core/management/commands/flush.py index e188e4613cb..3e9d9fefc95 100644 --- a/django/core/management/commands/flush.py +++ b/django/core/management/commands/flush.py @@ -1,3 +1,4 @@ +from contextlib import suppress from importlib import import_module from django.apps import apps @@ -39,10 +40,8 @@ class Command(BaseCommand): # Import the 'management' module within each installed app, to register # dispatcher events. for app_config in apps.get_app_configs(): - try: + with suppress(ImportError): import_module('.management', app_config.name) - except ImportError: - pass sql_list = sql_flush(self.style, connection, only_django=True, reset_sequences=reset_sequences, diff --git a/django/core/management/commands/shell.py b/django/core/management/commands/shell.py index 243680c5910..1bf91972695 100644 --- a/django/core/management/commands/shell.py +++ b/django/core/management/commands/shell.py @@ -1,6 +1,7 @@ import os import select import sys +from contextlib import suppress from django.core.management import BaseCommand, CommandError from django.utils.datastructures import OrderedSet @@ -68,11 +69,9 @@ class Command(BaseCommand): continue if not os.path.isfile(pythonrc): continue - try: + with suppress(NameError): with open(pythonrc) as handle: exec(compile(handle.read(), pythonrc, 'exec'), imported_objects) - except NameError: - pass code.interact(local=imported_objects) def handle(self, **options): @@ -90,8 +89,6 @@ class Command(BaseCommand): available_shells = [options['interface']] if options['interface'] else self.shells for shell in available_shells: - try: + with suppress(ImportError): return getattr(self, shell)(options) - except ImportError: - pass raise CommandError("Couldn't import {} interface.".format(shell)) diff --git a/django/core/validators.py b/django/core/validators.py index 1a36246171e..bfeaa2c7dc5 100644 --- a/django/core/validators.py +++ b/django/core/validators.py @@ -1,6 +1,7 @@ import ipaddress import os import re +from contextlib import suppress from urllib.parse import urlsplit, urlunsplit from django.core.exceptions import ValidationError @@ -200,10 +201,11 @@ class EmailValidator: # Try for possible IDN domain-part try: domain_part = domain_part.encode('idna').decode('ascii') - if self.validate_domain_part(domain_part): - return except UnicodeError: pass + else: + if self.validate_domain_part(domain_part): + return raise ValidationError(self.message, code=self.code) def validate_domain_part(self, domain_part): @@ -213,11 +215,9 @@ class EmailValidator: literal_match = self.literal_regex.match(domain_part) if literal_match: ip_address = literal_match.group(1) - try: + with suppress(ValidationError): validate_ipv46_address(ip_address) return True - except ValidationError: - pass return False def __eq__(self, other): diff --git a/django/db/backends/postgresql/client.py b/django/db/backends/postgresql/client.py index 466c2986d20..8d08b0d5cf2 100644 --- a/django/db/backends/postgresql/client.py +++ b/django/db/backends/postgresql/client.py @@ -1,6 +1,7 @@ import os import signal import subprocess +from contextlib import suppress from django.core.files.temp import NamedTemporaryFile from django.db.backends.base.client import BaseDatabaseClient @@ -40,7 +41,9 @@ class DatabaseClient(BaseDatabaseClient): if passwd: # Create temporary .pgpass file. temp_pgpass = NamedTemporaryFile(mode='w+') - try: + # If the current locale can't encode the data, let the user + # input the password manually. + with suppress(UnicodeEncodeError): print( _escape_pgpass(host) or '*', str(port) or '*', @@ -52,10 +55,6 @@ class DatabaseClient(BaseDatabaseClient): flush=True, ) os.environ['PGPASSFILE'] = temp_pgpass.name - except UnicodeEncodeError: - # If the current locale can't encode the data, we let - # the user input the password manually. - pass # Allow SIGINT to pass to psql to abort queries. signal.signal(signal.SIGINT, signal.SIG_IGN) subprocess.check_call(args) diff --git a/django/db/backends/sqlite3/operations.py b/django/db/backends/sqlite3/operations.py index 040e148c346..5ffc65e08a6 100644 --- a/django/db/backends/sqlite3/operations.py +++ b/django/db/backends/sqlite3/operations.py @@ -1,5 +1,6 @@ import datetime import uuid +from contextlib import suppress from django.conf import settings from django.core.exceptions import FieldError @@ -33,7 +34,9 @@ class DatabaseOperations(BaseDatabaseOperations): bad_aggregates = (aggregates.Sum, aggregates.Avg, aggregates.Variance, aggregates.StdDev) if isinstance(expression, bad_aggregates): for expr in expression.get_source_expressions(): - try: + # Not every subexpression has an output_field which is fine + # to ignore. + with suppress(FieldError): output_field = expr.output_field if isinstance(output_field, bad_fields): raise NotImplementedError( @@ -41,10 +44,6 @@ class DatabaseOperations(BaseDatabaseOperations): 'aggregations on date/time fields in sqlite3 ' 'since date/time is saved as text.' ) - except FieldError: - # Not every subexpression has an output_field which is fine - # to ignore. - pass def date_extract_sql(self, lookup_type, field_name): """ diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py index 3a49e26c92b..2973995d40e 100644 --- a/django/db/migrations/autodetector.py +++ b/django/db/migrations/autodetector.py @@ -1,5 +1,6 @@ import functools import re +from contextlib import suppress from itertools import chain from django.conf import settings @@ -429,7 +430,7 @@ class MigrationAutodetector: Place potential swappable models first in lists of created models (only real way to solve #22783). """ - try: + with suppress(LookupError): model = self.new_apps.get_model(item[0], item[1]) base_names = [base.__name__ for base in model.__bases__] string_version = "%s.%s" % (item[0], item[1]) @@ -440,8 +441,6 @@ class MigrationAutodetector: settings.AUTH_USER_MODEL.lower() == string_version.lower() ): return ("___" + item[0], "___" + item[1]) - except LookupError: - pass return item def generate_renamed_models(self): diff --git a/django/db/migrations/questioner.py b/django/db/migrations/questioner.py index b65a4f67095..9b5b9f35108 100644 --- a/django/db/migrations/questioner.py +++ b/django/db/migrations/questioner.py @@ -97,10 +97,11 @@ class InteractiveMigrationQuestioner(MigrationQuestioner): while True: try: value = int(result) - if 0 < value <= len(choices): - return value except ValueError: pass + else: + if 0 < value <= len(choices): + return value result = input("Please select a valid option: ") def _ask_default(self, default=''): diff --git a/django/db/migrations/state.py b/django/db/migrations/state.py index 0578badabe0..3bbdf4633b0 100644 --- a/django/db/migrations/state.py +++ b/django/db/migrations/state.py @@ -1,6 +1,6 @@ import copy from collections import OrderedDict -from contextlib import contextmanager +from contextlib import contextmanager, suppress from django.apps import AppConfig from django.apps.registry import Apps, apps as global_apps @@ -342,11 +342,9 @@ class StateApps(Apps): self.clear_cache() def unregister_model(self, app_label, model_name): - try: + with suppress(KeyError): del self.all_models[app_label][model_name] del self.app_configs[app_label].models[model_name] - except KeyError: - pass class ModelState: diff --git a/django/db/migrations/writer.py b/django/db/migrations/writer.py index aa296db8c58..00ff03e4943 100644 --- a/django/db/migrations/writer.py +++ b/django/db/migrations/writer.py @@ -1,5 +1,6 @@ import os import re +from contextlib import suppress from importlib import import_module from django import get_version @@ -222,10 +223,8 @@ class MigrationWriter: except ImportError: pass else: - try: + with suppress(ValueError): return module_dir(migrations_module) - except ValueError: - pass # Alright, see if it's a direct submodule of the app app_config = apps.get_app_config(self.migration.app_label) diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py index 67de5adf743..fde506095c5 100644 --- a/django/db/models/expressions.py +++ b/django/db/models/expressions.py @@ -1,5 +1,6 @@ import copy import datetime +from contextlib import suppress from django.core.exceptions import EmptyResultSet, FieldError from django.db.backends import utils as backend_utils @@ -577,11 +578,9 @@ class Func(Expression): def as_sqlite(self, compiler, connection, **extra_context): sql, params = self.as_sql(compiler, connection, **extra_context) - try: + with suppress(FieldError): if self.output_field.get_internal_type() == 'DecimalField': sql = 'CAST(%s AS NUMERIC)' % sql - except FieldError: - pass return sql, params def copy(self): diff --git a/django/db/models/options.py b/django/db/models/options.py index 90462278d9f..9ed2915edbd 100644 --- a/django/db/models/options.py +++ b/django/db/models/options.py @@ -3,6 +3,7 @@ import inspect import warnings from bisect import bisect from collections import OrderedDict, defaultdict +from contextlib import suppress from itertools import chain from django.apps import apps @@ -269,10 +270,8 @@ class Options: # is a cached property, and all the models haven't been loaded yet, so # we need to make sure we don't cache a string reference. if field.is_relation and hasattr(field.remote_field, 'model') and field.remote_field.model: - try: + with suppress(AttributeError): field.remote_field.model._meta._expire_cache(forward=False) - except AttributeError: - pass self._expire_cache() else: self._expire_cache(reverse=False) @@ -520,10 +519,8 @@ class Options: # Due to the way Django's internals work, get_field() should also # be able to fetch a field by attname. In the case of a concrete # field with relation, includes the *_id name too - try: + with suppress(AttributeError): res[field.attname] = field - except AttributeError: - pass return res @cached_property @@ -535,10 +532,8 @@ class Options: # Due to the way Django's internals work, get_field() should also # be able to fetch a field by attname. In the case of a concrete # field with relation, includes the *_id name too - try: + with suppress(AttributeError): res[field.attname] = field - except AttributeError: - pass return res def get_field(self, field_name): @@ -755,12 +750,10 @@ class Options: # Creates a cache key composed of all arguments cache_key = (forward, reverse, include_parents, include_hidden, topmost_call) - try: + with suppress(KeyError): # In order to avoid list manipulation. Always return a shallow copy # of the results. return self._get_fields_cache[cache_key] - except KeyError: - pass fields = [] # Recursively call _get_fields() on each parent, with the same diff --git a/django/db/models/query.py b/django/db/models/query.py index b686fd6cab9..b71e9a87d50 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -6,6 +6,7 @@ import copy import sys import warnings from collections import OrderedDict, deque +from contextlib import suppress from django.conf import settings from django.core import exceptions @@ -488,10 +489,8 @@ class QuerySet: return obj, True except IntegrityError: exc_info = sys.exc_info() - try: + with suppress(self.model.DoesNotExist): return self.get(**lookup), False - except self.model.DoesNotExist: - pass raise exc_info[0](exc_info[1]).with_traceback(exc_info[2]) def _extract_model_params(self, defaults, **kwargs): @@ -1256,12 +1255,10 @@ class RawQuerySet: columns = self.query.get_columns() # Adjust any column names which don't match field names for (query_name, model_name) in self.translations.items(): - try: + # Ignore translations for nonexistent column names + with suppress(ValueError): index = columns.index(query_name) columns[index] = model_name - except ValueError: - # Ignore translations for nonexistent column names - pass return columns @cached_property diff --git a/django/forms/fields.py b/django/forms/fields.py index 1bc6953561d..3a0609b9153 100644 --- a/django/forms/fields.py +++ b/django/forms/fields.py @@ -8,6 +8,7 @@ import itertools import os import re import uuid +from contextlib import suppress from decimal import Decimal, DecimalException from io import BytesIO from urllib.parse import urlsplit, urlunsplit @@ -1086,7 +1087,7 @@ class FilePathField(ChoiceField): f = os.path.join(root, f) self.choices.append((f, f.replace(path, "", 1))) else: - try: + with suppress(OSError): for f in sorted(os.listdir(self.path)): if f == '__pycache__': continue @@ -1095,8 +1096,6 @@ class FilePathField(ChoiceField): (self.allow_folders and os.path.isdir(full_file))) and (self.match is None or self.match_re.search(f))): self.choices.append((full_file, f)) - except OSError: - pass self.widget.choices = self.choices diff --git a/django/forms/forms.py b/django/forms/forms.py index e2e7c645ff2..0b434b24f8b 100644 --- a/django/forms/forms.py +++ b/django/forms/forms.py @@ -4,6 +4,7 @@ Form classes import copy from collections import OrderedDict +from contextlib import suppress from django.core.exceptions import NON_FIELD_ERRORS, ValidationError # BoundField is imported for backwards compatibility in Django 1.9 @@ -125,10 +126,8 @@ class BaseForm: return fields = OrderedDict() for key in field_order: - try: + with suppress(KeyError): # ignore unknown fields fields[key] = self.fields.pop(key) - except KeyError: # ignore unknown fields - pass fields.update(self.fields) # add remaining fields in original order self.fields = fields diff --git a/django/forms/formsets.py b/django/forms/formsets.py index 49febc2b2e7..0531d502044 100644 --- a/django/forms/formsets.py +++ b/django/forms/formsets.py @@ -1,3 +1,5 @@ +from contextlib import suppress + from django.core.exceptions import ValidationError from django.forms import Form from django.forms.fields import BooleanField, IntegerField @@ -160,10 +162,8 @@ class BaseFormSet: defaults['data'] = self.data defaults['files'] = self.files if self.initial and 'initial' not in kwargs: - try: + with suppress(IndexError): defaults['initial'] = self.initial[i] - except IndexError: - pass # Allow extra forms to be empty, unless they're part of # the minimum forms. if i >= self.initial_form_count() and i >= self.min_num: diff --git a/django/forms/models.py b/django/forms/models.py index f4467d2ebb5..79e4452b17e 100644 --- a/django/forms/models.py +++ b/django/forms/models.py @@ -4,6 +4,7 @@ and database field objects. """ from collections import OrderedDict +from contextlib import suppress from itertools import chain from django.core.exceptions import ( @@ -588,10 +589,8 @@ class BaseModelFormSet(BaseFormSet): kwargs['instance'] = self.get_queryset()[i] if i >= self.initial_form_count() and self.initial_extra: # Set initial values for extra forms - try: + with suppress(IndexError): kwargs['initial'] = self.initial_extra[i - self.initial_form_count()] - except IndexError: - pass return super()._construct_form(i, **kwargs) def get_queryset(self): diff --git a/django/forms/widgets.py b/django/forms/widgets.py index 282ba0b4818..599d1e80117 100644 --- a/django/forms/widgets.py +++ b/django/forms/widgets.py @@ -5,6 +5,7 @@ HTML Widget classes import copy import datetime import re +from contextlib import suppress from itertools import chain from django.conf import settings @@ -618,10 +619,8 @@ class ChoiceWidget(Widget): def value_from_datadict(self, data, files, name): getter = data.get if self.allow_multiple_selected: - try: + with suppress(AttributeError): getter = data.getlist - except AttributeError: - pass return getter(name) def format_value(self, value): @@ -977,12 +976,13 @@ class SelectDateWidget(Widget): year, month, day = value.year, value.month, value.day elif isinstance(value, str): if settings.USE_L10N: + input_format = get_format('DATE_INPUT_FORMATS')[0] try: - input_format = get_format('DATE_INPUT_FORMATS')[0] d = datetime.datetime.strptime(value, input_format) - year, month, day = d.year, d.month, d.day except ValueError: pass + else: + year, month, day = d.year, d.month, d.day match = self.date_re.match(value) if match: year, month, day = [int(val) for val in match.groups()] diff --git a/django/http/response.py b/django/http/response.py index 1083fd7dca8..384de9fb849 100644 --- a/django/http/response.py +++ b/django/http/response.py @@ -3,6 +3,7 @@ import json import re import sys import time +from contextlib import suppress from email.header import Header from http.client import responses from urllib.parse import urlparse @@ -136,10 +137,8 @@ class HttpResponseBase: self._headers[header.lower()] = (header, value) def __delitem__(self, header): - try: + with suppress(KeyError): del self._headers[header.lower()] - except KeyError: - pass def __getitem__(self, header): return self._headers[header.lower()][1] @@ -238,10 +237,8 @@ class HttpResponseBase: # See http://blog.dscpl.com.au/2012/10/obligations-for-calling-close-on.html def close(self): for closable in self._closable_objects: - try: + with suppress(Exception): closable.close() - except Exception: - pass self.closed = True signals.request_finished.send(sender=self._handler_class) @@ -307,10 +304,8 @@ class HttpResponse(HttpResponseBase): if hasattr(value, '__iter__') and not isinstance(value, (bytes, str)): content = b''.join(self.make_bytes(chunk) for chunk in value) if hasattr(value, 'close'): - try: + with suppress(Exception): value.close() - except Exception: - pass else: content = self.make_bytes(value) # Create a list of properly encoded bytestrings to support write(). diff --git a/django/template/backends/base.py b/django/template/backends/base.py index c47c95e51e5..4156365b97b 100644 --- a/django/template/backends/base.py +++ b/django/template/backends/base.py @@ -1,3 +1,5 @@ +from contextlib import suppress + from django.core.exceptions import ( ImproperlyConfigured, SuspiciousFileOperation, ) @@ -73,9 +75,8 @@ class BaseEngine: directory traversal attacks. """ for template_dir in self.template_dirs: - try: + # SuspiciousFileOperation occurs if the jointed path is located + # outside of this template_dir (it might be inside another one, + # so this isn't fatal). + with suppress(SuspiciousFileOperation): yield safe_join(template_dir, template_name) - except SuspiciousFileOperation: - # The joined path was located outside of this template_dir - # (it might be inside another one, so this isn't fatal). - pass diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py index e8e14fd9232..6dafe70ba51 100644 --- a/django/template/defaultfilters.py +++ b/django/template/defaultfilters.py @@ -1,6 +1,7 @@ """Default variable filters.""" import random as random_module import re +from contextlib import suppress from decimal import ROUND_HALF_UP, Context, Decimal, InvalidOperation from functools import wraps from operator import itemgetter @@ -606,7 +607,7 @@ def unordered_list(value, autoescape=True): def walk_items(item_list): item_iterator = iter(item_list) - try: + with suppress(StopIteration): item = next(item_iterator) while True: try: @@ -625,8 +626,6 @@ def unordered_list(value, autoescape=True): continue yield item, None item = next_item - except StopIteration: - pass def list_formatter(item_list, tabs=1): indent = '\t' * tabs @@ -876,11 +875,9 @@ def pluralize(value, arg='s'): except ValueError: # Invalid string that's not a number. pass except TypeError: # Value isn't a string or a number; maybe it's a list? - try: + with suppress(TypeError): # len() of unsized object. if len(value) != 1: return plural_suffix - except TypeError: # len() of unsized object. - pass return singular_suffix diff --git a/django/test/signals.py b/django/test/signals.py index a623e756ce1..3507c69adda 100644 --- a/django/test/signals.py +++ b/django/test/signals.py @@ -2,6 +2,7 @@ import os import threading import time import warnings +from contextlib import suppress from django.apps import apps from django.core.exceptions import ImproperlyConfigured @@ -63,14 +64,10 @@ def update_connections_time_zone(**kwargs): # Reset the database connections' time zone if kwargs['setting'] in {'TIME_ZONE', 'USE_TZ'}: for conn in connections.all(): - try: + with suppress(AttributeError): del conn.timezone - except AttributeError: - pass - try: + with suppress(AttributeError): del conn.timezone_name - except AttributeError: - pass conn.ensure_timezone() @@ -89,10 +86,8 @@ def reset_template_engines(**kwargs): 'INSTALLED_APPS', }: from django.template import engines - try: + with suppress(AttributeError): del engines.templates - except AttributeError: - pass engines._templates = None engines._engines = {} from django.template.engine import Engine diff --git a/django/urls/base.py b/django/urls/base.py index 6dccdd2e7d4..26084dbe3ee 100644 --- a/django/urls/base.py +++ b/django/urls/base.py @@ -1,3 +1,4 @@ +from contextlib import suppress from threading import local from urllib.parse import urlsplit, urlunsplit @@ -53,7 +54,7 @@ def reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None): ns = path.pop() current_ns = current_path.pop() if current_path else None # Lookup the name to see if it could be an app identifier. - try: + with suppress(KeyError): app_list = resolver.app_dict[ns] # Yes! Path part matches an app in the current Resolver. if current_ns and current_ns in app_list: @@ -64,8 +65,6 @@ def reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None): # The name isn't shared by one of the instances (i.e., # the default) so pick the first instance as the default. ns = app_list[0] - except KeyError: - pass if ns != current_ns: current_path = None @@ -119,10 +118,8 @@ def clear_script_prefix(): """ Unset the script prefix for the current thread. """ - try: + with suppress(AttributeError): del _prefixes.value - except AttributeError: - pass def set_urlconf(urlconf_name): diff --git a/django/utils/autoreload.py b/django/utils/autoreload.py index 2784a89aebc..a872d42d65b 100644 --- a/django/utils/autoreload.py +++ b/django/utils/autoreload.py @@ -34,6 +34,7 @@ import subprocess import sys import time import traceback +from contextlib import suppress import _thread @@ -43,10 +44,8 @@ from django.core.signals import request_finished # This import does nothing, but it's necessary to avoid some race conditions # in the threading module. See http://code.djangoproject.com/ticket/2330 . -try: +with suppress(ImportError): import threading # NOQA -except ImportError: - pass try: import termios @@ -54,7 +53,7 @@ except ImportError: termios = None USE_INOTIFY = False -try: +with suppress(ImportError): # Test whether inotify is enabled and likely to work import pyinotify @@ -62,8 +61,6 @@ try: if fd >= 0: USE_INOTIFY = True os.close(fd) -except ImportError: - pass RUN_RELOADER = True @@ -210,10 +207,8 @@ def code_changed(): continue if mtime != _mtimes[filename]: _mtimes = {} - try: + with suppress(ValueError): del _error_files[_error_files.index(filename)] - except ValueError: - pass return I18N_MODIFIED if filename.endswith('.mo') else FILE_MODIFIED return False @@ -292,19 +287,15 @@ def restart_with_reloader(): def python_reloader(main_func, args, kwargs): if os.environ.get("RUN_MAIN") == "true": _thread.start_new_thread(main_func, args, kwargs) - try: + with suppress(KeyboardInterrupt): reloader_thread() - except KeyboardInterrupt: - pass else: - try: + with suppress(KeyboardInterrupt): exit_code = restart_with_reloader() if exit_code < 0: os.kill(os.getpid(), -exit_code) else: sys.exit(exit_code) - except KeyboardInterrupt: - pass def jython_reloader(main_func, args, kwargs): diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py index 769637f1d3f..b42f674eb01 100644 --- a/django/utils/datastructures.py +++ b/django/utils/datastructures.py @@ -1,5 +1,6 @@ import copy from collections import OrderedDict +from contextlib import suppress class OrderedSet: @@ -18,10 +19,8 @@ class OrderedSet: del self.dict[item] def discard(self, item): - try: + with suppress(KeyError): self.remove(item) - except KeyError: - pass def __iter__(self): return iter(self.dict) diff --git a/django/utils/dateformat.py b/django/utils/dateformat.py index d3f586aacf0..d811e839658 100644 --- a/django/utils/dateformat.py +++ b/django/utils/dateformat.py @@ -14,6 +14,7 @@ import calendar import datetime import re import time +from contextlib import suppress from django.utils.dates import ( MONTHS, MONTHS_3, MONTHS_ALT, MONTHS_AP, WEEKDAYS, WEEKDAYS_ABBR, @@ -81,11 +82,9 @@ class TimeFormat(Formatter): if not self.timezone: return "" - try: + with suppress(NotImplementedError): if hasattr(self.data, 'tzinfo') and self.data.tzinfo: return self.data.tzname() or '' - except NotImplementedError: - pass return "" def f(self): @@ -166,13 +165,11 @@ class TimeFormat(Formatter): return "" name = None - try: - name = self.timezone.tzname(self.data) - except Exception: + with suppress(Exception): # pytz raises AmbiguousTimeError during the autumn DST change. # This happens mainly when __init__ receives a naive datetime # and sets self.timezone = get_default_timezone(). - pass + name = self.timezone.tzname(self.data) if name is None: name = self.format('O') return str(name) diff --git a/django/utils/formats.py b/django/utils/formats.py index b0c78f5ab2b..33865d93a86 100644 --- a/django/utils/formats.py +++ b/django/utils/formats.py @@ -1,6 +1,7 @@ import datetime import decimal import unicodedata +from contextlib import suppress from importlib import import_module from django.conf import settings @@ -79,10 +80,8 @@ def iter_format_modules(lang, format_module_path=None): locales.append(locale.split('_')[0]) for location in format_locations: for loc in locales: - try: + with suppress(ImportError): yield import_module('%s.formats' % (location % loc)) - except ImportError: - pass def get_format_modules(lang=None, reverse=False): @@ -110,10 +109,8 @@ def get_format(format_type, lang=None, use_l10n=None): if use_l10n and lang is None: lang = get_language() cache_key = (format_type, lang) - try: + with suppress(KeyError): return _format_cache[cache_key] - except KeyError: - pass # The requested format_type has not been cached yet. Try to find it in any # of the format_modules for the given lang if l10n is enabled. If it's not @@ -121,12 +118,9 @@ def get_format(format_type, lang=None, use_l10n=None): val = None if use_l10n: for module in get_format_modules(lang): - try: - val = getattr(module, format_type) - if val is not None: - break - except AttributeError: - pass + val = getattr(module, format_type, None) + if val is not None: + break if val is None: if format_type not in FORMAT_SETTINGS: return format_type diff --git a/django/utils/http.py b/django/utils/http.py index 07b6ae246a9..0870f1f180a 100644 --- a/django/utils/http.py +++ b/django/utils/http.py @@ -5,6 +5,7 @@ import re import unicodedata import warnings from binascii import Error as BinasciiError +from contextlib import suppress from email.utils import formatdate from urllib.parse import ( ParseResult, SplitResult, _coerce_args, _splitnetloc, _splitparams, quote, @@ -165,10 +166,8 @@ def parse_http_date_safe(date): """ Same as parse_http_date, but return None if the input is invalid. """ - try: + with suppress(Exception): return parse_http_date(date) - except Exception: - pass # Base 36 functions: useful for generating compact URLs diff --git a/django/utils/translation/__init__.py b/django/utils/translation/__init__.py index f342cf7227a..4a6840782c0 100644 --- a/django/utils/translation/__init__.py +++ b/django/utils/translation/__init__.py @@ -3,7 +3,7 @@ Internationalization support. """ import re import warnings -from contextlib import ContextDecorator +from contextlib import ContextDecorator, suppress from django.utils.deprecation import RemovedInDjango21Warning from django.utils.functional import lazy @@ -126,11 +126,9 @@ def lazy_number(func, resultclass, number=None, **kwargs): number_value = rhs kwargs['number'] = number_value translated = func(**kwargs) - try: + # String may not contain a placeholder for the number. + with suppress(TypeError): translated = translated % rhs - except TypeError: - # String doesn't contain a placeholder for the number - pass return translated proxy = lazy(lambda **kwargs: NumberAwareString(), NumberAwareString)(**kwargs) diff --git a/django/utils/translation/trans_real.py b/django/utils/translation/trans_real.py index 6b3aeb127ea..f36c21f0a11 100644 --- a/django/utils/translation/trans_real.py +++ b/django/utils/translation/trans_real.py @@ -6,6 +6,7 @@ import re import sys import warnings from collections import OrderedDict +from contextlib import suppress from threading import local from django.apps import apps @@ -256,10 +257,8 @@ def get_language(): """Return the currently selected language.""" t = getattr(_active, "value", None) if t is not None: - try: + with suppress(AttributeError): return t.to_language() - except AttributeError: - pass # If we don't have a real translation object, assume it's the default language. return settings.LANGUAGE_CODE @@ -425,10 +424,8 @@ def get_supported_language_variant(lang_code, strict=False): if lang_code: # If 'fr-ca' is not supported, try special fallback or language-only 'fr'. possible_lang_codes = [lang_code] - try: + with suppress(KeyError): possible_lang_codes.extend(LANG_INFO[lang_code]['fallback']) - except KeyError: - pass generic_lang_code = lang_code.split('-')[0] possible_lang_codes.append(generic_lang_code) supported_lang_codes = get_languages() @@ -486,10 +483,8 @@ def get_language_from_request(request, check_path=False): lang_code = request.COOKIES.get(settings.LANGUAGE_COOKIE_NAME) - try: + with suppress(LookupError): return get_supported_language_variant(lang_code) - except LookupError: - pass accept = request.META.get('HTTP_ACCEPT_LANGUAGE', '') for accept_lang, unused in parse_accept_lang_header(accept): diff --git a/django/views/debug.py b/django/views/debug.py index 5fa412d4364..15566a25cdf 100644 --- a/django/views/debug.py +++ b/django/views/debug.py @@ -2,6 +2,7 @@ import functools import re import sys import types +from contextlib import suppress from pathlib import Path from django.conf import settings @@ -344,18 +345,14 @@ class ExceptionReporter: """ source = None if loader is not None and hasattr(loader, "get_source"): - try: + with suppress(ImportError): source = loader.get_source(module_name) - except ImportError: - pass if source is not None: source = source.splitlines() if source is None: - try: + with suppress(OSError, IOError): with open(filename, 'rb') as fp: source = fp.read().splitlines() - except (OSError, IOError): - pass if source is None: return None, [], None, [] diff --git a/tests/admin_scripts/tests.py b/tests/admin_scripts/tests.py index e2d2a3bca1a..d90456ac138 100644 --- a/tests/admin_scripts/tests.py +++ b/tests/admin_scripts/tests.py @@ -12,6 +12,7 @@ import subprocess import sys import tempfile import unittest +from contextlib import suppress from io import StringIO from unittest import mock @@ -95,12 +96,10 @@ class AdminScriptTestCase(unittest.TestCase): # Also try to remove the compiled file; if it exists, it could # mess up later tests that depend upon the .py file not existing - try: + with suppress(OSError): if sys.platform.startswith('java'): # Jython produces module$py.class files os.remove(re.sub(r'\.py$', '$py.class', full_name)) - except OSError: - pass # Also remove a __pycache__ directory, if it exists cache_name = os.path.join(self.test_dir, '__pycache__') if os.path.isdir(cache_name): @@ -166,10 +165,8 @@ class AdminScriptTestCase(unittest.TestCase): def run_manage(self, args, settings_file=None): def safe_remove(path): - try: + with suppress(OSError): os.remove(path) - except OSError: - pass conf_dir = os.path.dirname(conf.__file__) template_manage_py = os.path.join(conf_dir, 'project_template', 'manage.py-tpl') diff --git a/tests/backends/tests.py b/tests/backends/tests.py index 6d38625a986..30c1cbf86d4 100644 --- a/tests/backends/tests.py +++ b/tests/backends/tests.py @@ -3,6 +3,7 @@ import datetime import threading import unittest import warnings +from contextlib import suppress from django.core.management.color import no_style from django.db import ( @@ -389,10 +390,8 @@ class BackendTestCase(TransactionTestCase): finally: # Clean up the mess created by connection._close(). Since the # connection is already closed, this crashes on some backends. - try: + with suppress(Exception): connection.close() - except Exception: - pass @override_settings(DEBUG=True) def test_queries(self): diff --git a/tests/bash_completion/tests.py b/tests/bash_completion/tests.py index 1d35e1f28e2..ba2a5ea773b 100644 --- a/tests/bash_completion/tests.py +++ b/tests/bash_completion/tests.py @@ -4,6 +4,7 @@ A series of tests to establish that the command-line bash completion works. import os import sys import unittest +from contextlib import suppress from django.apps import apps from django.core.management import ManagementUtility @@ -50,10 +51,8 @@ class BashCompletionTests(unittest.TestCase): def _run_autocomplete(self): util = ManagementUtility(argv=sys.argv) with captured_stdout() as stdout: - try: + with suppress(SystemExit): util.autocomplete() - except SystemExit: - pass return stdout.getvalue().strip().split('\n') def test_django_admin_py(self): diff --git a/tests/handlers/views.py b/tests/handlers/views.py index 22b94de3b90..3c1fa5b802b 100644 --- a/tests/handlers/views.py +++ b/tests/handlers/views.py @@ -1,12 +1,12 @@ +from contextlib import suppress + from django.core.exceptions import SuspiciousOperation from django.db import connection, transaction from django.http import HttpResponse, StreamingHttpResponse from django.views.decorators.csrf import csrf_exempt -try: +with suppress(ImportError): # Python < 3.5 from http import HTTPStatus -except ImportError: # Python < 3.5 - pass def regular(request): diff --git a/tests/mail/tests.py b/tests/mail/tests.py index 50922e21d7d..e1a7feb1f9f 100644 --- a/tests/mail/tests.py +++ b/tests/mail/tests.py @@ -8,6 +8,7 @@ import socket import sys import tempfile import threading +from contextlib import suppress from email import message_from_binary_file, message_from_bytes from email.header import Header from email.mime.text import MIMEText @@ -1123,12 +1124,10 @@ class ConsoleBackendTests(BaseEmailBackendTests, SimpleTestCase): class FakeSMTPChannel(smtpd.SMTPChannel): def collect_incoming_data(self, data): - try: + # Ignore decode error in SSL/TLS connection tests as the test only + # cares whether the connection attempt was made. + with suppress(UnicodeDecodeError): smtpd.SMTPChannel.collect_incoming_data(self, data) - except UnicodeDecodeError: - # ignore decode error in SSL/TLS connection tests as we only care - # whether the connection attempt was made - pass def smtp_AUTH(self, arg): if arg == 'CRAM-MD5': diff --git a/tests/postgres_tests/test_aggregates.py b/tests/postgres_tests/test_aggregates.py index 056d08441b4..1fe8a1bf03f 100644 --- a/tests/postgres_tests/test_aggregates.py +++ b/tests/postgres_tests/test_aggregates.py @@ -1,4 +1,5 @@ import json +from contextlib import suppress from django.db.models.expressions import F, Value from django.test.testcases import skipUnlessDBFeature @@ -7,14 +8,12 @@ from django.test.utils import Approximate from . import PostgreSQLTestCase from .models import AggregateTestModel, StatTestModel -try: +with suppress(ImportError): # psycopg2 is not installed from django.contrib.postgres.aggregates import ( ArrayAgg, BitAnd, BitOr, BoolAnd, BoolOr, Corr, CovarPop, JSONBAgg, RegrAvgX, RegrAvgY, RegrCount, RegrIntercept, RegrR2, RegrSlope, RegrSXX, RegrSXY, RegrSYY, StatAggregate, StringAgg, ) -except ImportError: - pass # psycopg2 is not installed class TestGeneralAggregate(PostgreSQLTestCase): diff --git a/tests/postgres_tests/test_array.py b/tests/postgres_tests/test_array.py index e2e4ccdeb27..7378b5c12db 100644 --- a/tests/postgres_tests/test_array.py +++ b/tests/postgres_tests/test_array.py @@ -2,6 +2,7 @@ import decimal import json import unittest import uuid +from contextlib import suppress from django import forms from django.core import exceptions, serializers, validators @@ -19,13 +20,11 @@ from .models import ( PostgreSQLModel, Tag, ) -try: +with suppress(ImportError): from django.contrib.postgres.fields import ArrayField from django.contrib.postgres.forms import ( SimpleArrayField, SplitArrayField, SplitArrayWidget, ) -except ImportError: - pass class TestSaveLoad(PostgreSQLTestCase): diff --git a/tests/postgres_tests/test_hstore.py b/tests/postgres_tests/test_hstore.py index 069e570f51d..55b179ba5e9 100644 --- a/tests/postgres_tests/test_hstore.py +++ b/tests/postgres_tests/test_hstore.py @@ -1,4 +1,5 @@ import json +from contextlib import suppress from django.core import exceptions, serializers from django.forms import Form @@ -7,12 +8,10 @@ from django.test.utils import modify_settings from . import PostgreSQLTestCase from .models import HStoreModel -try: +with suppress(ImportError): from django.contrib.postgres import forms from django.contrib.postgres.fields import HStoreField from django.contrib.postgres.validators import KeysValidator -except ImportError: - pass @modify_settings(INSTALLED_APPS={'append': 'django.contrib.postgres'}) diff --git a/tests/postgres_tests/test_json.py b/tests/postgres_tests/test_json.py index 2506fc36d62..20650ae95b1 100644 --- a/tests/postgres_tests/test_json.py +++ b/tests/postgres_tests/test_json.py @@ -1,5 +1,6 @@ import datetime import uuid +from contextlib import suppress from decimal import Decimal from django.core import exceptions, serializers @@ -11,11 +12,9 @@ from django.utils.html import escape from . import PostgreSQLTestCase from .models import JSONModel -try: +with suppress(ImportError): from django.contrib.postgres import forms from django.contrib.postgres.fields import JSONField -except ImportError: - pass @skipUnlessDBFeature('has_jsonb_datatype') diff --git a/tests/postgres_tests/test_ranges.py b/tests/postgres_tests/test_ranges.py index d87ad364389..da72240bf4c 100644 --- a/tests/postgres_tests/test_ranges.py +++ b/tests/postgres_tests/test_ranges.py @@ -1,5 +1,6 @@ import datetime import json +from contextlib import suppress from django import forms from django.core import exceptions, serializers @@ -10,14 +11,12 @@ from django.utils import timezone from . import PostgreSQLTestCase from .models import RangeLookupsModel, RangesModel -try: +with suppress(ImportError): from psycopg2.extras import DateRange, DateTimeTZRange, NumericRange from django.contrib.postgres import fields as pg_fields, forms as pg_forms from django.contrib.postgres.validators import ( RangeMaxValueValidator, RangeMinValueValidator, ) -except ImportError: - pass class TestSaveLoad(PostgreSQLTestCase): diff --git a/tests/runtests.py b/tests/runtests.py index 7f4f1670c51..08e53f4cb1d 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -8,6 +8,7 @@ import subprocess import sys import tempfile import warnings +from contextlib import suppress import django from django.apps import apps @@ -315,10 +316,8 @@ def bisect_tests(bisection_label, options, test_labels, parallel): # Make sure the bisection point isn't in the test list # Also remove tests that need to be run in specific combinations for label in [bisection_label, 'model_inheritance_same_model_name']: - try: + with suppress(ValueError): test_labels.remove(label) - except ValueError: - pass subprocess_args = get_subprocess_args(options) @@ -366,10 +365,8 @@ def paired_tests(paired_test, options, test_labels, parallel): # Make sure the constant member of the pair isn't in the test list # Also remove tests that need to be run in specific combinations for label in [paired_test, 'model_inheritance_same_model_name']: - try: + with suppress(ValueError): test_labels.remove(label) - except ValueError: - pass subprocess_args = get_subprocess_args(options) diff --git a/tests/staticfiles_tests/storage.py b/tests/staticfiles_tests/storage.py index 7a1f72c1303..0dcfd77a70f 100644 --- a/tests/staticfiles_tests/storage.py +++ b/tests/staticfiles_tests/storage.py @@ -1,4 +1,5 @@ import os +from contextlib import suppress from datetime import datetime, timedelta from django.conf import settings @@ -48,10 +49,8 @@ class PathNotImplementedStorage(storage.Storage): def delete(self, name): name = self._path(name) - try: + with suppress(FileNotFoundError): os.remove(name) - except FileNotFoundError: - pass def path(self, name): raise NotImplementedError diff --git a/tests/transaction_hooks/tests.py b/tests/transaction_hooks/tests.py index 000de4104cc..049211139dd 100644 --- a/tests/transaction_hooks/tests.py +++ b/tests/transaction_hooks/tests.py @@ -1,3 +1,5 @@ +from contextlib import suppress + from django.db import connection, transaction from django.test import TransactionTestCase, skipUnlessDBFeature @@ -48,12 +50,10 @@ class TestConnectionOnCommit(TransactionTestCase): self.assertDone([1]) def test_does_not_execute_if_transaction_rolled_back(self): - try: + with suppress(ForcedError): with transaction.atomic(): self.do(1) raise ForcedError() - except ForcedError: - pass self.assertDone([]) @@ -71,12 +71,10 @@ class TestConnectionOnCommit(TransactionTestCase): with transaction.atomic(): self.do(1) # one failed savepoint - try: + with suppress(ForcedError): with transaction.atomic(): self.do(2) raise ForcedError() - except ForcedError: - pass # another successful savepoint with transaction.atomic(): self.do(3) @@ -86,25 +84,21 @@ class TestConnectionOnCommit(TransactionTestCase): def test_no_hooks_run_from_failed_transaction(self): """If outer transaction fails, no hooks from within it run.""" - try: + with suppress(ForcedError): with transaction.atomic(): with transaction.atomic(): self.do(1) raise ForcedError() - except ForcedError: - pass self.assertDone([]) def test_inner_savepoint_rolled_back_with_outer(self): with transaction.atomic(): - try: + with suppress(ForcedError): with transaction.atomic(): with transaction.atomic(): self.do(1) raise ForcedError() - except ForcedError: - pass self.do(2) self.assertDone([2]) @@ -113,11 +107,9 @@ class TestConnectionOnCommit(TransactionTestCase): with transaction.atomic(): with transaction.atomic(): self.do(1) - try: + with suppress(ForcedError): with transaction.atomic(savepoint=False): raise ForcedError() - except ForcedError: - pass self.assertDone([]) @@ -125,11 +117,9 @@ class TestConnectionOnCommit(TransactionTestCase): with transaction.atomic(): with transaction.atomic(): self.do(1) - try: + with suppress(ForcedError): with transaction.atomic(): raise ForcedError() - except ForcedError: - pass self.assertDone([1]) @@ -151,12 +141,10 @@ class TestConnectionOnCommit(TransactionTestCase): self.assertDone([1, 2]) # not [1, 1, 2] def test_hooks_cleared_after_rollback(self): - try: + with suppress(ForcedError): with transaction.atomic(): self.do(1) raise ForcedError() - except ForcedError: - pass with transaction.atomic(): self.do(2) @@ -177,11 +165,9 @@ class TestConnectionOnCommit(TransactionTestCase): self.assertDone([2]) def test_error_in_hook_doesnt_prevent_clearing_hooks(self): - try: + with suppress(ForcedError): with transaction.atomic(): transaction.on_commit(lambda: self.notify('error')) - except ForcedError: - pass with transaction.atomic(): self.do(1)