summaryrefslogtreecommitdiff
path: root/linuxnamespaces/filedescriptor.py
diff options
context:
space:
mode:
Diffstat (limited to 'linuxnamespaces/filedescriptor.py')
-rw-r--r--linuxnamespaces/filedescriptor.py35
1 files changed, 32 insertions, 3 deletions
diff --git a/linuxnamespaces/filedescriptor.py b/linuxnamespaces/filedescriptor.py
index e4eff9b..ee96a94 100644
--- a/linuxnamespaces/filedescriptor.py
+++ b/linuxnamespaces/filedescriptor.py
@@ -8,11 +8,33 @@ import os
import typing
+# pylint: disable=too-few-public-methods # It's that one method we describe.
+@typing.runtime_checkable
+class HasFileno(typing.Protocol):
+ """A typing protocol representing a file-like object and looking up the
+ underlying file descriptor.
+ """
+
+ def fileno(self) -> int:
+ """Return the underlying file descriptor."""
+
+
+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,11 +59,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))
- def fileno(self) -> int:
+ @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) -> "FileDescriptor":
"""Return self such that it satisfies the HasFileno protocol."""
return self