Coverage for src / c41811 / config / basic / core.py: 100%

357 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-02-06 06:04 +0000

1# cython: language_level = 3 # noqa: ERA001 

2 

3 

4""" 

5主要中间层 

6 

7.. versionadded:: 0.2.0 

8""" 

9 

10from abc import ABC 

11from collections import OrderedDict 

12from collections.abc import Callable 

13from collections.abc import Iterable 

14from collections.abc import Iterator 

15from collections.abc import Mapping 

16from collections.abc import Sequence 

17from contextlib import suppress 

18from copy import deepcopy 

19from re import Pattern 

20from typing import Any 

21from typing import Literal 

22from typing import Self 

23from typing import cast 

24from typing import overload 

25from typing import override 

26 

27from .factory import ConfigDataFactory 

28from .utils import check_read_only 

29from .utils import fmt_path 

30from .._protocols import Indexed 

31from ..abc import ABCConfigData 

32from ..abc import ABCConfigFile 

33from ..abc import ABCConfigPool 

34from ..abc import ABCIndexedConfigData 

35from ..abc import ABCPath 

36from ..abc import ABCProcessorHelper 

37from ..abc import ABCSLProcessorPool 

38from ..abc import AnyKey 

39from ..abc import PathLike 

40from ..errors import ConfigDataReadOnlyError 

41from ..errors import ConfigDataTypeError 

42from ..errors import ConfigOperate 

43from ..errors import FailedProcessConfigFileError 

44from ..errors import KeyInfo 

45from ..errors import RequiredPathNotFoundError 

46from ..errors import UnsupportedConfigFormatError 

47 

48 

49class BasicConfigData[D](ABCConfigData, ABC): 

50 # noinspection GrazieInspection 

51 """ 

52 配置数据基类 

53 

54 .. versionadded:: 0.1.5 

55 

56 .. versionchanged:: 0.2.0 

57 重命名 ``BaseConfigData`` 为 ``BasicConfigData`` 

58 """ 

59 

60 _read_only: bool | None = False 

61 

62 @property 

63 @override 

64 def data_read_only(self) -> bool | None: 

65 return True # 全被子类复写了 测不到 # pragma: no cover 

66 

67 @property # type: ignore[explicit-override] # mypy抽风 

68 @override 

69 def read_only(self) -> bool | None: 

70 return super().read_only or self._read_only 

71 

72 @read_only.setter 

73 @override 

74 def read_only(self, value: Any) -> None: 

75 if self.data_read_only: 

76 raise ConfigDataReadOnlyError 

77 self._read_only = bool(value) 

78 

79 

80class BasicSingleConfigData[D](BasicConfigData[D], ABC): 

81 """ 

82 单文件配置数据基类 

83 

84 .. versionadded:: 0.2.0 

85 """ 

86 

87 def __init__(self, data: D): 

88 """ 

89 :param data: 配置的原始数据 

90 :type data: Any 

91 """ # noqa: D205 

92 self._data: D = deepcopy(data) 

93 

94 @property 

95 def data(self) -> D: 

96 """配置的原始数据*快照*""" 

97 return deepcopy(self._data) 

98 

99 @override 

100 def __eq__(self, other: Any) -> bool: 

101 if not isinstance(other, type(self)): 

102 return NotImplemented 

103 return self._data == other._data 

104 

105 __hash__ = None # type: ignore[assignment] 

106 

107 def __bool__(self) -> bool: 

108 return bool(self._data) 

109 

110 @override 

111 def __str__(self) -> str: 

112 return str(self._data) 

113 

114 @override 

115 def __repr__(self) -> str: 

116 return f"{self.__class__.__name__}({self._data!r})" 

117 

118 def __deepcopy__(self, memo: dict[str, Any]) -> Self: 

119 return self.from_data(self._data) 

120 

121 

122class BasicIndexedConfigData[D: Indexed[Any, Any]](BasicSingleConfigData[D], ABCIndexedConfigData[D], ABC): 

123 # noinspection GrazieInspection 

