aresource 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aresource-0.0.1/PKG-INFO +60 -0
- aresource-0.0.1/README.md +44 -0
- aresource-0.0.1/pyproject.toml +66 -0
- aresource-0.0.1/src/aresource/__init__.py +3 -0
- aresource-0.0.1/src/aresource/manager.py +102 -0
- aresource-0.0.1/src/aresource/resources/__init__.py +0 -0
- aresource-0.0.1/src/aresource/resources/aiohttp/__init__.py +0 -0
- aresource-0.0.1/src/aresource/resources/aiohttp/session.py +28 -0
- aresource-0.0.1/src/aresource/resources/aiohttp/web.py +42 -0
- aresource-0.0.1/src/aresource/resources/files/__init__.py +21 -0
- aresource-0.0.1/src/aresource/resources/files/base.py +36 -0
- aresource-0.0.1/src/aresource/resources/files/bytes.py +15 -0
- aresource-0.0.1/src/aresource/resources/files/hocon.py +17 -0
- aresource-0.0.1/src/aresource/resources/files/ini.py +19 -0
- aresource-0.0.1/src/aresource/resources/files/json.py +20 -0
- aresource-0.0.1/src/aresource/resources/files/path.py +18 -0
aresource-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: aresource
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary:
|
|
5
|
+
Author: Peter Babka
|
|
6
|
+
Author-email: 159peter951@gmail.com
|
|
7
|
+
Requires-Python: >=3.13, <4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
10
|
+
Provides-Extra: aiohttp
|
|
11
|
+
Provides-Extra: hocon
|
|
12
|
+
Requires-Dist: aiohttp (>=3.0.0,<4.0.0) ; extra == "aiohttp"
|
|
13
|
+
Requires-Dist: pyhocon (>=0.3.0,<0.4.0) ; extra == "hocon"
|
|
14
|
+
Project-URL: Repository, https://github.com/xbabka01/aresource
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# aresource
|
|
18
|
+
|
|
19
|
+
A Python project for resource management.
|
|
20
|
+
|
|
21
|
+
## Project Structure
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
src/
|
|
25
|
+
aresource/
|
|
26
|
+
tests/
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
Use [Poetry](https://python-poetry.org/) to install dependencies:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
poetry install
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
Import and use the package in your Python code:
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from aresource import manager
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Testing
|
|
46
|
+
|
|
47
|
+
Run tests with:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
poetry run pytest
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Code Quality
|
|
54
|
+
|
|
55
|
+
- Type checking: `poetry run mypy src/`
|
|
56
|
+
- Linting: `poetry run ruff src/`
|
|
57
|
+
|
|
58
|
+
## License
|
|
59
|
+
|
|
60
|
+
MIT License
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# aresource
|
|
2
|
+
|
|
3
|
+
A Python project for resource management.
|
|
4
|
+
|
|
5
|
+
## Project Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
src/
|
|
9
|
+
aresource/
|
|
10
|
+
tests/
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
Use [Poetry](https://python-poetry.org/) to install dependencies:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
poetry install
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
Import and use the package in your Python code:
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from aresource import manager
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Testing
|
|
30
|
+
|
|
31
|
+
Run tests with:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
poetry run pytest
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Code Quality
|
|
38
|
+
|
|
39
|
+
- Type checking: `poetry run mypy src/`
|
|
40
|
+
- Linting: `poetry run ruff src/`
|
|
41
|
+
|
|
42
|
+
## License
|
|
43
|
+
|
|
44
|
+
MIT License
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "aresource"
|
|
3
|
+
version = "0.0.1"
|
|
4
|
+
description = ""
|
|
5
|
+
authors = [
|
|
6
|
+
{name = "Peter Babka",email = "159peter951@gmail.com"}
|
|
7
|
+
]
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
requires-python = ">=3.13, <4.0"
|
|
10
|
+
dependencies = [
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
[project.urls]
|
|
14
|
+
repository = "https://github.com/xbabka01/aresource"
|
|
15
|
+
|
|
16
|
+
[tool.poetry]
|
|
17
|
+
packages = [{include = "aresource", from = "src"}]
|
|
18
|
+
|
|
19
|
+
[tool.poetry.dependencies]
|
|
20
|
+
aiohttp = { version = "^3.0.0", optional = true }
|
|
21
|
+
pyhocon = { version = "^0.3.0", optional = true }
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
[tool.poetry.extras]
|
|
25
|
+
aiohttp = ["aiohttp"]
|
|
26
|
+
hocon = ["pyhocon"]
|
|
27
|
+
|
|
28
|
+
[tool.poetry.group.dev.dependencies]
|
|
29
|
+
pytest = "^8.4.1"
|
|
30
|
+
pytest-asyncio = "^1.0.0"
|
|
31
|
+
pytest-cov = "^6.2.1"
|
|
32
|
+
pytest-ruff = "^0.5"
|
|
33
|
+
pytest-mypy = "^1.0.1"
|
|
34
|
+
aiohttp = "^3.12.14"
|
|
35
|
+
pyhocon = "^0.3.61"
|
|
36
|
+
|
|
37
|
+
[tool.pytest.ini_options]
|
|
38
|
+
addopts = "--strict-markers --tb=short --mypy --cov=aresource --cov-report=html --cov-report=term --ruff --ruff-format"
|
|
39
|
+
asyncio_mode = "auto"
|
|
40
|
+
testpaths = ["tests", "src"]
|
|
41
|
+
|
|
42
|
+
[tool.ruff]
|
|
43
|
+
line-length = 99
|
|
44
|
+
|
|
45
|
+
[tool.ruff.lint]
|
|
46
|
+
select = ["E", "F", "W", "I", "N", "UP", "B", "C4", "T20", "SIM", "Q", "RUF"]
|
|
47
|
+
ignore = []
|
|
48
|
+
|
|
49
|
+
[tool.ruff.format]
|
|
50
|
+
docstring-code-format = true
|
|
51
|
+
|
|
52
|
+
[tool.mypy]
|
|
53
|
+
python_version = "3.13"
|
|
54
|
+
files = ["src/aresource", "tests"]
|
|
55
|
+
strict = true
|
|
56
|
+
warn_redundant_casts = true
|
|
57
|
+
warn_unused_ignores = true
|
|
58
|
+
warn_unreachable = true
|
|
59
|
+
|
|
60
|
+
[[tool.mypy.overrides]]
|
|
61
|
+
module = "pyhocon"
|
|
62
|
+
ignore_missing_imports = true
|
|
63
|
+
|
|
64
|
+
[build-system]
|
|
65
|
+
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
66
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
import copy
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from ast import TypeVar
|
|
5
|
+
from collections.abc import AsyncIterator
|
|
6
|
+
from typing import Any, ClassVar, Self
|
|
7
|
+
|
|
8
|
+
T = TypeVar("T")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BaseResource[T](ABC):
|
|
12
|
+
name: str | None = None
|
|
13
|
+
|
|
14
|
+
def __get__(self, instance: "ResourceManager", owner: "type[ResourceManager]") -> T:
|
|
15
|
+
return instance.get_resource(self.name) # type: ignore[no-any-return]
|
|
16
|
+
|
|
17
|
+
def __set_name__(self, owner: "type[ResourceManager]", name: str) -> None:
|
|
18
|
+
owner.register_resource(name, self)
|
|
19
|
+
|
|
20
|
+
@abstractmethod
|
|
21
|
+
@contextlib.asynccontextmanager
|
|
22
|
+
async def acquire(self, manager: "ResourceManager") -> AsyncIterator[T]:
|
|
23
|
+
"""Acquire the resource asynchronously."""
|
|
24
|
+
yield None # type: ignore[misc]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ResourceManager:
|
|
28
|
+
_cls: Any = None
|
|
29
|
+
_resources: ClassVar[dict[str, tuple[BaseResource[Any], Any]]] = {}
|
|
30
|
+
|
|
31
|
+
def __init__(self) -> None:
|
|
32
|
+
self._exitstack: contextlib.AsyncExitStack | None = None
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def register_resource(cls, name: str, resource: BaseResource[Any]) -> None:
|
|
36
|
+
"""Register a resource with the manager."""
|
|
37
|
+
if not isinstance(resource, BaseResource):
|
|
38
|
+
raise TypeError(f"{resource.__name__} is not a subclass of BaseResource")
|
|
39
|
+
if name in cls._resources:
|
|
40
|
+
raise AttributeError(f"Resource {name} is already registered in {cls.__name__}")
|
|
41
|
+
# Create a copy of the class resources to avoid modifying the superclass
|
|
42
|
+
if cls._cls is not cls:
|
|
43
|
+
cls._resources = copy.deepcopy(cls._resources)
|
|
44
|
+
cls._cls = cls
|
|
45
|
+
|
|
46
|
+
cls._resources[name] = (resource, ...)
|
|
47
|
+
resource.name = name
|
|
48
|
+
|
|
49
|
+
def get_resource(self, name: str | None) -> Any:
|
|
50
|
+
"""
|
|
51
|
+
Get the value of a registered resource by name.
|
|
52
|
+
"""
|
|
53
|
+
if name not in self._resources:
|
|
54
|
+
raise AttributeError(f"Resource {name} is not registered in {self.__class__.__name__}")
|
|
55
|
+
value = self._resources[name][1]
|
|
56
|
+
if value is Ellipsis:
|
|
57
|
+
raise AttributeError(
|
|
58
|
+
f"Resource {name} is not initialized in {self.__class__.__name__}"
|
|
59
|
+
)
|
|
60
|
+
return value
|
|
61
|
+
|
|
62
|
+
def set_resource(self, name: str, value: Any) -> None:
|
|
63
|
+
"""
|
|
64
|
+
Set the value of a registered resource by name.
|
|
65
|
+
Raises AttributeError if the resource is not registered.
|
|
66
|
+
"""
|
|
67
|
+
if name not in self._resources:
|
|
68
|
+
raise AttributeError(f"Resource {name} is not registered in {self.__class__.__name__}")
|
|
69
|
+
|
|
70
|
+
resource = self._resources[name][0]
|
|
71
|
+
self._resources[name] = (resource, value)
|
|
72
|
+
|
|
73
|
+
async def setup(self) -> None:
|
|
74
|
+
await self.__aenter__()
|
|
75
|
+
|
|
76
|
+
async def __aenter__(self) -> Self:
|
|
77
|
+
"""Asynchronously enter the resource manager context, acquiring all resources.
|
|
78
|
+
Each resource is acquired using its async context manager and set in the manager.
|
|
79
|
+
Returns self.
|
|
80
|
+
"""
|
|
81
|
+
if self._exitstack is not None:
|
|
82
|
+
raise RuntimeError("ResourceManager is already set up")
|
|
83
|
+
|
|
84
|
+
self._exitstack = contextlib.AsyncExitStack()
|
|
85
|
+
for name, (resource, _) in self._resources.items():
|
|
86
|
+
value = await self._exitstack.enter_async_context(resource.acquire(self))
|
|
87
|
+
self.set_resource(name, value)
|
|
88
|
+
return self
|
|
89
|
+
|
|
90
|
+
async def aclose(self) -> None:
|
|
91
|
+
"""Asynchronously close the resource manager, releasing all resources."""
|
|
92
|
+
await self.__aexit__()
|
|
93
|
+
|
|
94
|
+
async def __aexit__(self, *exc: Any) -> bool | None:
|
|
95
|
+
"""Asynchronously exit the resource manager context, releasing all resources.
|
|
96
|
+
Delegates to the AsyncExitStack's __aexit__ method.
|
|
97
|
+
Returns the result of the exit stack's __aexit__.
|
|
98
|
+
"""
|
|
99
|
+
if self._exitstack is not None:
|
|
100
|
+
await self._exitstack.__aexit__(*exc)
|
|
101
|
+
self._exitstack = None
|
|
102
|
+
return False # Do not suppress exceptions
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
from collections.abc import AsyncIterator, Callable
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from aiohttp import ClientSession
|
|
6
|
+
|
|
7
|
+
from aresource.manager import BaseResource, ResourceManager
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ClientSessionResource(BaseResource[ClientSession]):
|
|
11
|
+
"""
|
|
12
|
+
Resource that provides an aiohttp ClientSession.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self, callback: Callable[[ResourceManager], dict[str, Any]] | None = None
|
|
17
|
+
) -> None:
|
|
18
|
+
super().__init__()
|
|
19
|
+
self.callback = callback
|
|
20
|
+
|
|
21
|
+
@contextlib.asynccontextmanager
|
|
22
|
+
async def acquire(self, manager: "ResourceManager") -> AsyncIterator[ClientSession]:
|
|
23
|
+
kwargs = self.callback(manager) if self.callback else {}
|
|
24
|
+
session = ClientSession(**kwargs)
|
|
25
|
+
try:
|
|
26
|
+
yield session
|
|
27
|
+
finally:
|
|
28
|
+
await session.close() # Ensure the session is closed after use
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
from collections.abc import AsyncIterator, Callable
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from aiohttp import web
|
|
6
|
+
|
|
7
|
+
from aresource.manager import BaseResource, ResourceManager
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class WebAppResource(BaseResource[web.Application]):
|
|
11
|
+
"""
|
|
12
|
+
Resource that provides an aiohttp ClientSession.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
routes: Callable[[ResourceManager], web.RouteTableDef],
|
|
18
|
+
callback: Callable[[ResourceManager], dict[str, Any]] | None = None,
|
|
19
|
+
) -> None:
|
|
20
|
+
super().__init__()
|
|
21
|
+
self.callback = callback
|
|
22
|
+
self.routes = routes
|
|
23
|
+
|
|
24
|
+
@contextlib.asynccontextmanager
|
|
25
|
+
async def acquire(self, manager: "ResourceManager") -> AsyncIterator[web.Application]:
|
|
26
|
+
kwargs = self.callback(manager) if self.callback else {}
|
|
27
|
+
app = web.Application(**kwargs)
|
|
28
|
+
|
|
29
|
+
routes = self.routes(manager)
|
|
30
|
+
app.add_routes(routes)
|
|
31
|
+
|
|
32
|
+
runner = web.AppRunner(app)
|
|
33
|
+
|
|
34
|
+
await runner.setup()
|
|
35
|
+
site = web.TCPSite(runner, "localhost", 8081)
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
await site.start()
|
|
39
|
+
yield app
|
|
40
|
+
finally:
|
|
41
|
+
await site.stop()
|
|
42
|
+
await app.shutdown()
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from .base import BasePathResource
|
|
2
|
+
from .bytes import BytesResource
|
|
3
|
+
from .ini import IniResource
|
|
4
|
+
from .json import JsonResource
|
|
5
|
+
from .path import PathResource
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"BasePathResource",
|
|
9
|
+
"BytesResource",
|
|
10
|
+
"HoconResource",
|
|
11
|
+
"IniResource",
|
|
12
|
+
"JsonResource",
|
|
13
|
+
"PathResource",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
from .hocon import HoconResource
|
|
18
|
+
except ImportError:
|
|
19
|
+
pass
|
|
20
|
+
else:
|
|
21
|
+
__all__.append("HoconResource")
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
from abc import ABCMeta
|
|
3
|
+
from collections.abc import Iterator
|
|
4
|
+
from importlib.resources import as_file, files
|
|
5
|
+
from importlib.resources.abc import Traversable
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import TypeVar
|
|
8
|
+
|
|
9
|
+
from aresource.manager import BaseResource
|
|
10
|
+
|
|
11
|
+
T = TypeVar("T")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class BasePathResource[T](BaseResource[T], metaclass=ABCMeta):
|
|
15
|
+
def __init__(self, package: str, path: str) -> None:
|
|
16
|
+
super().__init__()
|
|
17
|
+
self.package = package
|
|
18
|
+
self.path = path
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def resource(self) -> Traversable:
|
|
22
|
+
"""
|
|
23
|
+
Get the path to the resource in the package.
|
|
24
|
+
"""
|
|
25
|
+
return files(self.package).joinpath(self.path)
|
|
26
|
+
|
|
27
|
+
@contextlib.contextmanager
|
|
28
|
+
def as_file(self) -> Iterator[Path]:
|
|
29
|
+
"""
|
|
30
|
+
Context manager to yield the path to the resource as a file.
|
|
31
|
+
"""
|
|
32
|
+
with as_file(self.resource) as resource_path:
|
|
33
|
+
yield resource_path
|
|
34
|
+
|
|
35
|
+
def read(self) -> bytes:
|
|
36
|
+
return self.resource.read_bytes()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
from collections.abc import AsyncIterator
|
|
3
|
+
|
|
4
|
+
from aresource.manager import ResourceManager
|
|
5
|
+
from aresource.resources.files.base import BasePathResource
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BytesResource(BasePathResource[bytes]):
|
|
9
|
+
"""
|
|
10
|
+
Resource that provides a binary file in a package.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
@contextlib.asynccontextmanager
|
|
14
|
+
async def acquire(self, manager: "ResourceManager") -> AsyncIterator[bytes]:
|
|
15
|
+
yield self.read()
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
from collections.abc import AsyncIterator
|
|
3
|
+
|
|
4
|
+
from pyhocon import ConfigFactory, ConfigTree
|
|
5
|
+
|
|
6
|
+
from aresource.manager import ResourceManager
|
|
7
|
+
from aresource.resources.files.base import BasePathResource
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class HoconResource(BasePathResource[ConfigTree]):
|
|
11
|
+
"""
|
|
12
|
+
Resource that provides a JSON file in a package.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
@contextlib.asynccontextmanager
|
|
16
|
+
async def acquire(self, manager: "ResourceManager") -> AsyncIterator[ConfigTree]:
|
|
17
|
+
yield ConfigFactory.parse_string(self.read().decode("utf-8"))
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import configparser
|
|
2
|
+
import contextlib
|
|
3
|
+
from collections.abc import AsyncIterator
|
|
4
|
+
from configparser import ConfigParser
|
|
5
|
+
|
|
6
|
+
from aresource.manager import ResourceManager
|
|
7
|
+
from aresource.resources.files.base import BasePathResource
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class IniResource(BasePathResource[ConfigParser]):
|
|
11
|
+
"""
|
|
12
|
+
Resource that provides a JSON file in a package.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
@contextlib.asynccontextmanager
|
|
16
|
+
async def acquire(self, manager: "ResourceManager") -> AsyncIterator[ConfigParser]:
|
|
17
|
+
config = configparser.ConfigParser()
|
|
18
|
+
config.read_string(self.read().decode("utf-8"))
|
|
19
|
+
yield config
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
import json
|
|
3
|
+
from collections.abc import AsyncIterator
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from aresource.manager import ResourceManager
|
|
7
|
+
from aresource.resources.files.base import BasePathResource
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class JsonResource(BasePathResource[dict[str, Any] | list[Any]]):
|
|
11
|
+
"""
|
|
12
|
+
Resource that provides a JSON file in a package.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
@contextlib.asynccontextmanager
|
|
16
|
+
async def acquire(
|
|
17
|
+
self, manager: "ResourceManager"
|
|
18
|
+
) -> AsyncIterator[dict[str, Any] | list[Any]]:
|
|
19
|
+
data = self.read()
|
|
20
|
+
yield json.loads(data)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
from collections.abc import AsyncIterator
|
|
3
|
+
from importlib.resources import as_file
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from aresource.manager import ResourceManager
|
|
7
|
+
from aresource.resources.files.base import BasePathResource
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PathResource(BasePathResource[Path]):
|
|
11
|
+
"""
|
|
12
|
+
Resource that provides a path to a file in a package.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
@contextlib.asynccontextmanager
|
|
16
|
+
async def acquire(self, manager: "ResourceManager") -> AsyncIterator[Path]:
|
|
17
|
+
with as_file(self.resource) as resource_path:
|
|
18
|
+
yield resource_path
|