The standard library implements support for Linux's extended attribute API with os.getxattr/setxattr/listxattr/removexattr. I'd like to implement support for more platforms under the same API in the standard library.
Proof of concept ================ I've implemented a native-Python library (using ctypes) that runs CI tests against Linux, Mac OS X and FreeBSD. If accepted, this library could also be used to backport support to Python 3.6+.
API discussion ============== It is at least possible to do this on all three platforms as demonstrated by my library.
Mutable mapping --------------- I've also implemented a mutable mapping (inspired by os.environ) that can be used for access to a file's extended attributes. Unlike os.environ you have to instantiate the class with your path, but once you have it, it's a straightforward API.
# class Xattrs(path: os.PathLike, follow_symlinks: bool = True)
xattrs = xattr_compat.Xattrs("./my_file")
xattrs["user.humanfund.xattr"] = b"hello\0world"
print("Extended attributes:", xattrs.items())
MacOS X ------- Not listed in the manpage but defined in the xattr.h header file are a few more bitflags: XATTR_NOSECURITY, XATTR_NODEFAULT and XATTR_SHOWCOMPRESSION. Linux's setxattr takes a flags argument so os.setxattr() has a flags argument, but on MacOS X all four functions take a flags argument. It may be worth adding a flags= kwarg to all four Python functions to support the MacOS X case.
MacOS X also has no restrictions on the name of the format key which is nice. The manpages demand that you use UTF-8 for the encoding as well.
Linux ----- Linux requires the attribute name to start with "user.", "system.", "trusted." or "security.". Luckily the current Python implementation doesn't attempt to validate this, leaving it up to the kernel to set errno when you don't do this. Because Linux was the most restrictive of these platforms existing code will work correctly on the new platforms.
FreeBSD/NetBSD -------------- These systems added an argument attrnamespace to all of their xattr system calls. There are only two namespaces: EXTATTR_NAMESPACE_SYSTEM and EXTATTR_NAMESPACE_USER. There are no further restrictions on the attribute name. Unfortunately, directly supporting attrnamespace in the Python API is going to require breaking away a bit from the Linux interface.
You can't rely on checking the attribute string to determine which namespace a key should go into because there will be keys without those prefixes on live systems. You also can't trim the string in Python before handing it off to the kernel because it is valid to have a key that starts with "user." and so on in the BSDs and I imagine there are probably a lot of users who want compatibility with Linux and are doing exactly that.
I have my library using the USER namespace by default which seems to be the correct thing to do. However, it is still possible that someone might want to read or write from a SYSTEM namespace xattr. I've extended the function signature to optionally take a tuple in place of the os.PathLike argument. The first member of the tuple is the namespace constant, the second is the actual path or file descriptor that you want to work on. I'm pretty happy with this API - it doesn't break compatibility for the tiny user base of FreeBSD users who are interested in reading SYSTEM-namespaced xattrs from Python but also doesn't clutter things up too much. But maybe this is too tacky for the standard library.
Windows ------- If the Windows Subsystem for Linux implements xattr this code can likely mimic what it does. I don't have a Windows machine so I can't comment on if that is already there.
Bikeshedding ============ Should the mapping class be named os.xattrs or os.xattr or os.Xattrs or os.Xattr?
Implementation ============== If accepted, what sort of tests would be expected as part of the PR? I already have a handful of unit tests in the xattr_compat repo.