Browse Source

Fixed #30137 -- Replaced OSError aliases with the canonical OSError.

Used more specific errors (e.g. FileExistsError) as appropriate.
Jon Dufresne 6 years ago
parent
commit
7785e03ba8

+ 1 - 1
django/contrib/auth/password_validation.py

@@ -173,7 +173,7 @@ class CommonPasswordValidator:
         try:
             with gzip.open(str(password_list_path)) as f:
                 common_passwords_lines = f.read().decode().splitlines()
-        except IOError:
+        except OSError:
             with open(str(password_list_path)) as f:
                 common_passwords_lines = f.readlines()
 

+ 1 - 2
django/contrib/gis/gdal/libgdal.py

@@ -13,8 +13,7 @@ logger = logging.getLogger('django.contrib.gis')
 try:
     from django.conf import settings
     lib_path = settings.GDAL_LIBRARY_PATH
-except (AttributeError, EnvironmentError,
-        ImportError, ImproperlyConfigured):
+except (AttributeError, ImportError, ImproperlyConfigured, OSError):
     lib_path = None
 
 if lib_path:

+ 1 - 2
django/contrib/gis/geos/libgeos.py

@@ -23,8 +23,7 @@ def load_geos():
     try:
         from django.conf import settings
         lib_path = settings.GEOS_LIBRARY_PATH
-    except (AttributeError, EnvironmentError,
-            ImportError, ImproperlyConfigured):
+    except (AttributeError, ImportError, ImproperlyConfigured, OSError):
         lib_path = None
 
     # Setting the appropriate names for the GEOS-C library.

+ 2 - 2
django/contrib/sessions/backends/file.py

@@ -94,7 +94,7 @@ class SessionStore(SessionBase):
                     session_data = {}
                     self.delete()
                     self.create()
-        except (IOError, SuspiciousOperation):
+        except (OSError, SuspiciousOperation):
             self._session_key = None
         return session_data
 
@@ -166,7 +166,7 @@ class SessionStore(SessionBase):
             finally:
                 if not renamed:
                     os.unlink(output_file_name)
-        except (OSError, IOError, EOFError):
+        except (EOFError, OSError):
             pass
 
     def exists(self, session_key):

+ 2 - 2
django/contrib/staticfiles/storage.py

@@ -93,7 +93,7 @@ class HashedFilesMixin:
                 raise ValueError("The file '%s' could not be found with %r." % (filename, self))
             try:
                 content = self.open(filename)
-            except IOError:
+            except OSError:
                 # Handle directory paths and fragments
                 return name
         try:
@@ -380,7 +380,7 @@ class ManifestFilesMixin(HashedFilesMixin):
         try:
             with self.open(self.manifest_name) as manifest:
                 return manifest.read().decode()
-        except IOError:
+        except OSError:
             return None
 
     def load_manifest(self):

+ 2 - 2
django/core/files/move.py