124 """ 

125 支持 ``索引`` 操作的配置数据基类 

126 

127 .. versionadded:: 0.1.5 

128 

129 .. versionchanged:: 0.2.0 

130 重命名 ``BaseSupportsIndexConfigData`` 为 ``BasicIndexedConfigData`` 

131 """ 

132 

133 def _process_path[X, Y]( 

134 self, 

135 path: ABCPath[Any], 

136 path_checker: Callable[[Any, AnyKey, ABCPath[Any], int], X], 

137 process_return: Callable[[Any], Y], 

138 ) -> X | Y: 

139 # noinspection GrazieInspection 

140 """ 

141 处理键路径的通用函数 

142 

143 :param path: 键路径 

144 :type path: ABCPath 

145 :param path_checker: 检查并处理每个路径段,返回值非None时结束操作并返回值 

146 :type path_checker: 

147 Callable[(current_data: Any, current_key: ABCKey, last_path: ABCPath, path_index: int), X] 

148 :param process_return: 处理最终结果,该函数返回值会被直接返回 

149 :type process_return: Callable[(current_data: Any), Y] 

150 

151 :return: 处理结果 

152 :rtype: X | Y 

153 

154 .. versionchanged:: 0.2.0 

155 重命名参数 ``process_check`` 为 ``path_checker`` 

156 """ # noqa: RUF002 

157 current_data = self._data 

158 

159 for key_index, current_key in enumerate(path): 

160 last_path: ABCPath[Any] = path[key_index + 1 :] 

161 

162 check_result = path_checker(current_data, current_key, last_path, key_index) 

163 if check_result is not None: 

164 return check_result 

165 

166 current_data = current_key.__get_inner_element__(current_data) 

167 

168 return process_return(current_data) 

169 

170 @override 

171 def retrieve(self, path: PathLike, *, return_raw_value: bool = False) -> Any: 

172 path = fmt_path(path) 

173 

174 def checker(current_data: Any, current_key: AnyKey, _last_path: ABCPath[Any], key_index: int) -> None: 

175 missing_protocol = current_key.__supports__(current_data) 

176 if missing_protocol: 

177 raise ConfigDataTypeError( 

178 KeyInfo(cast(ABCPath[Any], path), current_key, key_index), missing_protocol, type(current_data) 

179 ) 

180 if not current_key.__contains_inner_element__(current_data): 

181 raise RequiredPathNotFoundError( 

182 KeyInfo(cast(ABCPath[Any], path), current_key, key_index), ConfigOperate.Read 

183 ) 

184 

185 def process_return[V: Any](current_data: V) -> V | ABCConfigData: 

186 if return_raw_value: 

187 return deepcopy(current_data) 

188 

189 is_sequence = isinstance(current_data, Sequence) and not isinstance(current_data, str | bytes) 

190 if isinstance(current_data, Mapping) or is_sequence: 

191 return ConfigDataFactory(current_data) # type: ignore[return-value] 

192 

193 return deepcopy(current_data) 

194 

195 return self._process_path(path, checker, process_return) 

196 

197 @override 

198 @check_read_only 

199 def modify(self, path: PathLike, value: Any, *, allow_create: bool = True) -> Self: 

200 path = fmt_path(path) 

201 

202 def checker(current_data: Any, current_key: AnyKey, last_path: ABCPath[Any], key_index: int) -> None: 

203 missing_protocol = current_key.__supports_modify__(current_data) 

204 if missing_protocol: 

205 raise ConfigDataTypeError( 

206 KeyInfo(cast(ABCPath[Any], path), current_key, key_index), missing_protocol, type(current_data) 

207 ) 

208 if not current_key.__contains_inner_element__(current_data): 

209 if not allow_create: 

210 raise RequiredPathNotFoundError( 

211 KeyInfo(cast(ABCPath[Any], path), current_key, key_index), ConfigOperate.Write 

212 ) 

213 current_key.__set_inner_element__(current_data, type(self._data)()) 

214 

215 if not last_path: 

216 current_key.__set_inner_element__(current_data, value) 

217 

218 self._process_path(path, checker, lambda *_: None) 

219 return self 

220 

221 @override 

222 @check_read_only 

223 def delete(self, path: PathLike) -> Self: 

