diff --git a/fs/error_tools.py b/fs/error_tools.py index bdb3818c..e792adae 100644 --- a/fs/error_tools.py +++ b/fs/error_tools.py @@ -84,7 +84,7 @@ def __exit__( _errno = exc_value.errno fserror = os_errors.get(_errno, errors.OperationFailed) if _errno == errno.EACCES and sys.platform == "win32": - if getattr(exc_value, "args", None) == 32: # pragma: no cover + if getattr(exc_value, "winerror", None) == 32: # pragma: no cover fserror = errors.ResourceLocked reraise(fserror, fserror(self._path, exc=exc_value), traceback) diff --git a/tests/test_error_tools.py b/tests/test_error_tools.py index 4f6aa324..379a93cb 100644 --- a/tests/test_error_tools.py +++ b/tests/test_error_tools.py @@ -2,8 +2,10 @@ import errno import unittest +from unittest import mock import fs.errors +from fs import error_tools from fs.error_tools import convert_os_errors @@ -23,3 +25,16 @@ def test_convert_enametoolong(self): raise exception self.assertEqual(ctx.exception.exc, exception) self.assertEqual(ctx.exception.path, "/tmp/test") + + def test_convert_eacces_sharing_violation_on_windows(self): + # On Windows, EACCES with winerror 32 (ERROR_SHARING_VIOLATION) + # means the file is locked by another process, not a plain + # permissions problem. + exception = OSError(errno.EACCES, "file in use") + exception.winerror = 32 + with mock.patch.object(error_tools.sys, "platform", "win32"): + with self.assertRaises(fs.errors.ResourceLocked) as ctx: + with convert_os_errors("open", "/tmp/test"): + raise exception + self.assertEqual(ctx.exception.exc, exception) + self.assertEqual(ctx.exception.path, "/tmp/test")