diff options
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 |