224 path = fmt_path(path) 

225 

226 def checker( 

227 current_data: Any, 

228 current_key: AnyKey, 

229 last_path: ABCPath[Any], 

230 key_index: int, 

231 ) -> Literal[True] | None: 

232 missing_protocol = current_key.__supports_modify__(current_data) 

233 if missing_protocol: 

234 raise ConfigDataTypeError( 

235 KeyInfo(cast(ABCPath[Any], path), current_key, key_index), missing_protocol, type(current_data) 

236 ) 

237 if not current_key.__contains_inner_element__(current_data): 

238 raise RequiredPathNotFoundError( 

239 KeyInfo(cast(ABCPath[Any], path), current_key, key_index), ConfigOperate.Delete 

240 ) 

241 

242 if not last_path: 

243 current_key.__delete_inner_element__(current_data) 

244 return True 

245 return None # 被mypy强制要求 

246 

247 self._process_path(path, checker, lambda *_: None) 

248 return self 

249 

250 @override 

251 def unset(self, path: PathLike) -> Self: 

252 with suppress(RequiredPathNotFoundError): 

253 self.delete(path) 

254 return self 

255 

256 @override 

257 def exists(self, path: PathLike, *, ignore_wrong_type: bool = False) -> bool: 

258 path = fmt_path(path) 

259 

260 def checker(current_data: Any, current_key: AnyKey, _last_path: ABCPath[Any], key_index: int) -> bool | None: 

261 missing_protocol = current_key.__supports__(current_data) 

262 if missing_protocol: 

263 if ignore_wrong_type: 

264 return False 

265 raise ConfigDataTypeError( 

266 KeyInfo(cast(ABCPath[Any], path), current_key, key_index), missing_protocol, type(current_data) 

267 ) 

268 if not current_key.__contains_inner_element__(current_data): 

269 return False 

270 return None 

271 

272 return cast(bool, self._process_path(path, checker, lambda *_: True)) 

273 

274 @override 

275 def get[V](self, path: PathLike, default: V | None = None, *, return_raw_value: bool = False) -> V | Any: 

276 try: 

277 return self.retrieve(path, return_raw_value=return_raw_value) 

278 except RequiredPathNotFoundError: 

279 return default 

280 

281 @override 

282 def setdefault[V](self, path: PathLike, default: V | None = None, *, return_raw_value: bool = False) -> V | Any: 

283 try: 

284 return self.retrieve(path) 

285 except RequiredPathNotFoundError: 

286 self.modify(path, default) 

287 return default 

288 

289 @override 

290 def __contains__(self, key: Any) -> bool: 

291 return key in self._data # type: ignore[operator] 

292 

293 @override 

294 def __iter__(self) -> Iterator[D]: 

295 return iter(self._data) 

296 

297 @override 

298 def __len__(self) -> int: 

299 return len(self._data) # type: ignore[arg-type] 

300 

301 @override 

302 def __getitem__(self, index: Any) -> Any: 

303 data = self._data[index] 

304 is_sequence = isinstance(data, Sequence) and not isinstance(data, str | bytes) 

305 if isinstance(data, Mapping) or is_sequence: 

306 return cast(Self, ConfigDataFactory(data)) 

307 return cast(D, deepcopy(data)) 

308 

309 @override 

310 def __setitem__(self, index: Any, value: Any) -> None: 

311 self._data[index] = value # type: ignore[index] 

312 

313 @override 

314 def __delitem__(self, index: Any) -> None: 

315 del self._data[index] # type: ignore[attr-defined] 

316 

317 

318class ConfigFile[D: ABCConfigData](ABCConfigFile[D]): 

319 """配置文件类""" 

320 

321 def __init__(self, initial_config: D | Any, *, config_format: str | None = None): 

322 """ 

323 :param initial_config: 配置数据 

324 :type initial_config: D 

325 :param config_format: 配置文件的格式 

326 :type config_format: str | None 

327 

328 .. caution:: 

329 本身并未对 ``initial_config`` 参数进行深拷贝,但是 :py:class:`ConfigDataFactory` 分发的类可能会将其深拷贝 

330 

331 .. versionchanged:: 0.2.0 

332 现在会自动尝试使用 :py:class:`ConfigDataFactory` 转换 ``initial_config`` 参数 

333 

334 重命名参数 ``config_data`` 为 ``initial_config`` 

335 """ # noqa: RUF002, D205 

