asyncpg 0.30.0__tar.gz → 0.32.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.
- {asyncpg-0.30.0 → asyncpg-0.32.0}/PKG-INFO +10 -21
- {asyncpg-0.30.0 → asyncpg-0.32.0}/README.rst +2 -2
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/_asyncio_compat.py +1 -1
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/_testbase/__init__.py +13 -4
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/_testbase/fuzzer.py +80 -23
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/_version.py +1 -1
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/connect_utils.py +321 -87
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/connection.py +143 -64
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/cursor.py +1 -1
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/introspection.py +8 -10
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/buffer.pxd +8 -1
- asyncpg-0.32.0/asyncpg/pgproto/buffer.pxi +3 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/buffer.pyx +18 -6
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/codecs/__init__.pxd +2 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/codecs/datetime.pyx +4 -4
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/consts.pxi +0 -3
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/pgproto.c +26461 -20849
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/pgproto.pyi +7 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/types.py +43 -31
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/uuid.pyx +9 -3
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pool.py +235 -57
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/prepared_stmt.py +3 -2
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/protocol/__init__.py +2 -1
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/protocol/codecs/array.pyx +2 -2
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/protocol/codecs/base.pxd +12 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/protocol/codecs/base.pyx +40 -13
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/protocol/codecs/pgproto.pyx +2 -1
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/protocol/coreproto.pxd +1 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/protocol/coreproto.pyx +12 -6
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/protocol/encodings.pyx +1 -1
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/protocol/pgtypes.pxi +6 -2
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/protocol/prepared_stmt.pyx +4 -4
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/protocol/protocol.c +54677 -50287
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/protocol/protocol.pxd +1 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/protocol/protocol.pyi +6 -21
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/protocol/protocol.pyx +138 -127
- asyncpg-0.32.0/asyncpg/protocol/record/pythoncapi_compat.h +2559 -0
- asyncpg-0.32.0/asyncpg/protocol/record/pythoncapi_compat_extras.h +72 -0
- asyncpg-0.32.0/asyncpg/protocol/record/recordobj.c +1290 -0
- asyncpg-0.32.0/asyncpg/protocol/record/recordobj.h +33 -0
- asyncpg-0.32.0/asyncpg/protocol/record.pyi +29 -0
- asyncpg-0.30.0/asyncpg/protocol/record/__init__.pxd → asyncpg-0.32.0/asyncpg/protocol/recordcapi.pxd +1 -6
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/protocol/scram.pxd +1 -1
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/protocol/scram.pyx +6 -16
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/types.py +2 -2
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg.egg-info/PKG-INFO +10 -21
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg.egg-info/SOURCES.txt +7 -2
- asyncpg-0.32.0/asyncpg.egg-info/requires.txt +11 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/docs/api/index.rst +14 -1
- {asyncpg-0.30.0 → asyncpg-0.32.0}/docs/index.rst +2 -2
- {asyncpg-0.30.0 → asyncpg-0.32.0}/docs/usage.rst +4 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/pyproject.toml +17 -10
- {asyncpg-0.30.0 → asyncpg-0.32.0}/setup.py +35 -11
- asyncpg-0.32.0/tests/certs/client.cert.pem +29 -0
- asyncpg-0.32.0/tests/certs/client_ca.cert.pem +25 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/certs/gen.py +128 -21
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/test_adversity.py +26 -3
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/test_cache_invalidation.py +3 -3
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/test_cancellation.py +3 -1
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/test_codecs.py +47 -1
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/test_connect.py +959 -211
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/test_copy.py +105 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/test_cursor.py +30 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/test_execute.py +26 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/test_logging.py +12 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/test_pool.py +647 -71
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/test_record.py +15 -2
- asyncpg-0.32.0/tests/test_scram.py +101 -0
- asyncpg-0.32.0/tests/test_subinterpreters.py +66 -0
- asyncpg-0.32.0/tests/test_timeout.py +378 -0
- asyncpg-0.30.0/AUTHORS +0 -6
- asyncpg-0.30.0/asyncpg/protocol/record/recordobj.c +0 -1025
- asyncpg-0.30.0/asyncpg/protocol/record/recordobj.h +0 -53
- asyncpg-0.30.0/asyncpg.egg-info/requires.txt +0 -31
- asyncpg-0.30.0/tests/certs/client.cert.pem +0 -24
- asyncpg-0.30.0/tests/certs/client_ca.cert.pem +0 -25
- asyncpg-0.30.0/tests/test_timeout.py +0 -154
- {asyncpg-0.30.0 → asyncpg-0.32.0}/.flake8 +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/LICENSE +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/MANIFEST.in +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/Makefile +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/__init__.py +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/cluster.py +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/compat.py +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/connresource.py +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/exceptions/__init__.py +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/exceptions/_base.py +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/__init__.pxd +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/__init__.py +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/codecs/bits.pyx +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/codecs/bytea.pyx +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/codecs/context.pyx +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/codecs/float.pyx +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/codecs/geometry.pyx +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/codecs/hstore.pyx +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/codecs/int.pyx +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/codecs/json.pyx +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/codecs/jsonpath.pyx +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/codecs/misc.pyx +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/codecs/network.pyx +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/codecs/numeric.pyx +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/codecs/pg_snapshot.pyx +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/codecs/text.pyx +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/codecs/tid.pyx +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/codecs/uuid.pyx +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/cpythonx.pxd +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/debug.h +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/debug.pxd +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/frb.pxd +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/frb.pyx +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/hton.h +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/hton.pxd +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/pgproto.pxd +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/pgproto.pyx +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/tohex.h +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/pgproto/tohex.pxd +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/protocol/codecs/__init__.py +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/protocol/codecs/range.pyx +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/protocol/codecs/record.pyx +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/protocol/codecs/textutils.pyx +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/protocol/consts.pxi +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/protocol/cpythonx.pxd +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/protocol/prepared_stmt.pxd +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/protocol/settings.pxd +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/protocol/settings.pyx +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/serverversion.py +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/transaction.py +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg/utils.py +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg.egg-info/dependency_links.txt +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg.egg-info/not-zip-safe +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/asyncpg.egg-info/top_level.txt +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/docs/Makefile +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/docs/_static/theme_overrides.css +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/docs/conf.py +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/docs/faq.rst +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/docs/installation.rst +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/performance.png +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/setup.cfg +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/__init__.py +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/certs/ca.cert.pem +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/certs/ca.crl.pem +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/certs/ca.key.pem +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/certs/client.csr.pem +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/certs/client.key.pem +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/certs/client.key.protected.pem +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/certs/client_ca.key.pem +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/certs/server.cert.pem +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/certs/server.crl.pem +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/certs/server.key.pem +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/test__environment.py +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/test__sourcecode.py +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/test_exceptions.py +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/test_introspection.py +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/test_listeners.py +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/test_prepare.py +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/test_test.py +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/test_transaction.py +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/test_types.py +0 -0
- {asyncpg-0.30.0 → asyncpg-0.32.0}/tests/test_utils.py +0 -0
|
@@ -1,46 +1,35 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: asyncpg
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.32.0
|
|
4
4
|
Summary: An asyncio PostgreSQL driver
|
|
5
5
|
Author-email: MagicStack Inc <hello@magic.io>
|
|
6
|
-
License: Apache
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
7
|
Project-URL: github, https://github.com/MagicStack/asyncpg
|
|
8
8
|
Keywords: database,postgres
|
|
9
9
|
Classifier: Development Status :: 5 - Production/Stable
|
|
10
10
|
Classifier: Framework :: AsyncIO
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
12
|
Classifier: Operating System :: POSIX
|
|
14
13
|
Classifier: Operating System :: MacOS :: MacOS X
|
|
15
14
|
Classifier: Operating System :: Microsoft :: Windows
|
|
16
15
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
18
16
|
Classifier: Programming Language :: Python :: 3.9
|
|
19
17
|
Classifier: Programming Language :: Python :: 3.10
|
|
20
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
21
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
22
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
23
|
+
Classifier: Programming Language :: Python :: Free Threading :: 2 - Beta
|
|
23
24
|
Classifier: Topic :: Database :: Front-Ends
|
|
24
|
-
Requires-Python: >=3.
|
|
25
|
+
Requires-Python: >=3.9.0
|
|
25
26
|
Description-Content-Type: text/x-rst
|
|
26
27
|
License-File: LICENSE
|
|
27
|
-
License-File: AUTHORS
|
|
28
28
|
Requires-Dist: async_timeout>=4.0.3; python_version < "3.11.0"
|
|
29
29
|
Provides-Extra: gssauth
|
|
30
30
|
Requires-Dist: gssapi; platform_system != "Windows" and extra == "gssauth"
|
|
31
31
|
Requires-Dist: sspilib; platform_system == "Windows" and extra == "gssauth"
|
|
32
|
-
|
|
33
|
-
Requires-Dist: flake8~=6.1; extra == "test"
|
|
34
|
-
Requires-Dist: flake8-pyi~=24.1.0; extra == "test"
|
|
35
|
-
Requires-Dist: distro~=1.9.0; extra == "test"
|
|
36
|
-
Requires-Dist: uvloop>=0.15.3; (platform_system != "Windows" and python_version < "3.14.0") and extra == "test"
|
|
37
|
-
Requires-Dist: gssapi; platform_system == "Linux" and extra == "test"
|
|
38
|
-
Requires-Dist: k5test; platform_system == "Linux" and extra == "test"
|
|
39
|
-
Requires-Dist: sspilib; platform_system == "Windows" and extra == "test"
|
|
40
|
-
Requires-Dist: mypy~=1.8.0; extra == "test"
|
|
41
|
-
Provides-Extra: docs
|
|
42
|
-
Requires-Dist: Sphinx~=8.1.3; extra == "docs"
|
|
43
|
-
Requires-Dist: sphinx_rtd_theme>=1.2.2; extra == "docs"
|
|
32
|
+
Dynamic: license-file
|
|
44
33
|
|
|
45
34
|
asyncpg -- A fast PostgreSQL Database Client Library for Python/asyncio
|
|
46
35
|
=======================================================================
|
|
@@ -57,8 +46,8 @@ of PostgreSQL server binary protocol for use with Python's ``asyncio``
|
|
|
57
46
|
framework. You can read more about asyncpg in an introductory
|
|
58
47
|
`blog post <http://magic.io/blog/asyncpg-1m-rows-from-postgres-to-python/>`_.
|
|
59
48
|
|
|
60
|
-
asyncpg requires Python 3.
|
|
61
|
-
versions 9.5 to
|
|
49
|
+
asyncpg requires Python 3.9 or later and is supported for PostgreSQL
|
|
50
|
+
versions 9.5 to 19. Other PostgreSQL versions or other databases
|
|
62
51
|
implementing the PostgreSQL protocol *may* work, but are not being
|
|
63
52
|
actively tested.
|
|
64
53
|
|
|
@@ -13,8 +13,8 @@ of PostgreSQL server binary protocol for use with Python's ``asyncio``
|
|
|
13
13
|
framework. You can read more about asyncpg in an introductory
|
|
14
14
|
`blog post <http://magic.io/blog/asyncpg-1m-rows-from-postgres-to-python/>`_.
|
|
15
15
|
|
|
16
|
-
asyncpg requires Python 3.
|
|
17
|
-
versions 9.5 to
|
|
16
|
+
asyncpg requires Python 3.9 or later and is supported for PostgreSQL
|
|
17
|
+
versions 9.5 to 19. Other PostgreSQL versions or other databases
|
|
18
18
|
implementing the PostgreSQL protocol *may* work, but are not being
|
|
19
19
|
actively tested.
|
|
20
20
|
|
|
@@ -33,7 +33,7 @@ async def wait_for(fut: compat.Awaitable[_T], timeout: float | None) -> _T:
|
|
|
33
33
|
|
|
34
34
|
If the wait is cancelled, the task is also cancelled.
|
|
35
35
|
|
|
36
|
-
If the task
|
|
36
|
+
If the task suppresses the cancellation and returns a value instead,
|
|
37
37
|
that value is returned.
|
|
38
38
|
|
|
39
39
|
This function is a coroutine.
|
|
@@ -101,9 +101,10 @@ class TestCase(unittest.TestCase, metaclass=TestCaseMeta):
|
|
|
101
101
|
def setUpClass(cls):
|
|
102
102
|
if os.environ.get('USE_UVLOOP'):
|
|
103
103
|
import uvloop
|
|
104
|
-
|
|
104
|
+
loop = uvloop.new_event_loop()
|
|
105
|
+
else:
|
|
106
|
+
loop = asyncio.new_event_loop()
|
|
105
107
|
|
|
106
|
-
loop = asyncio.new_event_loop()
|
|
107
108
|
asyncio.set_event_loop(None)
|
|
108
109
|
cls.loop = loop
|
|
109
110
|
|
|
@@ -267,6 +268,7 @@ def _shutdown_cluster(cluster):
|
|
|
267
268
|
|
|
268
269
|
|
|
269
270
|
def create_pool(dsn=None, *,
|
|
271
|
+
init_size=None,
|
|
270
272
|
min_size=10,
|
|
271
273
|
max_size=10,
|
|
272
274
|
max_queries=50000,
|
|
@@ -281,6 +283,7 @@ def create_pool(dsn=None, *,
|
|
|
281
283
|
**connect_kwargs):
|
|
282
284
|
return pool_class(
|
|
283
285
|
dsn,
|
|
286
|
+
init_size=init_size,
|
|
284
287
|
min_size=min_size,
|
|
285
288
|
max_size=max_size,
|
|
286
289
|
max_queries=max_queries,
|
|
@@ -366,10 +369,16 @@ class ClusterTestCase(TestCase):
|
|
|
366
369
|
self._pools = []
|
|
367
370
|
|
|
368
371
|
def tearDown(self):
|
|
369
|
-
|
|
372
|
+
maintenance_tasks = []
|
|
370
373
|
for pool in self._pools:
|
|
371
374
|
pool.terminate()
|
|
375
|
+
if pool._maintenance_task is not None:
|
|
376
|
+
maintenance_tasks.append(pool._maintenance_task)
|
|
377
|
+
if maintenance_tasks:
|
|
378
|
+
self.loop.run_until_complete(asyncio.gather(
|
|
379
|
+
*maintenance_tasks, return_exceptions=True))
|
|
372
380
|
self._pools = []
|
|
381
|
+
super().tearDown()
|
|
373
382
|
|
|
374
383
|
def create_pool(self, pool_class=pg_pool.Pool,
|
|
375
384
|
connection_class=pg_connection.Connection, **kwargs):
|
|
@@ -402,7 +411,7 @@ class ProxiedClusterTestCase(ClusterTestCase):
|
|
|
402
411
|
host = '127.0.0.1'
|
|
403
412
|
cls.proxy = fuzzer.TCPFuzzingProxy(
|
|
404
413
|
backend_host=host,
|
|
405
|
-
backend_port=conn_spec['port'],
|
|
414
|
+
backend_port=int(conn_spec['port']),
|
|
406
415
|
)
|
|
407
416
|
cls.proxy.start()
|
|
408
417
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
import asyncio
|
|
9
|
+
import contextlib
|
|
9
10
|
import socket
|
|
10
11
|
import threading
|
|
11
12
|
import typing
|
|
@@ -34,6 +35,7 @@ class TCPFuzzingProxy:
|
|
|
34
35
|
self.connections = {}
|
|
35
36
|
self.sock = None
|
|
36
37
|
self.listen_task = None
|
|
38
|
+
self.connection_factory = Connection
|
|
37
39
|
|
|
38
40
|
async def _wait(self, work):
|
|
39
41
|
work_task = asyncio.ensure_future(work)
|
|
@@ -98,11 +100,13 @@ class TCPFuzzingProxy:
|
|
|
98
100
|
try:
|
|
99
101
|
await self.listen_task
|
|
100
102
|
finally:
|
|
103
|
+
tasks = list(self.connections.values())
|
|
101
104
|
for c in list(self.connections):
|
|
102
105
|
c.close()
|
|
103
|
-
await asyncio.
|
|
106
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
104
107
|
if hasattr(self.loop, 'remove_reader'):
|
|
105
|
-
|
|
108
|
+
with contextlib.suppress(NotImplementedError):
|
|
109
|
+
self.loop.remove_reader(self.sock.fileno())
|
|
106
110
|
self.sock.close()
|
|
107
111
|
|
|
108
112
|
async def listen(self):
|
|
@@ -119,7 +123,7 @@ class TCPFuzzingProxy:
|
|
|
119
123
|
except StopServer:
|
|
120
124
|
break
|
|
121
125
|
|
|
122
|
-
conn =
|
|
126
|
+
conn = self.connection_factory(client_sock, backend_sock, self)
|
|
123
127
|
conn_task = self.loop.create_task(conn.handle())
|
|
124
128
|
self.connections[conn] = conn_task
|
|
125
129
|
|
|
@@ -141,13 +145,13 @@ class TCPFuzzingProxy:
|
|
|
141
145
|
self.restore_connectivity()
|
|
142
146
|
|
|
143
147
|
def _close_connection(self, connection):
|
|
144
|
-
conn_task = self.connections.
|
|
148
|
+
conn_task = self.connections.get(connection)
|
|
145
149
|
if conn_task is not None:
|
|
146
150
|
conn_task.cancel()
|
|
147
151
|
|
|
148
152
|
def close_all_connections(self):
|
|
149
153
|
for conn in list(self.connections):
|
|
150
|
-
self.loop.call_soon_threadsafe(
|
|
154
|
+
self.loop.call_soon_threadsafe(conn.close)
|
|
151
155
|
|
|
152
156
|
|
|
153
157
|
class Connection:
|
|
@@ -161,6 +165,27 @@ class Connection:
|
|
|
161
165
|
self.proxy_to_backend_task = None
|
|
162
166
|
self.proxy_from_backend_task = None
|
|
163
167
|
self.is_closed = False
|
|
168
|
+
self.client_reader = None
|
|
169
|
+
self.client_writer = None
|
|
170
|
+
|
|
171
|
+
async def prepare(self):
|
|
172
|
+
"""Optionally negotiate or inject faults before forwarding bytes."""
|
|
173
|
+
return True
|
|
174
|
+
|
|
175
|
+
async def use_client_stream(self, ssl_context=None):
|
|
176
|
+
"""Wrap the accepted socket in a stream, optionally with direct TLS."""
|
|
177
|
+
self.client_reader = asyncio.StreamReader()
|
|
178
|
+
protocol = asyncio.StreamReaderProtocol(self.client_reader)
|
|
179
|
+
transport, _ = await self.loop.connect_accepted_socket(
|
|
180
|
+
lambda: protocol, self.client_sock, ssl=ssl_context)
|
|
181
|
+
self.client_writer = asyncio.StreamWriter(
|
|
182
|
+
transport, protocol, self.client_reader, self.loop)
|
|
183
|
+
|
|
184
|
+
async def start_tls(self, ssl_context):
|
|
185
|
+
writer = self.client_writer
|
|
186
|
+
writer._transport = await self.loop.start_tls(
|
|
187
|
+
writer.transport, writer._protocol, ssl_context, server_side=True)
|
|
188
|
+
writer._protocol._over_ssl = True
|
|
164
189
|
|
|
165
190
|
def close(self):
|
|
166
191
|
if self.is_closed:
|
|
@@ -170,48 +195,72 @@ class Connection:
|
|
|
170
195
|
|
|
171
196
|
if self.proxy_to_backend_task is not None:
|
|
172
197
|
self.proxy_to_backend_task.cancel()
|
|
173
|
-
self.proxy_to_backend_task = None
|
|
174
198
|
|
|
175
199
|
if self.proxy_from_backend_task is not None:
|
|
176
200
|
self.proxy_from_backend_task.cancel()
|
|
177
|
-
self.proxy_from_backend_task = None
|
|
178
201
|
|
|
179
202
|
self.proxy._close_connection(self)
|
|
180
203
|
|
|
181
204
|
async def handle(self):
|
|
182
|
-
|
|
183
|
-
self.
|
|
205
|
+
try:
|
|
206
|
+
if not await self.prepare():
|
|
207
|
+
return
|
|
184
208
|
|
|
185
|
-
|
|
186
|
-
|
|
209
|
+
self.proxy_to_backend_task = asyncio.ensure_future(
|
|
210
|
+
self.proxy_to_backend())
|
|
211
|
+
self.proxy_from_backend_task = asyncio.ensure_future(
|
|
212
|
+
self.proxy_from_backend())
|
|
187
213
|
|
|
188
|
-
try:
|
|
189
214
|
await asyncio.wait(
|
|
190
215
|
[self.proxy_to_backend_task, self.proxy_from_backend_task],
|
|
191
216
|
return_when=asyncio.FIRST_COMPLETED)
|
|
192
217
|
|
|
218
|
+
except ConnectionError:
|
|
219
|
+
pass
|
|
220
|
+
|
|
193
221
|
finally:
|
|
222
|
+
# Relay completion can schedule close() while cleanup is awaiting
|
|
223
|
+
# its children. Do not let that callback cancel cleanup itself.
|
|
224
|
+
self.is_closed = True
|
|
194
225
|
if self.proxy_to_backend_task is not None:
|
|
195
226
|
self.proxy_to_backend_task.cancel()
|
|
196
227
|
|
|
197
228
|
if self.proxy_from_backend_task is not None:
|
|
198
229
|
self.proxy_from_backend_task.cancel()
|
|
199
230
|
|
|
231
|
+
await asyncio.gather(*(
|
|
232
|
+
task for task in (self.proxy_to_backend_task,
|
|
233
|
+
self.proxy_from_backend_task)
|
|
234
|
+
if task is not None
|
|
235
|
+
), return_exceptions=True)
|
|
236
|
+
|
|
200
237
|
# Asyncio fails to properly remove the readers and writers
|
|
201
238
|
# when the task doing recv() or send() is cancelled, so
|
|
202
239
|
# we must remove the readers and writers manually before
|
|
203
240
|
# closing the sockets.
|
|
204
|
-
self.
|
|
205
|
-
self.
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
241
|
+
sockets = [self.backend_sock]
|
|
242
|
+
if self.client_writer is None:
|
|
243
|
+
sockets.append(self.client_sock)
|
|
244
|
+
else:
|
|
245
|
+
self.client_writer.close()
|
|
246
|
+
with contextlib.suppress(ConnectionError):
|
|
247
|
+
await self.client_writer.wait_closed()
|
|
248
|
+
for sock in sockets:
|
|
249
|
+
if sock.fileno() < 0:
|
|
250
|
+
continue
|
|
251
|
+
# ProactorEventLoop has no reader/writer registration API.
|
|
252
|
+
with contextlib.suppress(NotImplementedError):
|
|
253
|
+
self.loop.remove_reader(sock.fileno())
|
|
254
|
+
self.loop.remove_writer(sock.fileno())
|
|
255
|
+
sock.close()
|
|
256
|
+
self.proxy.connections.pop(self, None)
|
|
211
257
|
|
|
212
258
|
async def _read(self, sock, n):
|
|
213
|
-
|
|
214
|
-
self.
|
|
259
|
+
if sock is self.client_sock and self.client_reader is not None:
|
|
260
|
+
read = self.client_reader.read(n)
|
|
261
|
+
else:
|
|
262
|
+
read = self.loop.sock_recv(sock, n)
|
|
263
|
+
read_task = asyncio.ensure_future(read)
|
|
215
264
|
conn_event_task = asyncio.ensure_future(
|
|
216
265
|
self.connectivity_loss.wait())
|
|
217
266
|
|
|
@@ -230,10 +279,16 @@ class Connection:
|
|
|
230
279
|
read_task.cancel()
|
|
231
280
|
if not conn_event_task.done():
|
|
232
281
|
conn_event_task.cancel()
|
|
282
|
+
await asyncio.gather(read_task, conn_event_task,
|
|
283
|
+
return_exceptions=True)
|
|
233
284
|
|
|
234
285
|
async def _write(self, sock, data):
|
|
235
|
-
|
|
236
|
-
self.
|
|
286
|
+
if sock is self.client_sock and self.client_writer is not None:
|
|
287
|
+
self.client_writer.write(data)
|
|
288
|
+
write = self.client_writer.drain()
|
|
289
|
+
else:
|
|
290
|
+
write = self.loop.sock_sendall(sock, data)
|
|
291
|
+
write_task = asyncio.ensure_future(write)
|
|
237
292
|
conn_event_task = asyncio.ensure_future(
|
|
238
293
|
self.connectivity_loss.wait())
|
|
239
294
|
|
|
@@ -252,6 +307,8 @@ class Connection:
|
|
|
252
307
|
write_task.cancel()
|
|
253
308
|
if not conn_event_task.done():
|
|
254
309
|
conn_event_task.cancel()
|
|
310
|
+
await asyncio.gather(write_task, conn_event_task,
|
|
311
|
+
return_exceptions=True)
|
|
255
312
|
|
|
256
313
|
async def proxy_to_backend(self):
|
|
257
314
|
buf = None
|