pynamodb-async 0.1.0__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.
- pynamodb_async-0.1.0/.gitignore +4 -0
- pynamodb_async-0.1.0/LICENSE +21 -0
- pynamodb_async-0.1.0/PKG-INFO +87 -0
- pynamodb_async-0.1.0/README.md +66 -0
- pynamodb_async-0.1.0/RELEASE_NOTES.rst +21 -0
- pynamodb_async-0.1.0/pyproject.toml +48 -0
- pynamodb_async-0.1.0/scripts/check_dist.py +113 -0
- pynamodb_async-0.1.0/scripts/release_notes.py +66 -0
- pynamodb_async-0.1.0/scripts/upstream_watch.py +45 -0
- pynamodb_async-0.1.0/src/pynamodb_async/__init__.py +21 -0
- pynamodb_async-0.1.0/src/pynamodb_async/_compat.py +198 -0
- pynamodb_async-0.1.0/src/pynamodb_async/_version.py +24 -0
- pynamodb_async-0.1.0/src/pynamodb_async/connection/__init__.py +13 -0
- pynamodb_async-0.1.0/src/pynamodb_async/connection/_botocore_private.py +30 -0
- pynamodb_async-0.1.0/src/pynamodb_async/connection/base.py +1310 -0
- pynamodb_async-0.1.0/src/pynamodb_async/connection/table.py +337 -0
- pynamodb_async-0.1.0/src/pynamodb_async/indexes.py +245 -0
- pynamodb_async-0.1.0/src/pynamodb_async/models.py +1179 -0
- pynamodb_async-0.1.0/src/pynamodb_async/pagination.py +222 -0
- pynamodb_async-0.1.0/src/pynamodb_async/transactions.py +144 -0
- pynamodb_async-0.1.0/tests/__init__.py +1 -0
- pynamodb_async-0.1.0/tests/conftest.py +11 -0
- pynamodb_async-0.1.0/tests/data.py +1308 -0
- pynamodb_async-0.1.0/tests/deep_eq.py +142 -0
- pynamodb_async-0.1.0/tests/integration/__init__.py +1 -0
- pynamodb_async-0.1.0/tests/integration/base_integration_test.py +176 -0
- pynamodb_async-0.1.0/tests/integration/binary_update_test.py +49 -0
- pynamodb_async-0.1.0/tests/integration/conftest.py +29 -0
- pynamodb_async-0.1.0/tests/integration/model_integration_test.py +145 -0
- pynamodb_async-0.1.0/tests/integration/table_integration_test.py +163 -0
- pynamodb_async-0.1.0/tests/integration/test_discriminator_index.py +115 -0
- pynamodb_async-0.1.0/tests/integration/test_transaction_integration.py +435 -0
- pynamodb_async-0.1.0/tests/response.py +24 -0
- pynamodb_async-0.1.0/tests/test_base_connection.py +1713 -0
- pynamodb_async-0.1.0/tests/test_client_cache.py +207 -0
- pynamodb_async-0.1.0/tests/test_connection_lifecycle.py +70 -0
- pynamodb_async-0.1.0/tests/test_model.py +3470 -0
- pynamodb_async-0.1.0/tests/test_model_close.py +21 -0
- pynamodb_async-0.1.0/tests/test_pagination.py +80 -0
- pynamodb_async-0.1.0/tests/test_signals.py +99 -0
- pynamodb_async-0.1.0/tests/test_table_connection.py +554 -0
- pynamodb_async-0.1.0/tests/test_transaction.py +151 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2014 Jharrod LaFon
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: pynamodb-async
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Native asyncio API for PynamoDB, built on aiobotocore
|
|
5
|
+
Project-URL: Source, https://github.com/zeroae/pynamodb-async
|
|
6
|
+
Project-URL: Issues, https://github.com/zeroae/pynamodb-async/issues
|
|
7
|
+
Project-URL: PynamoDB, https://github.com/pynamodb/PynamoDB
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Framework :: AsyncIO
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Requires-Dist: aiobotocore>=2.13.0
|
|
19
|
+
Requires-Dist: pynamodb<6.2,>=6.1
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# pynamodb-async
|
|
23
|
+
|
|
24
|
+
A native asyncio API for [PynamoDB](https://github.com/pynamodb/PynamoDB), built on
|
|
25
|
+
[aiobotocore](https://github.com/aio-libs/aiobotocore). It runs next to official PynamoDB:
|
|
26
|
+
you keep using PynamoDB's attributes, indexes and expressions, and get awaitable models,
|
|
27
|
+
queries, batches and transactions.
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
pip install pynamodb-async
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Needs Python 3.10+. This installs official `pynamodb` (6.1.x) and `aiobotocore`.
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
Declare a model with `Model` from `pynamodb_async.models` and attributes from `pynamodb.attributes`:
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
import pynamodb_async
|
|
41
|
+
from pynamodb.attributes import UnicodeAttribute
|
|
42
|
+
from pynamodb_async.models import Model
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class User(Model):
|
|
46
|
+
class Meta:
|
|
47
|
+
table_name = "users"
|
|
48
|
+
region = "us-east-1"
|
|
49
|
+
|
|
50
|
+
email = UnicodeAttribute(hash_key=True)
|
|
51
|
+
name = UnicodeAttribute(null=True)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
async def main():
|
|
55
|
+
async with pynamodb_async.connections():
|
|
56
|
+
await User("ada@example.com", name="Ada").save()
|
|
57
|
+
user = await User.get("ada@example.com")
|
|
58
|
+
async for u in User.query("ada@example.com"):
|
|
59
|
+
print(u.name)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Closing clients and event loops
|
|
63
|
+
|
|
64
|
+
Every async client holds an HTTP session. Close it when you are done with
|
|
65
|
+
`await User.close()`, or wrap your code in `async with pynamodb_async.connections():`, which
|
|
66
|
+
closes every connection opened inside the block. A client belongs to the event loop that
|
|
67
|
+
created it, so use each model and `Connection` from one event loop at a time, and prefer one
|
|
68
|
+
long-lived event loop for the whole program.
|
|
69
|
+
|
|
70
|
+
## Relationship to PynamoDB
|
|
71
|
+
|
|
72
|
+
This package works alongside official PynamoDB. Its code is generated from a private source
|
|
73
|
+
repository, a fork that also offers the same async API as a drop-in replacement for `pynamodb`.
|
|
74
|
+
This add-on has its own async `Connection.get_client()`. What it does not have are the fork's
|
|
75
|
+
sync additions: `pynamodb.models.Model.close()`, `pynamodb.connections()` and the sync
|
|
76
|
+
`pynamodb.connection.Connection.get_client()`. It also lacks upstream fixes merged after the
|
|
77
|
+
supported PynamoDB release (for example [#1302](https://github.com/pynamodb/PynamoDB/pull/1302))
|
|
78
|
+
until upstream ships them.
|
|
79
|
+
|
|
80
|
+
## Contributing
|
|
81
|
+
|
|
82
|
+
The code here is generated from a private source repository. Please open issues in this
|
|
83
|
+
repository; pull requests are ported over by hand.
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
|
|
87
|
+
MIT, derived from PynamoDB. See `LICENSE`.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# pynamodb-async
|
|
2
|
+
|
|
3
|
+
A native asyncio API for [PynamoDB](https://github.com/pynamodb/PynamoDB), built on
|
|
4
|
+
[aiobotocore](https://github.com/aio-libs/aiobotocore). It runs next to official PynamoDB:
|
|
5
|
+
you keep using PynamoDB's attributes, indexes and expressions, and get awaitable models,
|
|
6
|
+
queries, batches and transactions.
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
pip install pynamodb-async
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Needs Python 3.10+. This installs official `pynamodb` (6.1.x) and `aiobotocore`.
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
Declare a model with `Model` from `pynamodb_async.models` and attributes from `pynamodb.attributes`:
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
import pynamodb_async
|
|
20
|
+
from pynamodb.attributes import UnicodeAttribute
|
|
21
|
+
from pynamodb_async.models import Model
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class User(Model):
|
|
25
|
+
class Meta:
|
|
26
|
+
table_name = "users"
|
|
27
|
+
region = "us-east-1"
|
|
28
|
+
|
|
29
|
+
email = UnicodeAttribute(hash_key=True)
|
|
30
|
+
name = UnicodeAttribute(null=True)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
async def main():
|
|
34
|
+
async with pynamodb_async.connections():
|
|
35
|
+
await User("ada@example.com", name="Ada").save()
|
|
36
|
+
user = await User.get("ada@example.com")
|
|
37
|
+
async for u in User.query("ada@example.com"):
|
|
38
|
+
print(u.name)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Closing clients and event loops
|
|
42
|
+
|
|
43
|
+
Every async client holds an HTTP session. Close it when you are done with
|
|
44
|
+
`await User.close()`, or wrap your code in `async with pynamodb_async.connections():`, which
|
|
45
|
+
closes every connection opened inside the block. A client belongs to the event loop that
|
|
46
|
+
created it, so use each model and `Connection` from one event loop at a time, and prefer one
|
|
47
|
+
long-lived event loop for the whole program.
|
|
48
|
+
|
|
49
|
+
## Relationship to PynamoDB
|
|
50
|
+
|
|
51
|
+
This package works alongside official PynamoDB. Its code is generated from a private source
|
|
52
|
+
repository, a fork that also offers the same async API as a drop-in replacement for `pynamodb`.
|
|
53
|
+
This add-on has its own async `Connection.get_client()`. What it does not have are the fork's
|
|
54
|
+
sync additions: `pynamodb.models.Model.close()`, `pynamodb.connections()` and the sync
|
|
55
|
+
`pynamodb.connection.Connection.get_client()`. It also lacks upstream fixes merged after the
|
|
56
|
+
supported PynamoDB release (for example [#1302](https://github.com/pynamodb/PynamoDB/pull/1302))
|
|
57
|
+
until upstream ships them.
|
|
58
|
+
|
|
59
|
+
## Contributing
|
|
60
|
+
|
|
61
|
+
The code here is generated from a private source repository. Please open issues in this
|
|
62
|
+
repository; pull requests are ported over by hand.
|
|
63
|
+
|
|
64
|
+
## License
|
|
65
|
+
|
|
66
|
+
MIT, derived from PynamoDB. See `LICENSE`.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Release Notes
|
|
2
|
+
=============
|
|
3
|
+
|
|
4
|
+
v0.1.0
|
|
5
|
+
------
|
|
6
|
+
|
|
7
|
+
First release of ``pynamodb-async``: a native asyncio API for PynamoDB, built on aiobotocore.
|
|
8
|
+
Requires Python 3.10+, official ``pynamodb>=6.1,<6.2`` and ``aiobotocore>=2.13.0``.
|
|
9
|
+
|
|
10
|
+
* ``pynamodb_async.models.Model`` with awaitable ``get``, ``save``, ``update``, ``delete``,
|
|
11
|
+
``refresh``, ``count`` and table operations; ``query``, ``scan`` and ``batch_get`` return
|
|
12
|
+
async iterators; ``batch_write()`` is an async context manager.
|
|
13
|
+
* Async transactions: ``pynamodb_async.transactions.TransactGet`` / ``TransactWrite``.
|
|
14
|
+
* Close clients with ``await Model.close()`` or ``async with pynamodb_async.connections():``.
|
|
15
|
+
Models with the same settings on the same event loop share one client.
|
|
16
|
+
|
|
17
|
+
Known limitations:
|
|
18
|
+
|
|
19
|
+
* Use each model and connection from one event loop at a time.
|
|
20
|
+
* No built-in concurrency yet (for example parallel scan); gather independent operations
|
|
21
|
+
yourself.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.24", "hatch-vcs>=0.5", "setuptools-scm>=10"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pynamodb-async"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Native asyncio API for PynamoDB, built on aiobotocore"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
dependencies = ["pynamodb>=6.1,<6.2", "aiobotocore>=2.13.0"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
|
+
"Framework :: AsyncIO",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3.10",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
Source = "https://github.com/zeroae/pynamodb-async"
|
|
25
|
+
Issues = "https://github.com/zeroae/pynamodb-async/issues"
|
|
26
|
+
PynamoDB = "https://github.com/pynamodb/PynamoDB"
|
|
27
|
+
|
|
28
|
+
[dependency-groups]
|
|
29
|
+
test = ["pytest>=7", "pytest-asyncio>=0.23", "pytest-env", "pytest-mock", "freezegun", "blinker>=1.3,<2.0", "packaging"]
|
|
30
|
+
|
|
31
|
+
[tool.hatch.version]
|
|
32
|
+
source = "vcs"
|
|
33
|
+
tag-pattern = '^pynamodb-async-v(?P<version>\d+(?:\.\d+)*(?:(?:a|b|rc|\.post|\.dev)\d+)?)$'
|
|
34
|
+
raw-options = { search_parent_directories = true, fallback_version = "0.1.0.dev0", scm = { git = { describe_command = "git describe --dirty --tags --long --match 'pynamodb-async-v*'" } } }
|
|
35
|
+
|
|
36
|
+
[tool.hatch.build.hooks.vcs]
|
|
37
|
+
version-file = "src/pynamodb_async/_version.py"
|
|
38
|
+
|
|
39
|
+
[tool.hatch.build.targets.wheel]
|
|
40
|
+
packages = ["src/pynamodb_async"]
|
|
41
|
+
|
|
42
|
+
[tool.hatch.build.targets.sdist]
|
|
43
|
+
include = ["src", "tests", "scripts", "README.md", "RELEASE_NOTES.rst", "LICENSE", "pyproject.toml"]
|
|
44
|
+
|
|
45
|
+
[tool.pytest.ini_options]
|
|
46
|
+
asyncio_mode = "auto"
|
|
47
|
+
markers = ["ddblocal: requires a DynamoDB Local server on localhost:8000"]
|
|
48
|
+
env = ["AWS_ACCESS_KEY_ID=1", "AWS_SECRET_ACCESS_KEY=2", "AWS_DEFAULT_REGION=us-east-1"]
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# AUTO-GENERATED from scripts/check_dist.py by scripts/make_addon.py - DO NOT EDIT
|
|
3
|
+
"""Check built distributions before they are released.
|
|
4
|
+
|
|
5
|
+
python scripts/check_dist.py DIST_DIR --name zae-pynamodb --package pynamodb \
|
|
6
|
+
--requires-python '>=3.7' [--min-version 6.2.0.dev0] [--expect-version 6.2.0] \
|
|
7
|
+
[--sdist-allow 'src/*' ...]
|
|
8
|
+
|
|
9
|
+
Checks every wheel and sdist in DIST_DIR: project name, Requires-Python, version (at least
|
|
10
|
+
--min-version; exactly --expect-version, with no local part), wheel files limited to the
|
|
11
|
+
package and its .dist-info, and - when --sdist-allow is given - sdist files limited to those
|
|
12
|
+
globs. Prints one "problem: ..." line per issue and exits 1 if there are any.
|
|
13
|
+
"""
|
|
14
|
+
import argparse
|
|
15
|
+
import fnmatch
|
|
16
|
+
import sys
|
|
17
|
+
import tarfile
|
|
18
|
+
import zipfile
|
|
19
|
+
from email.parser import Parser
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import List, NamedTuple, Optional, Sequence
|
|
22
|
+
|
|
23
|
+
from packaging.version import Version
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Options(NamedTuple):
|
|
27
|
+
name: str
|
|
28
|
+
package: str
|
|
29
|
+
requires_python: str
|
|
30
|
+
min_version: Optional[str]
|
|
31
|
+
expect_version: Optional[str]
|
|
32
|
+
sdist_allow: List[str]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _check_metadata(label: str, text: str, opts: Options) -> List[str]:
|
|
36
|
+
meta = Parser().parsestr(text)
|
|
37
|
+
problems = []
|
|
38
|
+
if meta.get("Name") != opts.name:
|
|
39
|
+
problems.append("{}: Name is {!r}, expected {!r}".format(label, meta.get("Name"), opts.name))
|
|
40
|
+
if meta.get("Requires-Python") != opts.requires_python:
|
|
41
|
+
problems.append("{}: Requires-Python is {!r}, expected {!r}".format(
|
|
42
|
+
label, meta.get("Requires-Python"), opts.requires_python))
|
|
43
|
+
version = Version(meta.get("Version", "0"))
|
|
44
|
+
if opts.min_version and version < Version(opts.min_version):
|
|
45
|
+
problems.append("{}: version {} is below {}".format(label, version, opts.min_version))
|
|
46
|
+
if opts.expect_version and (version != Version(opts.expect_version) or version.local):
|
|
47
|
+
problems.append("{}: version {} is not exactly {}".format(label, version, opts.expect_version))
|
|
48
|
+
return problems
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def check_wheel(path: Path, opts: Options) -> List[str]:
|
|
52
|
+
with zipfile.ZipFile(str(path)) as zf:
|
|
53
|
+
names = zf.namelist()
|
|
54
|
+
meta_names = [n for n in names if n.endswith(".dist-info/METADATA")]
|
|
55
|
+
if not meta_names:
|
|
56
|
+
return ["{}: no .dist-info/METADATA".format(path.name)]
|
|
57
|
+
problems = _check_metadata(path.name, zf.read(meta_names[0]).decode("utf-8"), opts)
|
|
58
|
+
for name in names:
|
|
59
|
+
top = name.split("/", 1)[0]
|
|
60
|
+
if top != opts.package and not top.endswith(".dist-info"):
|
|
61
|
+
problems.append("{}: unexpected file {}".format(path.name, name))
|
|
62
|
+
return problems
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def check_sdist(path: Path, opts: Options) -> List[str]:
|
|
66
|
+
with tarfile.open(str(path), "r:gz") as tf:
|
|
67
|
+
members = [m for m in tf.getmembers() if m.isfile()]
|
|
68
|
+
rel = {m.name: m.name.split("/", 1)[1] if "/" in m.name else m.name for m in members}
|
|
69
|
+
pkg_info = [m for m in members if rel[m.name] == "PKG-INFO"]
|
|
70
|
+
if not pkg_info:
|
|
71
|
+
return ["{}: no PKG-INFO".format(path.name)]
|
|
72
|
+
handle = tf.extractfile(pkg_info[0])
|
|
73
|
+
problems = _check_metadata(path.name, handle.read().decode("utf-8") if handle else "", opts)
|
|
74
|
+
if opts.sdist_allow:
|
|
75
|
+
for full, name in sorted(rel.items()):
|
|
76
|
+
if not any(fnmatch.fnmatch(name, pattern) for pattern in opts.sdist_allow):
|
|
77
|
+
problems.append("{}: file not allowed in sdist: {}".format(path.name, name))
|
|
78
|
+
return problems
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def main(argv: Sequence[str]) -> int:
|
|
82
|
+
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
83
|
+
parser.add_argument("dist_dir")
|
|
84
|
+
parser.add_argument("--name", required=True)
|
|
85
|
+
parser.add_argument("--package", required=True)
|
|
86
|
+
parser.add_argument("--requires-python", required=True)
|
|
87
|
+
parser.add_argument("--min-version")
|
|
88
|
+
parser.add_argument("--expect-version")
|
|
89
|
+
parser.add_argument("--sdist-allow", action="append", default=[])
|
|
90
|
+
args = parser.parse_args(argv)
|
|
91
|
+
opts = Options(args.name, args.package, args.requires_python,
|
|
92
|
+
args.min_version, args.expect_version, args.sdist_allow)
|
|
93
|
+
dist = Path(args.dist_dir)
|
|
94
|
+
wheels = sorted(dist.glob("*.whl"))
|
|
95
|
+
sdists = sorted(dist.glob("*.tar.gz"))
|
|
96
|
+
problems = []
|
|
97
|
+
if not wheels:
|
|
98
|
+
problems.append("no wheel in {}".format(dist))
|
|
99
|
+
if not sdists:
|
|
100
|
+
problems.append("no sdist in {}".format(dist))
|
|
101
|
+
for w in wheels:
|
|
102
|
+
problems += check_wheel(w, opts)
|
|
103
|
+
for s in sdists:
|
|
104
|
+
problems += check_sdist(s, opts)
|
|
105
|
+
for p in problems:
|
|
106
|
+
print("problem: " + p)
|
|
107
|
+
if not problems:
|
|
108
|
+
print("ok: {} wheel(s), {} sdist(s)".format(len(wheels), len(sdists)))
|
|
109
|
+
return 1 if problems else 0
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
if __name__ == "__main__":
|
|
113
|
+
sys.exit(main(sys.argv[1:]))
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# AUTO-GENERATED from scripts/release_notes.py by scripts/make_addon.py - DO NOT EDIT
|
|
3
|
+
"""Print the release-notes section for a version from an .rst file.
|
|
4
|
+
|
|
5
|
+
python scripts/release_notes.py docs/release_notes.rst 6.2.0 [--output notes.md]
|
|
6
|
+
|
|
7
|
+
A section starts at a title line ``v<version>`` underlined with a single repeated character
|
|
8
|
+
(``-``, ``=``, ``~`` or ``^``) and runs until the next underlined title. Exits 1 if the
|
|
9
|
+
section is missing or empty, so a release cannot go out without notes.
|
|
10
|
+
"""
|
|
11
|
+
import argparse
|
|
12
|
+
import sys
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import List, Optional, Sequence
|
|
15
|
+
|
|
16
|
+
_UNDERLINE_CHARS = "-=~^"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _is_underline(line: str) -> bool:
|
|
20
|
+
s = line.strip()
|
|
21
|
+
return len(s) >= 3 and len(set(s)) == 1 and s[0] in _UNDERLINE_CHARS
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _is_title(lines: List[str], i: int) -> bool:
|
|
25
|
+
return (i + 1 < len(lines) and bool(lines[i].strip())
|
|
26
|
+
and not _is_underline(lines[i]) and _is_underline(lines[i + 1]))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def extract(text: str, version: str) -> Optional[str]:
|
|
30
|
+
lines = text.splitlines()
|
|
31
|
+
title = "v" + version
|
|
32
|
+
start = None
|
|
33
|
+
for i in range(len(lines)):
|
|
34
|
+
if lines[i].strip() == title and _is_title(lines, i):
|
|
35
|
+
start = i + 2
|
|
36
|
+
break
|
|
37
|
+
if start is None:
|
|
38
|
+
return None
|
|
39
|
+
end = len(lines)
|
|
40
|
+
for j in range(start, len(lines)):
|
|
41
|
+
if _is_title(lines, j):
|
|
42
|
+
end = j
|
|
43
|
+
break
|
|
44
|
+
body = "\n".join(lines[start:end]).strip()
|
|
45
|
+
return body or None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def main(argv: Sequence[str]) -> int:
|
|
49
|
+
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
50
|
+
parser.add_argument("rst_file")
|
|
51
|
+
parser.add_argument("version")
|
|
52
|
+
parser.add_argument("--output")
|
|
53
|
+
args = parser.parse_args(argv)
|
|
54
|
+
body = extract(Path(args.rst_file).read_text(encoding="utf-8"), args.version)
|
|
55
|
+
if body is None:
|
|
56
|
+
print("no non-empty 'v{}' section in {}".format(args.version, args.rst_file), file=sys.stderr)
|
|
57
|
+
return 1
|
|
58
|
+
if args.output:
|
|
59
|
+
Path(args.output).write_text(body + "\n", encoding="utf-8")
|
|
60
|
+
else:
|
|
61
|
+
print(body)
|
|
62
|
+
return 0
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
if __name__ == "__main__":
|
|
66
|
+
sys.exit(main(sys.argv[1:]))
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# AUTO-GENERATED from scripts/upstream_watch.py by scripts/make_addon.py - DO NOT EDIT
|
|
3
|
+
"""Check whether the latest official pynamodb release is inside the add-on's supported range.
|
|
4
|
+
|
|
5
|
+
python scripts/upstream_watch.py --spec '>=6.1,<6.2'
|
|
6
|
+
|
|
7
|
+
Prints the latest version. Exit 0 when it is supported, 2 when it is outside the range (the
|
|
8
|
+
weekly workflow then opens an issue to port the change and widen the range), 1 on errors.
|
|
9
|
+
"""
|
|
10
|
+
import argparse
|
|
11
|
+
import json
|
|
12
|
+
import sys
|
|
13
|
+
from typing import Callable, Sequence
|
|
14
|
+
from urllib.request import urlopen
|
|
15
|
+
|
|
16
|
+
from packaging.specifiers import SpecifierSet
|
|
17
|
+
from packaging.version import Version
|
|
18
|
+
|
|
19
|
+
PYPI_URL = "https://pypi.org/pypi/pynamodb/json"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def is_supported(version: str, spec: str) -> bool:
|
|
23
|
+
return Version(version) in SpecifierSet(spec)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def latest_release() -> str:
|
|
27
|
+
with urlopen(PYPI_URL, timeout=30) as response:
|
|
28
|
+
return json.load(response)["info"]["version"]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def main(argv: Sequence[str], latest: Callable[[], str] = latest_release) -> int:
|
|
32
|
+
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
33
|
+
parser.add_argument("--spec", required=True)
|
|
34
|
+
args = parser.parse_args(argv)
|
|
35
|
+
try:
|
|
36
|
+
version = latest()
|
|
37
|
+
except Exception as e:
|
|
38
|
+
print("error: could not read the latest pynamodb release: {}".format(e), file=sys.stderr)
|
|
39
|
+
return 1
|
|
40
|
+
print(version)
|
|
41
|
+
return 0 if is_supported(version, args.spec) else 2
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
if __name__ == "__main__":
|
|
45
|
+
sys.exit(main(sys.argv[1:]))
|
|
@@ -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']
|