336 super().__init__(cast(D, ConfigDataFactory(initial_config)), config_format=config_format) 

337 

338 @override 

339 def save( 

340 self, 

341 processor_pool: ABCSLProcessorPool, 

342 namespace: str, 

343 file_name: str, 

344 config_format: str | None = None, 

345 *processor_args: Any, 

346 **processor_kwargs: Any, 

347 ) -> None: 

348 if config_format is None: 

349 config_format = self._config_format 

350 

351 if config_format not in processor_pool.SLProcessors: 

352 raise UnsupportedConfigFormatError(config_format) 

353 

354 return processor_pool.SLProcessors[config_format].save( 

355 processor_pool, self, processor_pool.root_path, namespace, file_name, *processor_args, **processor_kwargs 

356 ) 

357 

358 @classmethod 

359 @override 

360 def load( 

361 cls, 

362 processor_pool: ABCSLProcessorPool, 

363 namespace: str, 

364 file_name: str, 

365 config_format: str, 

366 *processor_args: Any, 

367 **processor_kwargs: Any, 

368 ) -> Self: 

369 if config_format not in processor_pool.SLProcessors: 

370 raise UnsupportedConfigFormatError(config_format) 

371 

372 return cast( 

373 Self, 

374 processor_pool.SLProcessors[config_format].load( 

375 processor_pool, processor_pool.root_path, namespace, file_name, *processor_args, **processor_kwargs 

376 ), 

377 ) 

378 

379 @classmethod 

380 @override 

381 def initialize( 

382 cls, 

383 processor_pool: ABCSLProcessorPool, 

384 namespace: str, 

385 file_name: str, 

386 config_format: str, 

387 *processor_args: Any, 

388 **processor_kwargs: Any, 

389 ) -> Self: 

390 if config_format not in processor_pool.SLProcessors: 

391 raise UnsupportedConfigFormatError(config_format) 

392 

393 return cast( 

394 Self, 

395 processor_pool.SLProcessors[config_format].initialize( 

396 processor_pool, processor_pool.root_path, namespace, file_name, *processor_args, **processor_kwargs 

397 ), 

398 ) 

399 

400 

401class PHelper(ABCProcessorHelper): 

402 """处理器助手类""" 

403 

404 

405class BasicConfigPool(ABCConfigPool, ABC): 

406 """ 

407 基础配置池类 

408 

409 实现了一些通用方法 

410 

411 .. versionchanged:: 0.2.0 

412 重命名 ``BaseConfigPool`` 为 ``BasicConfigPool`` 

413 """ 

414 

415 def __init__(self, root_path: str = "./.config"): 

416 """ 

417 :param root_path: 配置根路径 

418 :type root_path: str 

419 """ # noqa: D205 

420 super().__init__(root_path) 

421 self._configs: dict[str, dict[str, ABCConfigFile[Any]]] = {} 

422 self._helper = PHelper() 

423 

424 @property 

425 @override 

426 def helper(self) -> ABCProcessorHelper: 

427 return self._helper 

428 

429 # noinspection PyMethodOverriding 

430 @overload # 咱也不知道为什么mypy只有这样检查会通过而pycharm会报错 

431 def get(self, namespace: str) -> dict[str, ABCConfigFile[Any]] | None: ... 

432 

433 # noinspection PyMethodOverriding 

434 @overload 

435 def get(self, namespace: str, file_name: str) -> ABCConfigFile[Any] | None: ... 

436 

437 @overload 

438 def get( 

439 self, 

440 namespace: str, 

441 file_name: str | None = None, 

442 ) -> dict[str, ABCConfigFile[Any]] | ABCConfigFile[Any] | None: ... 

443 

444 @override 

445 def get( 

446 self, 

447 namespace: str, 

448 file_name: str | None = None, 

449 ) -> dict[str, ABCConfigFile[Any]] | ABCConfigFile[Any] | None: 

