Coverage for python / lsst / resources / _resourceHandles / _baseResourceHandle.py: 70%
83 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-26 08:44 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-26 08:44 +0000
1# This file is part of lsst-resources.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# Use of this source code is governed by a 3-clause BSD-style
10# license that can be found in the LICENSE file.
11from __future__ import annotations
13from types import TracebackType
15__all__ = ("BaseResourceHandle", "CloseStatus", "ResourceHandleProtocol")
17import logging
18from abc import ABC, abstractmethod, abstractproperty
19from collections.abc import Callable, Iterable
20from enum import Enum, auto
21from io import SEEK_SET
22from typing import TYPE_CHECKING, AnyStr, Generic, Protocol, TypeVar
24if TYPE_CHECKING:
25 # Circular import.
26 from .._resourcePath import ResourcePath
28S = TypeVar("S", bound="ResourceHandleProtocol")
29T = TypeVar("T", bound="BaseResourceHandle")
30U = TypeVar("U", str, bytes)
33class CloseStatus(Enum):
34 """Enumerated closed/open status of a file handle, implementation detail
35 that may be used by BaseResourceHandle children.
36 """
38 OPEN = auto()
39 CLOSING = auto()
40 CLOSED = auto()
43class ResourceHandleProtocol(Protocol, Generic[U]):
44 """Defines the interface protocol that is compatible with children of
45 `.BaseResourceHandle`.
47 Any class that satisfies this protocol can be used in any context where a
48 `.BaseResourceHandle` is expected.
49 """
51 @abstractproperty
52 def mode(self) -> str: ... 52 ↛ exitline 52 didn't return from function 'mode' because
54 @abstractproperty
55 def name(self) -> str: ... 55 ↛ exitline 55 didn't return from function 'name' because
57 @abstractmethod
58 def close(self) -> None: ... 58 ↛ exitline 58 didn't return from function 'close' because
60 @abstractproperty
61 def closed(self) -> bool: ... 61 ↛ exitline 61 didn't return from function 'closed' because
63 @abstractmethod
64 def fileno(self) -> int: ... 64 ↛ exitline 64 didn't return from function 'fileno' because
66 @abstractmethod
67 def flush(self) -> None: ... 67 ↛ exitline 67 didn't return from function 'flush' because
69 @abstractproperty
70 def isatty(self) -> bool | Callable[[], bool]: ... 70 ↛ exitline 70 didn't return from function 'isatty' because
72 @abstractmethod
73 def readable(self) -> bool: ... 73 ↛ exitline 73 didn't return from function 'readable' because
75 @abstractmethod
76 def readline(self, size: int = -1) -> U: ... 76 ↛ exitline 76 didn't return from function 'readline' because
78 @abstractmethod
79 def readlines(self, hint: int = -1) -> Iterable[U]: ... 79 ↛ exitline 79 didn't return from function 'readlines' because
81 @abstractmethod
82 def seek(self, offset: int, whence: int = SEEK_SET, /) -> int:
83 pass
85 @abstractmethod
86 def seekable(self) -> bool: ... 86 ↛ exitline 86 didn't return from function 'seekable' because
88 @abstractmethod
89 def tell(self) -> int: ... 89 ↛ exitline 89 didn't return from function 'tell' because
91 @abstractmethod
92 def truncate(self, size: int | None = None) -> int: ... 92 ↛ exitline 92 didn't return from function 'truncate' because
94 @abstractmethod
95 def writable(self) -> bool: ... 95 ↛ exitline 95 didn't return from function 'writable' because
97 @abstractmethod
98 def writelines(self, lines: Iterable[U], /) -> None: ... 98 ↛ exitline 98 didn't return from function 'writelines' because
100 @abstractmethod
101 def read(self, size: int = -1) -> U: ... 101 ↛ exitline 101 didn't return from function 'read' because
103 @abstractmethod
104 def write(self, b: U, /) -> int: ... 104 ↛ exitline 104 didn't return from function 'write' because
106 def __enter__(self: S) -> S: ... 106 ↛ exitline 106 didn't return from function '__enter__' because
108 def __exit__( 108 ↛ exitline 108 didn't return from function '__exit__' because
109 self,
110 exc_type: type[BaseException] | None,
111 exc_val: BaseException | None,
112 exc_tb: TracebackType | None,
113 /,
114 ) -> bool | None: ...
117class BaseResourceHandle(ABC, ResourceHandleProtocol[U]):
118 """Base class interface for the handle like interface of
119 `~lsst.resources.ResourcePath` subclasses.
121 Parameters
122 ----------
123 mode : `str`
124 Handle modes as described in the python `io` module.
125 log : `~logging.Logger`
126 Logger to used when writing messages.
127 uri : `lsst.resources.ResourcePath`
128 The URI of the resource being opened.
129 newline : `str`
130 When doing multiline operations, break the stream on given character
131 Defaults to newline.
133 Notes
134 -----
135 Documentation on the methods of this class line should refer to the
136 corresponding methods in the `io` module.
137 """
139 _closed: CloseStatus
140 _mode: str
141 _log: logging.Logger
142 _newline: U
143 _uri: ResourcePath
145 def __init__(
146 self, mode: str, log: logging.Logger, uri: ResourcePath, *, newline: AnyStr | None = None
147 ) -> None:
148 if newline is None:
149 if "b" in mode:
150 self._newline = b"\n" # type: ignore
151 else:
152 self._newline = "\n" # type: ignore
153 else:
154 self._newline = newline # type: ignore
155 self._mode = mode
156 self._log = log
157 self._uri = uri
159 @property
160 def name(self) -> str:
161 # Use name for compatibility with TextIOWrapper.
162 return str(self._uri)
164 @property
165 def mode(self) -> str:
166 return self._mode
168 def __enter__(self: T) -> T:
169 self._closed = CloseStatus.OPEN
170 return self
172 def __exit__(
173 self,
174 exc_type: type[BaseException] | None,
175 exc_bal: BaseException | None,
176 exc_tb: TracebackType | None,
177 ) -> bool | None:
178 self.close()
179 return None