From c5c9fe325782a790d563a0a8b1cf62a855a50d81 Mon Sep 17 00:00:00 2001 From: Helmut Grohne Date: Sat, 25 May 2024 10:22:21 +0200 Subject: add a FileDescriptor type It serves two main purposes. For one thing, it allows telling bare integers and file descriptors apart on a typing level similar to a NewType. For another it adds common methods to a file descriptor and enables closing it via a context manager. --- linuxnamespaces/atlocation.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'linuxnamespaces/atlocation.py') diff --git a/linuxnamespaces/atlocation.py b/linuxnamespaces/atlocation.py index 8da5982..8a38650 100644 --- a/linuxnamespaces/atlocation.py +++ b/linuxnamespaces/atlocation.py @@ -9,15 +9,16 @@ code for doing so. import enum import errno -import fcntl import os import os.path import pathlib import stat import typing +from .filedescriptor import FileDescriptor -AT_FDCWD = -100 + +AT_FDCWD = FileDescriptor(-100) PathConvertible = typing.Union[str, os.PathLike] @@ -51,7 +52,7 @@ class AtLocation: management. """ - fd: int + fd: FileDescriptor location: PathConvertible flags: AtFlags @@ -63,10 +64,10 @@ class AtLocation: ) -> "AtLocation": """The argument thing can be many different thing. If it is an AtLocation, it is copied and all other arguments must be unset. If it - is an integer, it is considered to be a file descriptor and the - location must be unset if flags contains AT_EMPTY_PATH. flags are used - as is except that AT_EMPTY_PATH is automatically added when given a - file descriptor and no location. + is an integer (e.g. FileDescriptor), it is considered to be a file + descriptor and the location must be unset if flags contains + AT_EMPTY_PATH. flags are used as is except that AT_EMPTY_PATH is + automatically added when given a file descriptor and no location. """ if isinstance(thing, AtLocation): if location is not None or flags != AtFlags.NONE: @@ -78,7 +79,10 @@ class AtLocation: if isinstance(thing, int): if thing < 0 and thing != AT_FDCWD: raise ValueError("fd cannot be negative") - obj.fd = thing + if isinstance(thing, FileDescriptor): + obj.fd = thing + else: + obj.fd = FileDescriptor(thing) if location is None: obj.location = "" obj.flags = flags | AtFlags.AT_EMPTY_PATH @@ -100,7 +104,7 @@ class AtLocation: def close(self) -> None: """Close the underlying file descriptor.""" if self.fd >= 0: - os.close(self.fd) + self.fd.close() self.fd = AT_FDCWD def as_emptypath(self, inheritable: bool = True) -> "AtLocation": @@ -109,11 +113,7 @@ class AtLocation: all cases, the caller is responsible for closing the result object. """ if self.flags & AtFlags.AT_EMPTY_PATH: - newfd = fcntl.fcntl( - self.fd, - fcntl.F_DUPFD if inheritable else fcntl.F_DUPFD_CLOEXEC, - 0, - ) + newfd = self.fd.dup(inheritable=inheritable) return AtLocation(newfd, flags=self.flags) return AtLocation( self.open(flags=os.O_PATH | (0 if inheritable else os.O_CLOEXEC)) @@ -175,7 +175,7 @@ class AtLocation: def __truediv__(self, name: "AtLocationLike") -> "AtLocation": return self.joinpath(name) - def fileno(self) -> int: + def fileno(self) -> FileDescriptor: """Return the underlying file descriptor if this is an AT_EMPTY_PATH location and raise a ValueError otherwise. """ @@ -186,7 +186,7 @@ class AtLocation: return self.fd @property - def fd_or_none(self) -> int | None: + def fd_or_none(self) -> FileDescriptor | None: """A variant of the fd attribute that replaces AT_FDCWD with None.""" return None if self.fd == AT_FDCWD else self.fd -- cgit v1.2.3