450 if namespace not in self._configs: 

451 return None 

452 result = self._configs[namespace] 

453 

454 if file_name is None: 

455 return result 

456 

457 if file_name in result: 

458 return result[file_name] 

459 

460 return None 

461 

462 @override 

463 def set(self, namespace: str, file_name: str, config: ABCConfigFile[Any]) -> Self: 

464 if namespace not in self._configs: 

465 self._configs[namespace] = {} 

466 

467 self._configs[namespace][file_name] = config 

468 return self 

469 

470 def _get_formats( 

471 self, 

472 file_name: str, 

473 config_formats: str | Iterable[str] | None, 

474 configfile_format: str | None = None, 

475 ) -> Iterable[str]: 

476 """ 

477 从给定参数计算所有可能的配置格式 

478 

479 .. attention:: 

480 返回所有可能的配置格式,不会检查配置格式是否存在! 

481 可迭代对象的产生顺序即为配置格式优先级,优先级逻辑见下表 

482 

483 :param file_name: 文件名 

484 :type file_name: str 

485 :param config_formats: 配置格式 

486 :type config_formats: str | Iterable[str] | None 

487 :param configfile_format: 

488 该配置文件对象本身配置格式属性的值 

489 可选项,一般在保存时填入 

490 用于在没手动指定配置格式且没文件后缀时使用该值进行尝试 

491 

492 .. seealso:: 

493 :py:attr:`ABCConfigFile.config_format` 

494 

495 :return: 配置格式 

496 :rtype: Iterable[str] 

497 

498 :raise UnsupportedConfigFormatError: 不支持的配置格式 

499 

500 格式计算优先级 

501 -------------- 

502 

503 1.config_formats的bool求值为真 

504 

505 2.文件名注册了对应的SL处理器 

506 

507 3.configfile_format非None 

508 

509 .. versionadded:: 0.2.0 

510 """ # noqa: RUF002 

511 result_formats = [] 

512 # 先尝试从传入的参数中获取配置文件格式 

513 if config_formats is None: 

514 config_formats = [] 

515 elif isinstance(config_formats, str): 

516 config_formats = [config_formats] 

517 else: 

518 config_formats = list(config_formats) 

519 result_formats.extend(config_formats) 

520 

521 def _check_file_name(match: str | Pattern[str]) -> bool: 

522 if isinstance(match, str): 

523 return file_name.endswith(match) 

524 return bool(match.fullmatch(file_name)) # 目前没SL处理器用得上 # pragma: no cover 

525 

526 # 再尝试从文件名匹配配置文件格式 

527 for m in self.FileNameProcessors: 

528 if _check_file_name(m): 

529 result_formats.extend(self.FileNameProcessors[m]) 

530 

531 # 最后尝试从配置文件对象本身获取配置文件格式 

532 if configfile_format is not None: 

533 result_formats.append(configfile_format) 

534 

535 if not result_formats: 

536 raise UnsupportedConfigFormatError(None) 

537 

538 return OrderedDict.fromkeys(result_formats) 

539 

540 def _try_sl_processors[R]( 

541 self, 

542 namespace: str, 

543 file_name: str, 

544 config_formats: str | Iterable[str] | None, 

545 processor: Callable[[Self, str, str, str], R], 

546 file_config_format: str | None = None, 

547 ) -> R: 

