|
17 | 17 | import sys |
18 | 18 | import warnings |
19 | 19 | from pathlib import Path, PosixPath, PurePath, WindowsPath |
20 | | -from typing import Any |
| 20 | +from typing import Any, overload |
21 | 21 |
|
22 | 22 | from sphinx.deprecation import RemovedInSphinx90Warning |
23 | 23 |
|
@@ -133,3 +133,38 @@ def __getitem__(self, item: int | slice) -> str: |
133 | 133 | def __len__(self) -> int: |
134 | 134 | warnings.warn(_MSG, RemovedInSphinx90Warning, stacklevel=2) |
135 | 135 | return len(self.__str__()) |
| 136 | + |
| 137 | + |
| 138 | +class _StrPathProperty: |
| 139 | + def __init__(self) -> None: |
| 140 | + self.instance_attr: str = '' |
| 141 | + |
| 142 | + def __set_name__(self, owner: object, name: str) -> None: |
| 143 | + self.instance_attr = f'_{name}' # i.e. '_srcdir' |
| 144 | + |
| 145 | + @overload |
| 146 | + def __get__(self, obj: None, objtype: None) -> _StrPathProperty: ... # NoQA: E704 |
| 147 | + |
| 148 | + @overload |
| 149 | + def __get__(self, obj: object, objtype: type[object]) -> _StrPath: ... # NoQA: E704 |
| 150 | + |
| 151 | + def __get__( |
| 152 | + self, obj: object | None, objtype: type[object] | None = None |
| 153 | + ) -> _StrPathProperty | _StrPath: |
| 154 | + if obj is None: |
| 155 | + return self |
| 156 | + if not self.instance_attr: |
| 157 | + raise AttributeError |
| 158 | + return getattr(obj, self.instance_attr) |
| 159 | + |
| 160 | + def __set__(self, obj: Any, value: _StrPath | Path) -> None: |
| 161 | + try: |
| 162 | + setattr(obj, self.instance_attr, _StrPath(value)) |
| 163 | + except TypeError as err: |
| 164 | + cls_name = type(obj).__qualname__ |
| 165 | + name = self.instance_attr.removeprefix('_') |
| 166 | + msg = f'{cls_name}.{name} may only be set to path-like objects' |
| 167 | + raise TypeError(msg) from err |
| 168 | + |
| 169 | + def __delete__(self, obj: Any) -> None: |
| 170 | + delattr(obj, self.instance_attr) |
0 commit comments