From 30a111639ce6a7ad54f77a091451bd5354afaae9 Mon Sep 17 00:00:00 2001 From: Helmut Grohne Date: Wed, 21 May 2025 21:24:28 +0200 Subject: 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. --- linuxnamespaces/filedescriptor.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) (limited to 'linuxnamespaces/filedescriptor.py') 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 -- cgit v1.2.3