548 """ 

549 自动尝试推断ABCConfigFile所支持的config_format 

550 

551 :param namespace: 命名空间 

552 :type namespace: str 

553 :param file_name: 文件名 

554 :type file_name: str 

555 :param config_formats: 配置格式 

556 :type config_formats: str | Iterable[str] | None 

557 :param processor: 

558 处理器,参数为[配置池对象, 命名空间, 文件名, 配置格式]返回值会被直接返回, 

559 出现意料内的SL处理器无法处理需抛出FailedProcessConfigFileError以允许继续尝试别的SL处理器 

560 :type processor: Callable[[Self, str, str, str], R] 

561 :param file_config_format: 

562 该配置文件对象本身配置格式属性的值 

563 可选项,一般在保存时填入 

564 用于在没手动指定配置格式且没文件后缀时使用该值进行尝试 

565 

566 .. seealso:: 

567 :py:attr:`ABCConfigFile.config_format` 

568 

569 :return: 处理器返回值 

570 :rtype: R 

571 

572 :raise UnsupportedConfigFormatError: 不支持的配置格式 

573 :raise FailedProcessConfigFileError: 处理配置文件失败 

574 

575 .. seealso:: 

576 格式计算优先级 

577 

578 :py:meth:`_get_formats` 

579 

580 .. versionadded:: 0.1.2 

581 

582 .. versionchanged:: 0.2.0 

583 拆分格式计算到方法 :py:meth:`_get_formats` 

584 """ # noqa: RUF002 

585 

586 def callback_wrapper(cfg_fmt: str) -> R: 

587 return processor(self, namespace, file_name, cfg_fmt) 

588 

589 # 尝试从多个SL加载器中找到能正确加载的那一个 

590 errors: dict[str, FailedProcessConfigFileError[Any] | UnsupportedConfigFormatError] = {} 

591 for config_format in self._get_formats(file_name, config_formats, file_config_format): 

592 if config_format not in self.SLProcessors: 

593 errors[config_format] = UnsupportedConfigFormatError(config_format) 

594 continue 

595 try: 

596 # 能正常运行直接返回结果不再进行尝试 

597 return callback_wrapper(config_format) 

598 except FailedProcessConfigFileError as err: 

599 errors[config_format] = err 

600 

601 for error in errors.values(): 

602 if isinstance(error, UnsupportedConfigFormatError): 

603 raise error from None 

604 

605 # 如果没有一个SL加载器能正确加载则抛出异常 

606 raise FailedProcessConfigFileError(errors) 

607 

608 @override 

609 def save( 

610 self, 

611 namespace: str, 

612 file_name: str, 

613 config_formats: str | Iterable[str] | None = None, 

614 config: ABCConfigFile[Any] | None = None, 

615 *args: Any, 

616 **kwargs: Any, 

617 ) -> Self: 

618 if config is not None: 

619 self.set(namespace, file_name, config) 

620 

621 file = self._configs[namespace][file_name] 

622 

623 def processor(pool: Self, ns: str, fn: str, cf: str) -> None: 

624 file.save(pool, ns, fn, cf, *args, **kwargs) 

625 

626 self._try_sl_processors(namespace, file_name, config_formats, processor, file_config_format=file.config_format) 

627 return self 

628 

629 @override 

630 def save_all( 

631 self, *, ignore_err: bool = False 

632 ) -> dict[str, dict[str, tuple[ABCConfigFile[Any], Exception]]] | None: 

633 errors: dict[str, dict[str, tuple[ABCConfigFile[Any], Exception]]] = {} 

634 for namespace, configs in deepcopy(self._configs).items(): 

635 errors[namespace] = {} 

636 for file_name, config in configs.items(): 

637 try: 

638 self.save(namespace, file_name) 

639 except Exception as err: 

640 if not ignore_err: 

641 raise 

642 errors[namespace][file_name] = (config, err) 

643 

644 if not ignore_err: 

645 return None 

646 

647 return {k: v for k, v in errors.items() if v} 

648 

649 @override 

650 def initialize( 

651 self, 

652 namespace: str, 

653 file_name: str, 

654 *args: Any, 

655 config_formats: str | Iterable[str] | None = None, 

656 **kwargs: Any, 

657 ) -> ABCConfigFile[Any]: 

658 def processor(pool: Self, ns: str, fn: str, cf: str) -> ABCConfigFile[Any]: 

659 config_file_cls: type[ABCConfigFile[Any]] = self.SLProcessors[cf].supported_file_classes[0] 

660 result = config_file_cls.initialize(pool, ns, fn, cf, *args, **kwargs) 

661 

662 pool.set(namespace, file_name, result) 

663 return result 

664 

665 return self._try_sl_processors(namespace, file_name, config_formats, processor) 

666 

667 @override 

