From 3d611c6f3b88acef5fa6595c81d93ba65fe7700f Mon Sep 17 00:00:00 2001 From: mrbean-bremen Date: Wed, 13 Mar 2024 20:14:36 +0100 Subject: [PATCH] Fix handling of missing attribute in os.getxattr - no tests for real OS (extended atributes not enabled in CI) - fixes #971 --- CHANGES.md | 1 + README.md | 2 +- docs/usage.rst | 6 +++++- pyfakefs/fake_os.py | 2 ++ pyfakefs/tests/fake_os_test.py | 37 +++++++++++++++++----------------- 5 files changed, 28 insertions(+), 20 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 376b1ada..f0366160 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -22,6 +22,7 @@ The released versions correspond to PyPI releases. (see [#965](../../issues/965)) * fixed handling of `dirfd` in `os.symlink` (see [#968](../../issues/968)) * add missing `follow_symlink` argument to `os.link` (see [#973](../../issues/973)) +* fixed handling of missing attribute in `os.getxattr` (see [#971](../../issues/971)) ### Enhancements * added support for `O_NOFOLLOW` and `O_DIRECTORY` flags in `os.open` diff --git a/README.md b/README.md index b55c63d9..ad3f9973 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ for more information. pyfakefs.py was initially developed at Google by Mike Bland as a modest fake implementation of core Python modules. It was introduced to all of Google in September 2006. Since then, it has been enhanced to extend its -functionality and usefulness. At last count, pyfakefs was used in over 2,000 +functionality and usefulness. At last count, pyfakefs was used in over 20,000 Python tests at Google. Google released pyfakefs to the public in 2011 as Google Code project diff --git a/docs/usage.rst b/docs/usage.rst index 0773c026..e7ba4466 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -690,7 +690,7 @@ While ``pyfakefs`` can be used just with the standard Python file system functions, there are a few convenience methods in ``fake_filesystem`` that can help you setting up your tests. The methods can be accessed via the ``fake_filesystem`` instance in your tests: ``Patcher.fs``, the ``fs`` -fixture in pytest, ``TestCase.fs`` for ``unittest``, and the ``fs`` argument +fixture in pytest, ``TestCase.fs`` for ``unittest``, and the positional argument for the ``patchfs`` decorator. File creation helpers @@ -965,6 +965,10 @@ The following test works both under Windows and Linux: assert os.path.splitdrive(r"C:\foo\bar") == ("C:", r"\foo\bar") assert os.path.ismount("C:") +.. note:: Only behavior not relying on OS-specific functionality is emulated on another system. + For example, if you use the Linux-specific functionality of extended attributes (``os.getxattr`` etc.) + in your code, you have to test this under Linux. + Set file as inaccessible under Windows ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Normally, if you try to set a file or directory as inaccessible using ``chmod`` under diff --git a/pyfakefs/fake_os.py b/pyfakefs/fake_os.py index d2f3f248..68741a78 100644 --- a/pyfakefs/fake_os.py +++ b/pyfakefs/fake_os.py @@ -491,6 +491,8 @@ def getxattr( if isinstance(attribute, bytes): attribute = attribute.decode(sys.getfilesystemencoding()) file_obj = self.filesystem.resolve(path, follow_symlinks, allow_fd=True) + if attribute not in file_obj.xattr: + raise OSError(errno.ENODATA, "No data available", path) return file_obj.xattr.get(attribute) def listxattr( diff --git a/pyfakefs/tests/fake_os_test.py b/pyfakefs/tests/fake_os_test.py index 0240b3fa..a351433b 100644 --- a/pyfakefs/tests/fake_os_test.py +++ b/pyfakefs/tests/fake_os_test.py @@ -5496,26 +5496,25 @@ def test_empty_xattr(self): self.assertEqual([], self.os.listxattr(self.dir_path)) self.assertEqual([], self.os.listxattr(self.file_path)) + def test_getxattr_raises_for_non_existing_file(self): + with self.assertRaises(FileNotFoundError): + self.os.getxattr("bogus_path", "test") + + def test_getxattr_raises_for_non_existing_attribute(self): + with self.assertRaises(OSError) as cm: + self.os.getxattr(self.file_path, "bogus") + self.assertEqual(errno.ENODATA, cm.exception.errno) + def test_setxattr(self): - self.assertRaises(TypeError, self.os.setxattr, self.file_path, "test", "value") - self.assert_raises_os_error( - errno.EEXIST, - self.os.setxattr, - self.file_path, - "test", - b"value", - self.os.XATTR_REPLACE, - ) + with self.assertRaises(TypeError): + self.os.setxattr(self.file_path, "test", "value") + with self.assertRaises(FileExistsError): + self.os.setxattr(self.file_path, "test", b"value", self.os.XATTR_REPLACE) self.os.setxattr(self.file_path, "test", b"value") self.assertEqual(b"value", self.os.getxattr(self.file_path, "test")) - self.assert_raises_os_error( - errno.ENODATA, - self.os.setxattr, - self.file_path, - "test", - b"value", - self.os.XATTR_CREATE, - ) + with self.assertRaises(OSError) as cm: + self.os.setxattr(self.file_path, "test", b"value", self.os.XATTR_CREATE) + self.assertEqual(errno.ENODATA, cm.exception.errno) def test_removeattr(self): self.os.removexattr(self.file_path, "test") @@ -5525,7 +5524,9 @@ def test_removeattr(self): self.assertEqual(b"value", self.os.getxattr(self.file_path, "test")) self.os.removexattr(self.file_path, "test") self.assertEqual([], self.os.listxattr(self.file_path)) - self.assertIsNone(self.os.getxattr(self.file_path, "test")) + with self.assertRaises(OSError) as cm: + self.os.getxattr(self.file_path, "test") + self.assertEqual(errno.ENODATA, cm.exception.errno) def test_default_path(self): self.os.chdir(self.dir_path)