|
14 | 14 | import dataclasses |
15 | 15 | import functools |
16 | 16 | import inspect |
| 17 | +import json |
17 | 18 | import logging |
18 | 19 | import os |
| 20 | +import pathlib |
19 | 21 | from abc import ABC |
20 | 22 | from collections.abc import Callable, Iterable, Mapping |
21 | | -from typing import Any, ClassVar, TypeAlias, cast |
| 23 | +from typing import Any, ClassVar, Literal, TypeAlias, cast |
22 | 24 |
|
23 | 25 | from typing_extensions import ParamSpec, TypeVar, override |
24 | 26 |
|
25 | 27 | from . import data, exceptions, types, utils |
26 | 28 | from .registry import Registry |
27 | 29 | from .upload import Upload, make_upload |
28 | 30 |
|
| 31 | +try: |
| 32 | + from platformdirs import user_config_dir # pyright: ignore[reportAssignmentType] |
| 33 | +except ImportError: |
| 34 | + |
| 35 | + def user_config_dir( |
| 36 | + appname: str | None = None, |
| 37 | + appauthor: str | Literal[False] | None = None, |
| 38 | + ): |
| 39 | + """Mock for user config locator.""" |
| 40 | + return |
| 41 | + |
| 42 | + |
29 | 43 | P = ParamSpec("P") |
30 | 44 | T = TypeVar("T") |
31 | 45 | S = TypeVar("S", bound="Storage") |
|
36 | 50 | Capability: TypeAlias = utils.Capability |
37 | 51 |
|
38 | 52 | adapters = Registry["type[Storage]"]() |
| 53 | +storages = Registry["Storage"]() |
| 54 | + |
39 | 55 | location_transformers = Registry[types.LocationTransformer]() |
40 | 56 |
|
41 | 57 |
|
@@ -995,3 +1011,42 @@ def make_storage(name: str, settings: dict[str, Any]) -> Storage: |
995 | 1011 | settings.setdefault("name", name) |
996 | 1012 |
|
997 | 1013 | return adapter(settings) |
| 1014 | + |
| 1015 | + |
| 1016 | +def get_storage(name: str, settings: dict[str, Any] | None = None) -> Storage: |
| 1017 | + """Get storage from the pool. |
| 1018 | +
|
| 1019 | + If storage accessed for the first time, it's initialized and added to the |
| 1020 | + pool. After that the same storage is returned every time the function is |
| 1021 | + called with the given name. |
| 1022 | +
|
| 1023 | + """ |
| 1024 | + if name not in storages: |
| 1025 | + if settings is None: |
| 1026 | + config_file = os.getenv("FILE_KEEPER_CONFIG") |
| 1027 | + |
| 1028 | + if not config_file and (config_dir := user_config_dir("file-keeper")): |
| 1029 | + config_file = os.path.join(config_dir, "file-keeper.json") |
| 1030 | + |
| 1031 | + if not config_file or not os.path.isfile(config_file): |
| 1032 | + path = pathlib.Path().absolute() |
| 1033 | + |
| 1034 | + while len(path.parts) > 1: |
| 1035 | + config_file = str(path / "file-keeper.json") |
| 1036 | + if os.path.exists(config_file): |
| 1037 | + break |
| 1038 | + path = path.parent |
| 1039 | + else: |
| 1040 | + config_file = None |
| 1041 | + |
| 1042 | + if config_file: |
| 1043 | + log.debug("Load configuration from %s", config_file) |
| 1044 | + with open(config_file) as src: |
| 1045 | + settings = json.load(src).get("storages", {}).get(name) |
| 1046 | + |
| 1047 | + if not settings: |
| 1048 | + raise exceptions.UnknownStorageError(name) |
| 1049 | + |
| 1050 | + storages.register(name, make_storage(name, settings)) |
| 1051 | + |
| 1052 | + return storages[name] |
0 commit comments