668 def load( 

669 self, 

670 namespace: str, 

671 file_name: str, 

672 *args: Any, 

673 config_formats: str | Iterable[str] | None = None, 

674 allow_initialize: bool = False, 

675 **kwargs: Any, 

676 ) -> ABCConfigFile[Any]: 

677 """ 

678 加载配置到指定命名空间并返回 

679 

680 :param namespace: 命名空间 

681 :type namespace: str 

682 :param file_name: 文件名 

683 :type file_name: str 

684 :param config_formats: 配置格式 

685 :type config_formats: str | Iterable[str] | None 

686 :param allow_initialize: 是否允许初始化配置文件 

687 :type allow_initialize: bool 

688 

689 :return: 配置对象 

690 :rtype: ABCConfigFile 

691 

692 .. versionchanged:: 0.2.0 

693 现在会像 :py:meth:`save` 一样接收并传递额外参数 

694 

695 删除参数 ``config_file_cls`` 

696 

697 重命名参数 ``allow_create`` 为 ``allow_initialize`` 

698 

699 现在由 :py:meth:`ABCConfigFile.initialize` 创建新的空 :py:class:`ABCConfigFile` 对象 

700 """ 

701 cache = self.get(namespace, file_name) 

702 if cache is not None: 

703 return cache 

704 

705 def processor(pool: Self, ns: str, fn: str, cf: str) -> ABCConfigFile[Any]: 

706 config_file_cls = self.SLProcessors[cf].supported_file_classes[0] 

707 try: 

708 result = config_file_cls.load(pool, ns, fn, cf, *args, **kwargs) 

709 except FileNotFoundError: 

710 if not allow_initialize: 

711 raise 

712 result = pool.initialize(ns, fn, *args, config_formats=cf, **kwargs) 

713 

714 pool.set(namespace, file_name, result) 

715 return result 

716 

717 return self._try_sl_processors(namespace, file_name, config_formats, processor) 

718 

719 @override 

720 def remove(self, namespace: str, file_name: str | None = None) -> Self: 

721 if file_name is None: 

722 del self._configs[namespace] 

723 return self 

724 

725 del self._configs[namespace][file_name] 

726 if not self._configs[namespace]: 

727 del self._configs[namespace] 

728 return self 

729 

730 @override 

731 def discard(self, namespace: str, file_name: str | None = None) -> Self: 

732 with suppress(KeyError): 

733 self.remove(namespace, file_name) 

734 return self 

735 

736 def __getitem__(self, item: str | tuple[str, str]) -> dict[str, ABCConfigFile[Any]] | ABCConfigFile[Any]: 

737 if isinstance(item, tuple): 

738 if len(item) != 2: 

739 msg = f"item must be a tuple of length 2, got {item}" 

740 raise ValueError(msg) 

741 return deepcopy(self.configs[item[0]][item[1]]) 

742 return deepcopy(self.configs[item]) 

743 

744 def __contains__(self, item: Any) -> bool: 

745 """.. versionadded:: 0.1.2""" 

746 if isinstance(item, str): 

747 return item in self._configs 

748 if isinstance(item, Iterable): 

749 item = tuple(item) 

750 if len(item) == 1: 

751 return item[0] in self._configs 

752 if len(item) != 2: 

753 msg = f"item must be a tuple of length 2, got {item}" 

754 raise ValueError(msg) 

755 return (item[0] in self._configs) and (item[1] in self._configs[item[0]]) 

756 

757 def __len__(self) -> int: 

758 """配置文件总数""" 

759 return sum(len(v) for v in self._configs.values()) 

760 

761 @property 

762 def configs(self) -> dict[str, dict[str, ABCConfigFile[Any]]]: 

763 """配置文件字典""" 

764 return deepcopy(self._configs) 

765 

766 @override 

767 def __repr__(self) -> str: 

768 return f"{self.__class__.__name__}({self.configs!r})" 

769 

770 

771__all__ = ( 

772 "BasicConfigData", 

773 "BasicConfigPool", 

774 "BasicIndexedConfigData", 

775 "BasicSingleConfigData", 

776 "ConfigFile", 

777 "PHelper", 

778)