@@ -35,7 +35,7 @@ def file_move_safe(old_file_name, new_file_name, chunk_size=1024 * 64, allow_ove
     If that fails, stream manually from one file to another in pure Python.
 
     If the destination file exists and ``allow_overwrite`` is ``False``, raise
-    ``IOError``.
+    ``FileExistsError``.
     """
     # There's no reason to move if we don't have to.
     if _samefile(old_file_name, new_file_name):
@@ -43,7 +43,7 @@ def file_move_safe(old_file_name, new_file_name, chunk_size=1024 * 64, allow_ove
 
     try:
         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)
+            raise FileExistsError('Destination file %s exists and allow_overwrite is False.' % new_file_name)
 
         os.rename(old_file_name, new_file_name)
         return

+ 1 - 1
django/core/files/storage.py

@@ -246,7 +246,7 @@ class FileSystemStorage(Storage):
                 # was created concurrently.
                 pass
         if not os.path.isdir(directory):
-            raise IOError("%s exists and is not a directory." % directory)
+            raise FileExistsError('%s exists and is not a directory.' % directory)
 
         # There's a potential race condition between get_available_name and
         # saving the file; it's possible that two threads might return the

+ 1 - 1
django/core/files/temp.py

@@ -50,7 +50,7 @@ if os.name == 'nt':
                 self.close_called = True
                 try:
                     self.file.close()
-                except (OSError, IOError):
+                except OSError:
                     pass
                 try:
                     self.unlink(self.name)

+ 1 - 2
django/core/mail/backends/smtp.py

@@ -1,6 +1,5 @@
 """SMTP email backend class."""
 import smtplib
-import socket
 import ssl
 import threading
 
@@ -69,7 +68,7 @@ class EmailBackend(BaseEmailBackend):
             if self.username and self.password:
                 self.connection.login(self.username, self.password)
             return True
-        except (smtplib.SMTPException, socket.error):
+        except OSError:
             if not self.fail_silently:
                 raise
 

+ 1 - 1
django/core/management/commands/compilemessages.py

@@ -19,7 +19,7 @@ def is_writable(path):
     try:
         with open(path, 'a'):
             os.utime(path, None)
-    except (IOError, OSError):
+    except OSError:
         return False
     return True
 

+ 1 - 1
django/core/management/commands/runserver.py

@@ -137,7 +137,7 @@ class Command(BaseCommand):
             handler = self.get_handler(*args, **options)
             run(self.addr, int(self.port), handler,
                 ipv6=self.use_ipv6, threading=threading, server_cls=self.server_cls)
-        except socket.error as e:
+        except OSError as e:
             # Use helpful error messages instead of ugly tracebacks.
             ERRORS = {
                 errno.EACCES: "You don't have permission to access that port.",

+ 2 - 2
django/core/management/templates.py

@@ -257,7 +257,7 @@ class TemplateCommand(BaseCommand):
             self.stdout.write("Downloading %s\n" % display_url)
         try:
             the_path, info = urlretrieve(url, path.join(tempdir, filename))
-        except IOError as e:
+        except OSError as e:
             raise CommandError("couldn't download URL %s to %s: %s" %
                                (url, filename, e))
 
@@ -312,7 +312,7 @@ class TemplateCommand(BaseCommand):
         try:
             archive.extract(filename, tempdir)
             return tempdir
-        except (archive.ArchiveException, IOError) as e:
+        except (archive.ArchiveException, OSError) as e:
             raise CommandError("couldn't extract file %s to %s: %s" %
                                (filename, tempdir, e))
 

+ 3 - 3
django/core/servers/basehttp.py

@@ -51,8 +51,8 @@ def get_internal_wsgi_application():
 
 
 def is_broken_pipe_error():
-    exc_type, exc_value = sys.exc_info()[:2]
-    return issubclass(exc_type, socket.error) and exc_value.args[0] == 32
+    exc_type, _, _ = sys.exc_info()
+    return issubclass(exc_type, BrokenPipeError)
 
 
 class WSGIServer(simple_server.WSGIServer):
@@ -171,7 +171,7 @@ class WSGIRequestHandler(simple_server.WSGIRequestHandler):
             self.handle_one_request()
         try:
             self.connection.shutdown(socket.SHUT_WR)
-        except (socket.error, AttributeError):
+        except (AttributeError, OSError):
             pass
 
     def handle_one_request(self):

+ 4 - 4
django/http/request.py

@@ -22,7 +22,7 @@ RAISE_ERROR = object()
 host_validation_re = re.compile(r"^([a-z0-9.-]+|\[[a-f0-9]*:[a-f0-9\.:]+\])(:\d+)?$")
 
 
-class UnreadablePostError(IOError):
+class UnreadablePostError(OSError):
     pass
 
 
@@ -284,7 +284,7 @@ class HttpRequest:
 
             try:
                 self._body = self.read()
-            except IOError as e:
+            except OSError as e:
                 raise UnreadablePostError(*e.args) from e
             self._stream = BytesIO(self._body)
         return self._body
@@ -339,14 +339,14 @@ class HttpRequest:
         self._read_started = True
         try:
             return self._stream.read(*args, **kwargs)
-        except IOError as e:
+        except OSError as e:
             raise UnreadablePostError(*e.args) from e
 
     def readline(self, *args, **kwargs):
         self._read_started = True
         try:
             return self._stream.readline(*args, **kwargs)
-        except IOError as e:
+        except OSError as e:
             raise UnreadablePostError(*e.args) from e
 
     def __iter__(self):

+ 3 - 3
django/http/response.py

@@ -251,13 +251,13 @@ class HttpResponseBase:
         signals.request_finished.send(sender=self._handler_class)
 
     def write(self, content):
-        raise IOError("This %s instance is not writable" % self.__class__.__name__)
+        raise OSError('This %s instance is not writable' % self.__class__.__name__)
 
     def flush(self):
         pass
 
     def tell(self):
-        raise IOError("This %s instance cannot tell its position" % self.__class__.__name__)
+        raise OSError('This %s instance cannot tell its position' % self.__class__.__name__)
 
     # These methods partially implement a stream-like object interface.
     # See https://docs.python.org/library/io.html#io.IOBase
@@ -272,7 +272,7 @@ class HttpResponseBase:
         return False
 
     def writelines(self, lines):
-        raise IOError("This %s instance is not writable" % self.__class__.__name__)
+        raise OSError('This %s instance is not writable' % self.__class__.__name__)
 
 
 class HttpResponse(HttpResponseBase):

+ 1 - 1
django/middleware/csrf.py

@@ -293,7 +293,7 @@ class CsrfViewMiddleware(MiddlewareMixin):
             if request.method == "POST":
                 try:
                     request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
-                except IOError:
+                except OSError:
                     # Handle a broken connection before we've completed reading
                     # the POST data. process_view shouldn't raise any
                     # exceptions, so we'll ignore and serve the user a 403

+ 1 - 1
django/utils/translation/trans_real.py

@@ -99,7 +99,7 @@ class DjangoTranslation(gettext_module.GNUTranslations):
         self._add_local_translations()
         if self.__language == settings.LANGUAGE_CODE and self.domain == 'django' and self._catalog is None:
             # default lang should have at least one translation file available.
-            raise IOError("No translation files found for default language %s." % settings.LANGUAGE_CODE)
+            raise OSError('No translation files found for default language %s.' % settings.LANGUAGE_CODE)
         self._add_fallback(localedirs)
         if self._catalog is None:
             # No catalogs found for this language, set an empty catalog.

+ 1 - 1
django/views/debug.py

@@ -357,7 +357,7 @@ class ExceptionReporter:
             try:
                 with open(filename, 'rb') as fp:
                     source = fp.read().splitlines()
-            except (OSError, IOError):
+            except OSError:
                 pass
         if source is None:
             return None, [], None, []

+ 1 - 1
docs/topics/files.txt

@@ -114,7 +114,7 @@ over a large number of objects. If files are not manually closed after
 accessing them, the risk of running out of file descriptors may arise. This
 may lead to the following error::
 
-    IOError: [Errno 24] Too many open files
+    OSError: [Errno 24] Too many open files
 
 
 File storage

+ 1 - 1
tests/admin_widgets/tests.py

@@ -867,7 +867,7 @@ class DateTimePickerSeleniumTests(AdminWidgetSeleniumTestCase):
         for language_code, language_name in settings.LANGUAGES:
             try:
                 catalog = gettext.translation('djangojs', path, [language_code])
-            except IOError:
+            except OSError:
                 continue
             if month_string in catalog._catalog:
                 month_name = catalog._catalog[month_string]

+ 2 - 2
tests/cache/tests.py

@@ -1448,8 +1448,8 @@ class FileBasedCacheTests(BaseCacheTests, TestCase):
         self.assertEqual(cache.get('foo', 'baz'), 'baz')
 
     def test_get_does_not_ignore_non_filenotfound_exceptions(self):
-        with mock.patch('builtins.open', side_effect=IOError):
-            with self.assertRaises(IOError):
+        with mock.patch('builtins.open', side_effect=OSError):
+            with self.assertRaises(OSError):
                 cache.get('foo')
 
     def test_empty_cache_file_considered_expired(self):

+ 4 - 4
tests/csrf_tests/tests.py

@@ -441,12 +441,12 @@ class CsrfViewMiddlewareTestMixin:
 
     def test_post_data_read_failure(self):
         """
-        #20128 -- IOErrors during POST data reading should be caught and
-        treated as if the POST data wasn't there.
+        OSErrors during POST data reading are caught and treated as if the
+        POST data wasn't there (#20128).
         """
         class CsrfPostRequest(HttpRequest):
             """
-            HttpRequest that can raise an IOError when accessing POST data
+            HttpRequest that can raise an OSError when accessing POST data
             """
             def __init__(self, token, raise_error):
                 super().__init__()
@@ -464,7 +464,7 @@ class CsrfViewMiddlewareTestMixin:
                 self.raise_error = raise_error
 
             def _load_post_and_files(self):
-                raise IOError('error reading input data')
+                raise OSError('error reading input data')
 
             def _get_post(self):
                 if self.raise_error:

+ 2 - 2
tests/file_storage/tests.py

@@ -482,9 +482,9 @@ class FileStorageTests(SimpleTestCase):
         f1 = ContentFile('chunks fails')
 
         def failing_chunks():
-            raise IOError
+            raise OSError
         f1.chunks = failing_chunks
-        with self.assertRaises(IOError):
+        with self.assertRaises(OSError):
             self.storage.save('error.file', f1)
 
     def test_delete_no_name(self):

+ 2 - 5
tests/file_uploads/tests.py

@@ -548,16 +548,13 @@ class DirectoryCreationTests(SimpleTestCase):
             self.obj.testfile.save('foo.txt', SimpleUploadedFile('foo.txt', b'x'), save=False)
 
     def test_not_a_directory(self):
-        """The correct IOError is raised when the upload directory name exists but isn't a directory"""
         # Create a file with the upload directory name
         open(UPLOAD_TO, 'wb').close()
         self.addCleanup(os.remove, UPLOAD_TO)
-        with self.assertRaises(IOError) as exc_info:
+        msg = '%s exists and is not a directory.' % UPLOAD_TO
+        with self.assertRaisesMessage(FileExistsError, msg):
             with SimpleUploadedFile('foo.txt', b'x') as file:
                 self.obj.testfile.save('foo.txt', file, save=False)
-        # The test needs to be done on a specific string as IOError
-        # is raised even without the patch (just not early enough)
-        self.assertEqual(exc_info.exception.args[0], "%s exists and is not a directory." % UPLOAD_TO)
 
 
 class MultiParserTests(SimpleTestCase):

+ 3 - 2
tests/files/tests.py

@@ -355,8 +355,9 @@ class FileMoveSafeTests(unittest.TestCase):
         handle_a, self.file_a = tempfile.mkstemp()
         handle_b, self.file_b = tempfile.mkstemp()
 
-        # file_move_safe should raise an IOError exception if destination file exists and allow_overwrite is False
-        with self.assertRaises(IOError):
+        # file_move_safe() raises OSError if the destination file exists and
+        # allow_overwrite is False.
+        with self.assertRaises(FileExistsError):
             file_move_safe(self.file_a, self.file_b, allow_overwrite=False)
 
         # should allow it and continue on if allow_overwrite is True

+ 2 - 5
tests/i18n/tests.py

@@ -1747,13 +1747,10 @@ class TranslationFilesMissing(SimpleTestCase):
         gettext_module.find = lambda *args, **kw: None
 
     def test_failure_finding_default_mo_files(self):
-        '''
-        Ensure IOError is raised if the default language is unparseable.
-        Refs: #18192
-        '''
+        """OSError is raised if the default language is unparseable."""
         self.patchGettextFind()
         trans_real._translations = {}
-        with self.assertRaises(IOError):
+        with self.assertRaises(OSError):
             activate('en')
 
 

+ 1 - 2
tests/mail/tests.py

@@ -4,7 +4,6 @@ import mimetypes
 import os
 import shutil
 import smtpd
-import socket
 import sys
 import tempfile
 import threading
@@ -1570,7 +1569,7 @@ class SMTPBackendStoppedServerTests(SMTPBackendTestsBase):
         """
         A socket connection error is silenced with fail_silently=True.
         """
-        with self.assertRaises(socket.error):
+        with self.assertRaises(ConnectionError):
             self.backend.open()
         self.backend.fail_silently = True
         self.backend.open()

+ 4 - 4
tests/requests/tests.py

@@ -479,11 +479,11 @@ class RequestsTests(SimpleTestCase):
     def test_POST_connection_error(self):
         """
         If wsgi.input.read() raises an exception while trying to read() the
-        POST, the exception should be identifiable (not a generic IOError).
+        POST, the exception is identifiable (not a generic OSError).
         """
         class ExplodingBytesIO(BytesIO):
             def read(self, len=0):
-                raise IOError("kaboom!")
+                raise OSError('kaboom!')
 
         payload = b'name=value'
         request = WSGIRequest({
@@ -520,11 +520,11 @@ class RequestsTests(SimpleTestCase):
     def test_FILES_connection_error(self):
         """
         If wsgi.input.read() raises an exception while trying to read() the
-        FILES, the exception should be identifiable (not a generic IOError).
+        FILES, the exception is identifiable (not a generic OSError).
         """
         class ExplodingBytesIO(BytesIO):
             def read(self, len=0):
-                raise IOError("kaboom!")
+                raise OSError('kaboom!')
 
         payload = b'x'
         request = WSGIRequest({

+ 3 - 3
tests/responses/tests.py

@@ -22,14 +22,14 @@ class HttpResponseBaseTests(SimpleTestCase):
         r = HttpResponseBase()
         self.assertIs(r.writable(), False)
 
-        with self.assertRaisesMessage(IOError, 'This HttpResponseBase instance is not writable'):
+        with self.assertRaisesMessage(OSError, 'This HttpResponseBase instance is not writable'):
             r.write('asdf')
-        with self.assertRaisesMessage(IOError, 'This HttpResponseBase instance is not writable'):
+        with self.assertRaisesMessage(OSError, 'This HttpResponseBase instance is not writable'):
             r.writelines(['asdf\n', 'qwer\n'])
 
     def test_tell(self):
         r = HttpResponseBase()
-        with self.assertRaisesMessage(IOError, 'This HttpResponseBase instance cannot tell its position'):
+        with self.assertRaisesMessage(OSError, 'This HttpResponseBase instance cannot tell its position'):
             r.tell()
 
     def test_setdefault(self):

+ 2 - 2
tests/servers/tests.py

@@ -194,10 +194,10 @@ class LiveServerPort(LiveServerBase):
         TestCase = type("TestCase", (LiveServerBase,), {})
         try:
             TestCase.setUpClass()
-        except socket.error as e:
+        except OSError as e:
             if e.errno == errno.EADDRINUSE:
                 # We're out of ports, LiveServerTestCase correctly fails with
-                # a socket error.
+                # an OSError.
                 return
             # Unexpected error.
             raise

+ 1 - 1
tests/sessions_tests/tests.py

@@ -532,7 +532,7 @@ class FileSessionTests(SessionTestsMixin, unittest.TestCase):
     def test_invalid_key_backslash(self):
         # Ensure we don't allow directory-traversal.
         # This is tested directly on _key_to_file, as load() will swallow
-        # a SuspiciousOperation in the same way as an IOError - by creating
+        # a SuspiciousOperation in the same way as an OSError - by creating
         # a new session, making it unclear whether the slashes were detected.
         with self.assertRaises(InvalidSessionKey):
             self.backend()._key_to_file("a\\b\\c")

+ 1 - 1
tests/staticfiles_tests/cases.py

@@ -23,7 +23,7 @@ class BaseStaticFilesMixin:
         )
 
     def assertFileNotFound(self, filepath):
-        with self.assertRaises(IOError):
+        with self.assertRaises(OSError):
             self._get_file(filepath)
 
     def render_template(self, template, **kwargs):

+ 2 - 2
tests/template_tests/test_loaders.py

@@ -191,11 +191,11 @@ class FileSystemLoaderTests(SimpleTestCase):
             tmppath = os.path.join(tmpdir, tmpfile.name)
             os.chmod(tmppath, 0o0222)
             with self.set_dirs([tmpdir]):
-                with self.assertRaisesMessage(IOError, 'Permission denied'):
+                with self.assertRaisesMessage(PermissionError, 'Permission denied'):
                     self.engine.get_template(tmpfile.name)
 
     def test_notafile_error(self):
-        with self.assertRaises(IOError):
+        with self.assertRaises(IsADirectoryError):
             self.engine.get_template('first')