diff options
author | Helmut Grohne <helmut@subdivi.de> | 2025-05-21 21:24:28 +0200 |
---|---|---|
committer | Helmut Grohne <helmut@subdivi.de> | 2025-05-21 21:24:28 +0200 |
commit | 30a111639ce6a7ad54f77a091451bd5354afaae9 (patch) | |
tree | ed1de3ae8f3b9d5baf7384d7fdfd71a878529e30 /linuxnamespaces/filedescriptor.py | |
parent | 3d16d57bf63f18b46dd7c440fefa3e9fb8c112f0 (diff) | |
download | python-linuxnamespaces-30a111639ce6a7ad54f77a091451bd5354afaae9.tar.gz |
expand use of FileDescriptor and add FileDescriptorLike type alias
When accepting file descriptors, non-int objects with a fileno method
are now generally accepted. When returning a file descriptor, a
FileDescriptor instance is now returned.
Diffstat (limited to 'linuxnamespaces/filedescriptor.py')
-rw-r--r-- | linuxnamespaces/filedescriptor.py | 25 |
1 files changed, 22 insertions, 3 deletions
diff --git a/linuxnamespaces/filedescriptor.py b/linuxnamespaces/filedescriptor.py index 2cf8442..d11d4d6 100644 --- a/linuxnamespaces/filedescriptor.py +++ b/linuxnamespaces/filedescriptor.py @@ -8,11 +8,28 @@ import os import typing +@typing.runtime_checkable +class HasFileno(typing.Protocol): + def fileno(self) -> int: + ... + + +FileDescriptorLike = int | HasFileno + + class FileDescriptor(int): """Type tag for integers that represent file descriptors. It also provides a few very generic file descriptor methods. """ + def __new__(cls, value: FileDescriptorLike) -> typing.Self: + """Construct a FileDescriptor from an int or HasFileno.""" + if isinstance(value, cls): + return value # No need to copy, it's immutable. + if not isinstance(value, int): + value = value.fileno() + return super(FileDescriptor, cls).__new__(cls, value) + def __enter__(self) -> "FileDescriptor": """When used as a context manager, close the file descriptor on scope exit. @@ -37,16 +54,18 @@ class FileDescriptor(int): return FileDescriptor(os.dup(self)) return FileDescriptor(fcntl.fcntl(self, fcntl.F_DUPFD_CLOEXEC, 0)) - def dup2(self, fd2: int, inheritable: bool = True) -> "FileDescriptor": + def dup2( + self, fd2: FileDescriptorLike, inheritable: bool = True + ) -> "FileDescriptor": """Duplicate the file to the given file descriptor number.""" - return FileDescriptor(os.dup2(self, fd2, inheritable)) + return FileDescriptor(os.dup2(self, FileDescriptor(fd2), inheritable)) @classmethod def pidfd_open(cls, pid: int, flags: int = 0) -> typing.Self: """Convenience wrapper for os.pidfd_open.""" return cls(os.pidfd_open(pid, flags)) - def fileno(self) -> int: + def fileno(self) -> FileDescriptor: """Return self such that it satisfies the HasFileno protocol.""" return self |