pynamodb-async 0.1.0__py3-none-any.whl
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.
- pynamodb_async/__init__.py +21 -0
- pynamodb_async/_compat.py +198 -0
- pynamodb_async/_version.py +24 -0
- pynamodb_async/connection/__init__.py +13 -0
- pynamodb_async/connection/_botocore_private.py +30 -0
- pynamodb_async/connection/base.py +1310 -0
- pynamodb_async/connection/table.py +337 -0
- pynamodb_async/indexes.py +245 -0
- pynamodb_async/models.py +1179 -0
- pynamodb_async/pagination.py +222 -0
- pynamodb_async/transactions.py +144 -0
- pynamodb_async-0.1.0.dist-info/METADATA +87 -0
- pynamodb_async-0.1.0.dist-info/RECORD +15 -0
- pynamodb_async-0.1.0.dist-info/WHEEL +4 -0
- pynamodb_async-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# AUTO-GENERATED from pynamodb/asyncio/__init__.py by scripts/make_addon.py - DO NOT EDIT
|
|
2
|
+
"""
|
|
3
|
+
Async API for PynamoDB, built on aiobotocore.
|
|
4
|
+
|
|
5
|
+
Install with ``pip install pynamodb-async`` (Python 3.10+). Usage mirrors the
|
|
6
|
+
sync API: ``from pynamodb_async.models import Model``.
|
|
7
|
+
"""
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
if sys.version_info < (3, 10): # pragma: no cover
|
|
11
|
+
raise ImportError("pynamodb_async requires Python 3.10+; install pynamodb-async")
|
|
12
|
+
try:
|
|
13
|
+
import aiobotocore # noqa: F401
|
|
14
|
+
except ImportError as e:
|
|
15
|
+
raise ImportError(
|
|
16
|
+
"pynamodb_async requires aiobotocore; install pynamodb-async (Python 3.10+)"
|
|
17
|
+
) from e
|
|
18
|
+
|
|
19
|
+
from pynamodb_async.connection.base import connections # noqa: E402
|
|
20
|
+
|
|
21
|
+
__all__ = ['connections']
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# AUTO-GENERATED from pynamodb/asyncio/_compat.py by scripts/make_addon.py - DO NOT EDIT
|
|
2
|
+
"""
|
|
3
|
+
Async halves of operations that differ between the sync and async APIs.
|
|
4
|
+
|
|
5
|
+
Helpers for operations whose synchronous and asynchronous forms cannot be derived
|
|
6
|
+
from one another by rewriting keywords (sleeping, gathering, list building). This is
|
|
7
|
+
the only module in this package allowed to import asyncio.
|
|
8
|
+
|
|
9
|
+
`alist` is async-only: the generator rewrites `_compat.alist(` to `list(`.
|
|
10
|
+
"""
|
|
11
|
+
import asyncio
|
|
12
|
+
import time
|
|
13
|
+
import weakref
|
|
14
|
+
from typing import Any, AsyncIterable, Dict, Hashable, List, TypeVar
|
|
15
|
+
|
|
16
|
+
from aiobotocore.config import AioConfig
|
|
17
|
+
|
|
18
|
+
from pynamodb.constants import SERVICE_NAME
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class _AsyncTime:
|
|
22
|
+
"""Default clock for RateLimiter: real time, non-blocking sleep."""
|
|
23
|
+
|
|
24
|
+
time = staticmethod(time.time)
|
|
25
|
+
|
|
26
|
+
@staticmethod
|
|
27
|
+
async def sleep(seconds: float) -> None:
|
|
28
|
+
await asyncio.sleep(seconds)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
TIME_MODULE: Any = _AsyncTime()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
_T = TypeVar('_T')
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
async def alist(iterable: AsyncIterable[_T]) -> List[_T]:
|
|
38
|
+
"""Collects an async iterable; the generator rewrites `await alist(x)` to `list(x)`."""
|
|
39
|
+
return [item async for item in iterable]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
async def sleep(seconds: float) -> None:
|
|
43
|
+
await asyncio.sleep(seconds)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _has_credentials(client: Any) -> bool:
|
|
47
|
+
signer = client._request_signer
|
|
48
|
+
return not (signer and not signer._credentials)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def client_usable(connection: Any) -> bool:
|
|
52
|
+
client = connection._client
|
|
53
|
+
if not client or not _has_credentials(client):
|
|
54
|
+
return False
|
|
55
|
+
try:
|
|
56
|
+
loop = asyncio.get_running_loop()
|
|
57
|
+
except RuntimeError:
|
|
58
|
+
return False
|
|
59
|
+
# aiobotocore clients only work on the loop that created them.
|
|
60
|
+
return _held_loop(connection) is loop
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class _Entry:
|
|
64
|
+
__slots__ = ('client', 'refs')
|
|
65
|
+
|
|
66
|
+
def __init__(self, client: Any) -> None:
|
|
67
|
+
self.client = client
|
|
68
|
+
self.refs = 0
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# event loop -> {Connection._client_key() -> shared client}. Weak on the loop,
|
|
72
|
+
# but a client that has made a request holds an aiohttp session, which holds
|
|
73
|
+
# the loop, so a finished loop's entry would keep itself alive. open_client
|
|
74
|
+
# therefore prunes entries for closed loops (see _prune_closed_loops).
|
|
75
|
+
_clients: "weakref.WeakKeyDictionary[asyncio.AbstractEventLoop, Dict[Hashable, _Entry]]" = weakref.WeakKeyDictionary()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _prune_closed_loops() -> None:
|
|
79
|
+
"""Drops cache entries of closed loops; their clients cannot be closed any more."""
|
|
80
|
+
for loop in [loop for loop in list(_clients.keys()) if loop.is_closed()]:
|
|
81
|
+
_clients.pop(loop, None)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _held_loop(connection: Any) -> Any:
|
|
85
|
+
ref = getattr(connection, '_client_loop', None)
|
|
86
|
+
return ref() if ref is not None else None
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _held_entry(connection: Any, loop: Any) -> Any:
|
|
90
|
+
"""The usable entry this connection already holds on `loop`, if any."""
|
|
91
|
+
entry = getattr(connection, '_client_entry', None)
|
|
92
|
+
if entry is not None and _held_loop(connection) is loop and _has_credentials(entry.client):
|
|
93
|
+
return entry
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _make_header_hook(headers: Any) -> Any:
|
|
98
|
+
# Closes over a copy of the headers, not the connection: the client lives in
|
|
99
|
+
# a shared cache and must not keep a connection (and so its loop) alive.
|
|
100
|
+
headers = dict(headers) if headers else None
|
|
101
|
+
|
|
102
|
+
def _before_send(request: Any, **_: Any) -> None:
|
|
103
|
+
if headers:
|
|
104
|
+
request.headers.update(headers)
|
|
105
|
+
|
|
106
|
+
return _before_send
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
async def open_client(connection: Any, config: Any) -> Any:
|
|
110
|
+
loop = asyncio.get_running_loop()
|
|
111
|
+
held = _held_entry(connection, loop)
|
|
112
|
+
if held is not None:
|
|
113
|
+
return held.client
|
|
114
|
+
_prune_closed_loops()
|
|
115
|
+
per_loop = _clients.setdefault(loop, {})
|
|
116
|
+
key = connection._client_key()
|
|
117
|
+
entry = per_loop.get(key)
|
|
118
|
+
if entry is None or not _has_credentials(entry.client):
|
|
119
|
+
# A poisoned entry is replaced in the cache; connections still holding
|
|
120
|
+
# it release it when they notice (client_usable) or close.
|
|
121
|
+
client = await connection.session.create_client(
|
|
122
|
+
SERVICE_NAME, connection.region, endpoint_url=connection.host, config=AioConfig().merge(config),
|
|
123
|
+
).__aenter__()
|
|
124
|
+
# Another coroutine may have opened a client while we were suspended.
|
|
125
|
+
held = _held_entry(connection, loop)
|
|
126
|
+
existing = per_loop.get(key)
|
|
127
|
+
if held is not None:
|
|
128
|
+
await client.__aexit__(None, None, None)
|
|
129
|
+
return held.client
|
|
130
|
+
if existing is not None and _has_credentials(existing.client):
|
|
131
|
+
# Take the reference before suspending to close the extra client;
|
|
132
|
+
# otherwise the last other holder could release (and close) the
|
|
133
|
+
# existing one meanwhile, leaving this connection on a closed client.
|
|
134
|
+
_hold(connection, existing, loop)
|
|
135
|
+
await client.__aexit__(None, None, None)
|
|
136
|
+
return existing.client
|
|
137
|
+
# extra_headers is part of the key, so every holder sends the same headers.
|
|
138
|
+
client.meta.events.register_first('before-send.*.*', _make_header_hook(connection._extra_headers))
|
|
139
|
+
entry = _Entry(client)
|
|
140
|
+
per_loop[key] = entry
|
|
141
|
+
_hold(connection, entry, loop)
|
|
142
|
+
return entry.client
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _hold(connection: Any, entry: _Entry, loop: Any) -> None:
|
|
146
|
+
entry.refs += 1
|
|
147
|
+
connection._client_entry = entry
|
|
148
|
+
connection._client_loop = weakref.ref(loop)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
async def _release(connection: Any) -> None:
|
|
152
|
+
entry = getattr(connection, '_client_entry', None)
|
|
153
|
+
loop = _held_loop(connection)
|
|
154
|
+
connection._client_entry = None
|
|
155
|
+
connection._client_loop = None
|
|
156
|
+
connection._client = None
|
|
157
|
+
if entry is None:
|
|
158
|
+
return
|
|
159
|
+
entry.refs -= 1
|
|
160
|
+
if entry.refs > 0:
|
|
161
|
+
return
|
|
162
|
+
per_loop = _clients.get(loop) if loop is not None else None
|
|
163
|
+
if per_loop is not None:
|
|
164
|
+
for key, value in list(per_loop.items()):
|
|
165
|
+
if value is entry:
|
|
166
|
+
del per_loop[key]
|
|
167
|
+
try:
|
|
168
|
+
running = asyncio.get_running_loop()
|
|
169
|
+
except RuntimeError:
|
|
170
|
+
running = None
|
|
171
|
+
if running is not None and running is loop:
|
|
172
|
+
await entry.client.__aexit__(None, None, None)
|
|
173
|
+
# Otherwise the owning loop is gone or different; its sockets cannot be
|
|
174
|
+
# closed from here, so the client is dropped.
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
async def replace_client(connection: Any) -> None:
|
|
178
|
+
await _release(connection)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
async def close_client(connection: Any) -> None:
|
|
182
|
+
await _release(connection)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def client_property(connection: Any) -> Any:
|
|
186
|
+
if connection._client is None:
|
|
187
|
+
raise RuntimeError(
|
|
188
|
+
"The async client is not open yet; use `await connection.get_client()` "
|
|
189
|
+
"(any awaited operation opens it)"
|
|
190
|
+
)
|
|
191
|
+
return connection._client
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def connection_repr(connection: Any) -> str:
|
|
195
|
+
# repr must never raise, so it cannot go through the (not yet open) client.
|
|
196
|
+
if connection._client is not None:
|
|
197
|
+
return "Connection<{}>".format(connection._client.meta.endpoint_url)
|
|
198
|
+
return "Connection<{}>".format(connection.host or connection.region)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '0.1.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 0)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# AUTO-GENERATED from pynamodb/asyncio/connection/__init__.py by scripts/make_addon.py - DO NOT EDIT
|
|
2
|
+
"""
|
|
3
|
+
PynamoDB lowest level connection
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from pynamodb_async.connection.base import Connection
|
|
7
|
+
from pynamodb_async.connection.table import TableConnection
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"Connection",
|
|
12
|
+
"TableConnection",
|
|
13
|
+
]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# AUTO-GENERATED from pynamodb/asyncio/connection/_botocore_private.py by scripts/make_addon.py - DO NOT EDIT
|
|
2
|
+
"""
|
|
3
|
+
Type-annotates the private aiobotocore APIs that we're currently relying on.
|
|
4
|
+
"""
|
|
5
|
+
from typing import Dict
|
|
6
|
+
|
|
7
|
+
import botocore.credentials
|
|
8
|
+
import botocore.endpoint
|
|
9
|
+
import botocore.hooks
|
|
10
|
+
import botocore.signers
|
|
11
|
+
from aiobotocore.client import AioBaseClient
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class BotocoreEndpointPrivate(botocore.endpoint.Endpoint):
|
|
15
|
+
_event_emitter: botocore.hooks.HierarchicalEmitter
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class BotocoreRequestSignerPrivate(botocore.signers.RequestSigner):
|
|
19
|
+
_credentials: botocore.credentials.Credentials
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class BotocoreBaseClientPrivate(AioBaseClient):
|
|
23
|
+
_endpoint: BotocoreEndpointPrivate
|
|
24
|
+
_request_signer: BotocoreRequestSignerPrivate
|
|
25
|
+
|
|
26
|
+
async def _make_api_call( self,
|
|
27
|
+
operation_name: str,
|
|
28
|
+
operation_kwargs: Dict,
|
|
29
|
+
) -> Dict:
|
|
30
|
+
raise NotImplementedError
|