asyncpg 0.28.0__tar.gz → 0.30.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.
Files changed (155) hide show
  1. asyncpg-0.30.0/.flake8 +5 -0
  2. {asyncpg-0.28.0 → asyncpg-0.30.0}/MANIFEST.in +1 -1
  3. {asyncpg-0.28.0 → asyncpg-0.30.0}/PKG-INFO +33 -12
  4. {asyncpg-0.28.0 → asyncpg-0.30.0}/README.rst +14 -7
  5. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/__init__.py +6 -1
  6. asyncpg-0.30.0/asyncpg/_asyncio_compat.py +94 -0
  7. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/_testbase/__init__.py +30 -14
  8. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/_version.py +5 -1
  9. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/cluster.py +49 -8
  10. asyncpg-0.30.0/asyncpg/compat.py +88 -0
  11. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/connect_utils.py +130 -66
  12. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/connection.py +405 -80
  13. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/cursor.py +15 -1
  14. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/exceptions/__init__.py +24 -6
  15. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/exceptions/_base.py +11 -1
  16. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/introspection.py +16 -6
  17. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/codecs/__init__.pxd +4 -0
  18. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/codecs/context.pyx +12 -0
  19. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/codecs/json.pyx +29 -1
  20. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/codecs/numeric.pyx +17 -2
  21. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/codecs/text.pyx +2 -2
  22. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/frb.pxd +1 -2
  23. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/hton.h +1 -1
  24. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/pgproto.c +40501 -30317
  25. asyncpg-0.30.0/asyncpg/pgproto/pgproto.pyi +13 -0
  26. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/types.py +3 -1
  27. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/uuid.pyx +24 -10
  28. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pool.py +109 -52
  29. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/prepared_stmt.py +29 -3
  30. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/__init__.py +2 -0
  31. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/codecs/base.pxd +3 -0
  32. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/codecs/base.pyx +63 -29
  33. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/coreproto.pxd +10 -12
  34. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/coreproto.pyx +115 -19
  35. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/prepared_stmt.pxd +1 -0
  36. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/prepared_stmt.pyx +8 -1
  37. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/protocol.c +88456 -68478
  38. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/protocol.pxd +0 -3
  39. asyncpg-0.30.0/asyncpg/protocol/protocol.pyi +300 -0
  40. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/protocol.pyx +57 -19
  41. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/record/recordobj.c +2 -2
  42. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/settings.pxd +1 -1
  43. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/settings.pyx +6 -4
  44. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/serverversion.py +17 -7
  45. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/types.py +74 -28
  46. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/utils.py +8 -1
  47. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg.egg-info/PKG-INFO +33 -12
  48. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg.egg-info/SOURCES.txt +5 -0
  49. asyncpg-0.30.0/asyncpg.egg-info/requires.txt +31 -0
  50. {asyncpg-0.28.0 → asyncpg-0.30.0}/docs/api/index.rst +1 -2
  51. {asyncpg-0.28.0 → asyncpg-0.30.0}/docs/conf.py +1 -2
  52. {asyncpg-0.28.0 → asyncpg-0.30.0}/docs/index.rst +2 -2
  53. asyncpg-0.30.0/docs/installation.rst +74 -0
  54. {asyncpg-0.28.0 → asyncpg-0.30.0}/docs/usage.rst +57 -13
  55. {asyncpg-0.28.0 → asyncpg-0.30.0}/pyproject.toml +54 -10
  56. {asyncpg-0.28.0 → asyncpg-0.30.0}/setup.py +4 -4
  57. asyncpg-0.30.0/tests/certs/ca.cert.pem +35 -0
  58. asyncpg-0.30.0/tests/certs/ca.key.pem +51 -0
  59. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/certs/gen.py +15 -1
  60. asyncpg-0.30.0/tests/certs/server.cert.pem +39 -0
  61. asyncpg-0.30.0/tests/certs/server.crl.pem +19 -0
  62. asyncpg-0.30.0/tests/certs/server.key.pem +51 -0
  63. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/test__sourcecode.py +32 -1
  64. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/test_adversity.py +19 -4
  65. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/test_codecs.py +73 -28
  66. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/test_connect.py +402 -50
  67. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/test_copy.py +38 -5
  68. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/test_cursor.py +15 -5
  69. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/test_exceptions.py +13 -7
  70. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/test_execute.py +39 -0
  71. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/test_introspection.py +7 -1
  72. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/test_listeners.py +2 -4
  73. asyncpg-0.30.0/tests/test_logging.py +51 -0
  74. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/test_pool.py +102 -2
  75. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/test_prepare.py +14 -0
  76. asyncpg-0.28.0/.flake8 +0 -3
  77. asyncpg-0.28.0/asyncpg/compat.py +0 -65
  78. asyncpg-0.28.0/asyncpg.egg-info/requires.txt +0 -14
  79. asyncpg-0.28.0/docs/installation.rst +0 -59
  80. asyncpg-0.28.0/tests/certs/ca.cert.pem +0 -35
  81. asyncpg-0.28.0/tests/certs/ca.key.pem +0 -51
  82. asyncpg-0.28.0/tests/certs/server.cert.pem +0 -39
  83. asyncpg-0.28.0/tests/certs/server.key.pem +0 -51
  84. {asyncpg-0.28.0 → asyncpg-0.30.0}/AUTHORS +0 -0
  85. {asyncpg-0.28.0 → asyncpg-0.30.0}/LICENSE +0 -0
  86. {asyncpg-0.28.0 → asyncpg-0.30.0}/Makefile +0 -0
  87. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/_testbase/fuzzer.py +0 -0
  88. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/connresource.py +0 -0
  89. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/__init__.pxd +0 -0
  90. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/__init__.py +0 -0
  91. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/buffer.pxd +0 -0
  92. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/buffer.pyx +0 -0
  93. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/codecs/bits.pyx +0 -0
  94. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/codecs/bytea.pyx +0 -0
  95. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/codecs/datetime.pyx +0 -0
  96. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/codecs/float.pyx +0 -0
  97. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/codecs/geometry.pyx +0 -0
  98. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/codecs/hstore.pyx +0 -0
  99. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/codecs/int.pyx +0 -0
  100. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/codecs/jsonpath.pyx +0 -0
  101. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/codecs/misc.pyx +0 -0
  102. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/codecs/network.pyx +0 -0
  103. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/codecs/pg_snapshot.pyx +0 -0
  104. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/codecs/tid.pyx +0 -0
  105. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/codecs/uuid.pyx +0 -0
  106. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/consts.pxi +0 -0
  107. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/cpythonx.pxd +0 -0
  108. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/debug.h +0 -0
  109. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/debug.pxd +0 -0
  110. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/frb.pyx +0 -0
  111. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/hton.pxd +0 -0
  112. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/pgproto.pxd +0 -0
  113. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/pgproto.pyx +0 -0
  114. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/tohex.h +0 -0
  115. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/pgproto/tohex.pxd +0 -0
  116. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/codecs/__init__.py +0 -0
  117. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/codecs/array.pyx +0 -0
  118. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/codecs/pgproto.pyx +0 -0
  119. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/codecs/range.pyx +0 -0
  120. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/codecs/record.pyx +0 -0
  121. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/codecs/textutils.pyx +0 -0
  122. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/consts.pxi +0 -0
  123. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/cpythonx.pxd +0 -0
  124. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/encodings.pyx +0 -0
  125. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/pgtypes.pxi +0 -0
  126. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/record/__init__.pxd +0 -0
  127. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/record/recordobj.h +0 -0
  128. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/scram.pxd +0 -0
  129. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/protocol/scram.pyx +0 -0
  130. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg/transaction.py +0 -0
  131. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg.egg-info/dependency_links.txt +0 -0
  132. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg.egg-info/not-zip-safe +0 -0
  133. {asyncpg-0.28.0 → asyncpg-0.30.0}/asyncpg.egg-info/top_level.txt +0 -0
  134. {asyncpg-0.28.0 → asyncpg-0.30.0}/docs/Makefile +0 -0
  135. {asyncpg-0.28.0 → asyncpg-0.30.0}/docs/_static/theme_overrides.css +0 -0
  136. {asyncpg-0.28.0 → asyncpg-0.30.0}/docs/faq.rst +0 -0
  137. {asyncpg-0.28.0 → asyncpg-0.30.0}/performance.png +0 -0
  138. {asyncpg-0.28.0 → asyncpg-0.30.0}/setup.cfg +0 -0
  139. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/__init__.py +0 -0
  140. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/certs/ca.crl.pem +0 -0
  141. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/certs/client.cert.pem +0 -0
  142. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/certs/client.csr.pem +0 -0
  143. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/certs/client.key.pem +0 -0
  144. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/certs/client.key.protected.pem +0 -0
  145. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/certs/client_ca.cert.pem +0 -0
  146. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/certs/client_ca.key.pem +0 -0
  147. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/test__environment.py +0 -0
  148. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/test_cache_invalidation.py +0 -0
  149. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/test_cancellation.py +0 -0
  150. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/test_record.py +0 -0
  151. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/test_test.py +0 -0
  152. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/test_timeout.py +0 -0
  153. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/test_transaction.py +0 -0
  154. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/test_types.py +0 -0
  155. {asyncpg-0.28.0 → asyncpg-0.30.0}/tests/test_utils.py +0 -0
asyncpg-0.30.0/.flake8 ADDED
@@ -0,0 +1,5 @@
1
+ [flake8]
2
+ select = C90,E,F,W,Y0
3
+ ignore = E402,E731,W503,W504,E252
4
+ exclude = .git,__pycache__,build,dist,.eggs,.github,.local,.venv*,.tox
5
+ per-file-ignores = *.pyi: F401,F403,F405,F811,E127,E128,E203,E266,E301,E302,E305,E501,E701,E704,E741,B303,W503,W504
@@ -1,5 +1,5 @@
1
1
  recursive-include docs *.py *.rst Makefile *.css
2
2
  recursive-include examples *.py
3
3
  recursive-include tests *.py *.pem
4
- recursive-include asyncpg *.pyx *.pxd *.pxi *.py *.c *.h
4
+ recursive-include asyncpg *.pyx *.pxd *.pxi *.py *.pyi *.c *.h
5
5
  include LICENSE README.rst Makefile performance.png .flake8
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: asyncpg
3
- Version: 0.28.0
3
+ Version: 0.30.0
4
4
  Summary: An asyncio PostgreSQL driver
5
5
  Author-email: MagicStack Inc <hello@magic.io>
6
6
  License: Apache License, Version 2.0
@@ -14,19 +14,33 @@ Classifier: Operating System :: POSIX
14
14
  Classifier: Operating System :: MacOS :: MacOS X
15
15
  Classifier: Operating System :: Microsoft :: Windows
16
16
  Classifier: Programming Language :: Python :: 3 :: Only
17
- Classifier: Programming Language :: Python :: 3.7
18
17
  Classifier: Programming Language :: Python :: 3.8
19
18
  Classifier: Programming Language :: Python :: 3.9
20
19
  Classifier: Programming Language :: Python :: 3.10
21
20
  Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
22
  Classifier: Programming Language :: Python :: Implementation :: CPython
23
23
  Classifier: Topic :: Database :: Front-Ends
24
- Requires-Python: >=3.7.0
24
+ Requires-Python: >=3.8.0
25
25
  Description-Content-Type: text/x-rst
26
- Provides-Extra: test
27
- Provides-Extra: docs
28
26
  License-File: LICENSE
29
27
  License-File: AUTHORS
28
+ Requires-Dist: async_timeout>=4.0.3; python_version < "3.11.0"
29
+ Provides-Extra: gssauth
30
+ Requires-Dist: gssapi; platform_system != "Windows" and extra == "gssauth"
31
+ Requires-Dist: sspilib; platform_system == "Windows" and extra == "gssauth"
32
+ Provides-Extra: test
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"
30
44
 
31
45
  asyncpg -- A fast PostgreSQL Database Client Library for Python/asyncio
32
46
  =======================================================================
@@ -43,9 +57,10 @@ of PostgreSQL server binary protocol for use with Python's ``asyncio``
43
57
  framework. You can read more about asyncpg in an introductory
44
58
  `blog post <http://magic.io/blog/asyncpg-1m-rows-from-postgres-to-python/>`_.
45
59
 
46
- asyncpg requires Python 3.7 or later and is supported for PostgreSQL
47
- versions 9.5 to 15. Older PostgreSQL versions or other databases implementing
48
- the PostgreSQL protocol *may* work, but are not being actively tested.
60
+ asyncpg requires Python 3.8 or later and is supported for PostgreSQL
61
+ versions 9.5 to 17. Other PostgreSQL versions or other databases
62
+ implementing the PostgreSQL protocol *may* work, but are not being
63
+ actively tested.
49
64
 
50
65
 
51
66
  Documentation
@@ -88,11 +103,18 @@ This enables asyncpg to have easy-to-use support for:
88
103
  Installation
89
104
  ------------
90
105
 
91
- asyncpg is available on PyPI and has no dependencies.
92
- Use pip to install::
106
+ asyncpg is available on PyPI. When not using GSSAPI/SSPI authentication it
107
+ has no dependencies. Use pip to install::
93
108
 
94
109
  $ pip install asyncpg
95
110
 
111
+ If you need GSSAPI/SSPI authentication, use::
112
+
113
+ $ pip install 'asyncpg[gssauth]'
114
+
115
+ For more details, please `see the documentation
116
+ <https://magicstack.github.io/asyncpg/current/installation.html>`_.
117
+
96
118
 
97
119
  Basic Usage
98
120
  -----------
@@ -111,8 +133,7 @@ Basic Usage
111
133
  )
112
134
  await conn.close()
113
135
 
114
- loop = asyncio.get_event_loop()
115
- loop.run_until_complete(run())
136
+ asyncio.run(run())
116
137
 
117
138
 
118
139
  License
@@ -13,9 +13,10 @@ 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.7 or later and is supported for PostgreSQL
17
- versions 9.5 to 15. Older PostgreSQL versions or other databases implementing
18
- the PostgreSQL protocol *may* work, but are not being actively tested.
16
+ asyncpg requires Python 3.8 or later and is supported for PostgreSQL
17
+ versions 9.5 to 17. Other PostgreSQL versions or other databases
18
+ implementing the PostgreSQL protocol *may* work, but are not being
19
+ actively tested.
19
20
 
20
21
 
21
22
  Documentation
@@ -58,11 +59,18 @@ This enables asyncpg to have easy-to-use support for:
58
59
  Installation
59
60
  ------------
60
61
 
61
- asyncpg is available on PyPI and has no dependencies.
62
- Use pip to install::
62
+ asyncpg is available on PyPI. When not using GSSAPI/SSPI authentication it
63
+ has no dependencies. Use pip to install::
63
64
 
64
65
  $ pip install asyncpg
65
66
 
67
+ If you need GSSAPI/SSPI authentication, use::
68
+
69
+ $ pip install 'asyncpg[gssauth]'
70
+
71
+ For more details, please `see the documentation
72
+ <https://magicstack.github.io/asyncpg/current/installation.html>`_.
73
+
66
74
 
67
75
  Basic Usage
68
76
  -----------
@@ -81,8 +89,7 @@ Basic Usage
81
89
  )
82
90
  await conn.close()
83
91
 
84
- loop = asyncio.get_event_loop()
85
- loop.run_until_complete(run())
92
+ asyncio.run(run())
86
93
 
87
94
 
88
95
  License
@@ -4,6 +4,7 @@
4
4
  # This module is part of asyncpg and is released under
5
5
  # the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0
6
6
 
7
+ from __future__ import annotations
7
8
 
8
9
  from .connection import connect, Connection # NOQA
9
10
  from .exceptions import * # NOQA
@@ -14,6 +15,10 @@ from .types import * # NOQA
14
15
 
15
16
  from ._version import __version__ # NOQA
16
17
 
18
+ from . import exceptions
17
19
 
18
- __all__ = ('connect', 'create_pool', 'Pool', 'Record', 'Connection')
20
+
21
+ __all__: tuple[str, ...] = (
22
+ 'connect', 'create_pool', 'Pool', 'Record', 'Connection'
23
+ )
19
24
  __all__ += exceptions.__all__ # NOQA
@@ -0,0 +1,94 @@
1
+ # Backports from Python/Lib/asyncio for older Pythons
2
+ #
3
+ # Copyright (c) 2001-2023 Python Software Foundation; All Rights Reserved
4
+ #
5
+ # SPDX-License-Identifier: PSF-2.0
6
+
7
+ from __future__ import annotations
8
+
9
+ import asyncio
10
+ import functools
11
+ import sys
12
+ import typing
13
+
14
+ if typing.TYPE_CHECKING:
15
+ from . import compat
16
+
17
+ if sys.version_info < (3, 11):
18
+ from async_timeout import timeout as timeout_ctx
19
+ else:
20
+ from asyncio import timeout as timeout_ctx
21
+
22
+ _T = typing.TypeVar('_T')
23
+
24
+
25
+ async def wait_for(fut: compat.Awaitable[_T], timeout: float | None) -> _T:
26
+ """Wait for the single Future or coroutine to complete, with timeout.
27
+
28
+ Coroutine will be wrapped in Task.
29
+
30
+ Returns result of the Future or coroutine. When a timeout occurs,
31
+ it cancels the task and raises TimeoutError. To avoid the task
32
+ cancellation, wrap it in shield().
33
+
34
+ If the wait is cancelled, the task is also cancelled.
35
+
36
+ If the task supresses the cancellation and returns a value instead,
37
+ that value is returned.
38
+
39
+ This function is a coroutine.
40
+ """
41
+ # The special case for timeout <= 0 is for the following case:
42
+ #
43
+ # async def test_waitfor():
44
+ # func_started = False
45
+ #
46
+ # async def func():
47
+ # nonlocal func_started
48
+ # func_started = True
49
+ #
50
+ # try:
51
+ # await asyncio.wait_for(func(), 0)
52
+ # except asyncio.TimeoutError:
53
+ # assert not func_started
54
+ # else:
55
+ # assert False
56
+ #
57
+ # asyncio.run(test_waitfor())
58
+
59
+ if timeout is not None and timeout <= 0:
60
+ fut = asyncio.ensure_future(fut)
61
+
62
+ if fut.done():
63
+ return fut.result()
64
+
65
+ await _cancel_and_wait(fut)
66
+ try:
67
+ return fut.result()
68
+ except asyncio.CancelledError as exc:
69
+ raise TimeoutError from exc
70
+
71
+ async with timeout_ctx(timeout):
72
+ return await fut
73
+
74
+
75
+ async def _cancel_and_wait(fut: asyncio.Future[_T]) -> None:
76
+ """Cancel the *fut* future or task and wait until it completes."""
77
+
78
+ loop = asyncio.get_running_loop()
79
+ waiter = loop.create_future()
80
+ cb = functools.partial(_release_waiter, waiter)
81
+ fut.add_done_callback(cb)
82
+
83
+ try:
84
+ fut.cancel()
85
+ # We cannot wait on *fut* directly to make
86
+ # sure _cancel_and_wait itself is reliably cancellable.
87
+ await waiter
88
+ finally:
89
+ fut.remove_done_callback(cb)
90
+
91
+
92
+ def _release_waiter(waiter: asyncio.Future[typing.Any], *args: object) -> None:
93
+ if not waiter.done():
94
+ waiter.set_result(None)
@@ -117,10 +117,22 @@ class TestCase(unittest.TestCase, metaclass=TestCaseMeta):
117
117
  self.__unhandled_exceptions = []
118
118
 
119
119
  def tearDown(self):
120
- if self.__unhandled_exceptions:
120
+ excs = []
121
+ for exc in self.__unhandled_exceptions:
122
+ if isinstance(exc, ConnectionResetError):
123
+ texc = traceback.TracebackException.from_exception(
124
+ exc, lookup_lines=False)
125
+ if texc.stack[-1].name == "_call_connection_lost":
126
+ # On Windows calling socket.shutdown may raise
127
+ # ConnectionResetError, which happens in the
128
+ # finally block of _call_connection_lost.
129
+ continue
130
+ excs.append(exc)
131
+
132
+ if excs:
121
133
  formatted = []
122
134
 
123
- for i, context in enumerate(self.__unhandled_exceptions):
135
+ for i, context in enumerate(excs):
124
136
  formatted.append(self._format_loop_exception(context, i + 1))
125
137
 
126
138
  self.fail(
@@ -214,13 +226,6 @@ def _init_cluster(ClusterCls, cluster_kwargs, initdb_options=None):
214
226
  return cluster
215
227
 
216
228
 
217
- def _start_cluster(ClusterCls, cluster_kwargs, server_settings,
218
- initdb_options=None):
219
- cluster = _init_cluster(ClusterCls, cluster_kwargs, initdb_options)
220
- cluster.start(port='dynamic', server_settings=server_settings)
221
- return cluster
222
-
223
-
224
229
  def _get_initdb_options(initdb_options=None):
225
230
  if not initdb_options:
226
231
  initdb_options = {}
@@ -244,8 +249,12 @@ def _init_default_cluster(initdb_options=None):
244
249
  _default_cluster = pg_cluster.RunningCluster()
245
250
  else:
246
251
  _default_cluster = _init_cluster(
247
- pg_cluster.TempCluster, cluster_kwargs={},
248
- initdb_options=_get_initdb_options(initdb_options))
252
+ pg_cluster.TempCluster,
253
+ cluster_kwargs={
254
+ "data_dir_suffix": ".apgtest",
255
+ },
256
+ initdb_options=_get_initdb_options(initdb_options),
257
+ )
249
258
 
250
259
  return _default_cluster
251
260
 
@@ -262,6 +271,7 @@ def create_pool(dsn=None, *,
262
271
  max_size=10,
263
272
  max_queries=50000,
264
273
  max_inactive_connection_lifetime=60.0,
274
+ connect=None,
265
275
  setup=None,
266
276
  init=None,
267
277
  loop=None,
@@ -271,12 +281,18 @@ def create_pool(dsn=None, *,
271
281
  **connect_kwargs):
272
282
  return pool_class(
273
283
  dsn,
274
- min_size=min_size, max_size=max_size,
275
- max_queries=max_queries, loop=loop, setup=setup, init=init,
284
+ min_size=min_size,
285
+ max_size=max_size,
286
+ max_queries=max_queries,
287
+ loop=loop,
288
+ connect=connect,
289
+ setup=setup,
290
+ init=init,
276
291
  max_inactive_connection_lifetime=max_inactive_connection_lifetime,
277
292
  connection_class=connection_class,
278
293
  record_class=record_class,
279
- **connect_kwargs)
294
+ **connect_kwargs,
295
+ )
280
296
 
281
297
 
282
298
  class ClusterTestCase(TestCase):
@@ -10,4 +10,8 @@
10
10
  # supported platforms, publish the packages on PyPI, merge the PR
11
11
  # to the target branch, create a Git tag pointing to the commit.
12
12
 
13
- __version__ = '0.28.0'
13
+ from __future__ import annotations
14
+
15
+ import typing
16
+
17
+ __version__: typing.Final = '0.30.0'
@@ -9,9 +9,11 @@ import asyncio
9
9
  import os
10
10
  import os.path
11
11
  import platform
12
+ import random
12
13
  import re
13
14
  import shutil
14
15
  import socket
16
+ import string
15
17
  import subprocess
16
18
  import sys
17
19
  import tempfile
@@ -45,6 +47,29 @@ def find_available_port():
45
47
  sock.close()
46
48
 
47
49
 
50
+ def _world_readable_mkdtemp(suffix=None, prefix=None, dir=None):
51
+ name = "".join(random.choices(string.ascii_lowercase, k=8))
52
+ if dir is None:
53
+ dir = tempfile.gettempdir()
54
+ if prefix is None:
55
+ prefix = tempfile.gettempprefix()
56
+ if suffix is None:
57
+ suffix = ""
58
+ fn = os.path.join(dir, prefix + name + suffix)
59
+ os.mkdir(fn, 0o755)
60
+ return fn
61
+
62
+
63
+ def _mkdtemp(suffix=None, prefix=None, dir=None):
64
+ if _system == 'Windows' and os.environ.get("GITHUB_ACTIONS"):
65
+ # Due to mitigations introduced in python/cpython#118486
66
+ # when Python runs in a session created via an SSH connection
67
+ # tempfile.mkdtemp creates directories that are not accessible.
68
+ return _world_readable_mkdtemp(suffix, prefix, dir)
69
+ else:
70
+ return tempfile.mkdtemp(suffix, prefix, dir)
71
+
72
+
48
73
  class ClusterError(Exception):
49
74
  pass
50
75
 
@@ -122,9 +147,13 @@ class Cluster:
122
147
  else:
123
148
  extra_args = []
124
149
 
150
+ os.makedirs(self._data_dir, exist_ok=True)
125
151
  process = subprocess.run(
126
152
  [self._pg_ctl, 'init', '-D', self._data_dir] + extra_args,
127
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
153
+ stdout=subprocess.PIPE,
154
+ stderr=subprocess.STDOUT,
155
+ cwd=self._data_dir,
156
+ )
128
157
 
129
158
  output = process.stdout
130
159
 
@@ -199,7 +228,10 @@ class Cluster:
199
228
  process = subprocess.run(
200
229
  [self._pg_ctl, 'start', '-D', self._data_dir,
201
230
  '-o', ' '.join(extra_args)],
202
- stdout=stdout, stderr=subprocess.STDOUT)
231
+ stdout=stdout,
232
+ stderr=subprocess.STDOUT,
233
+ cwd=self._data_dir,
234
+ )
203
235
 
204
236
  if process.returncode != 0:
205
237
  if process.stderr:
@@ -218,7 +250,10 @@ class Cluster:
218
250
  self._daemon_process = \
219
251
  subprocess.Popen(
220
252
  [self._postgres, '-D', self._data_dir, *extra_args],
221
- stdout=stdout, stderr=subprocess.STDOUT)
253
+ stdout=stdout,
254
+ stderr=subprocess.STDOUT,
255
+ cwd=self._data_dir,
256
+ )
222
257
 
223
258
  self._daemon_pid = self._daemon_process.pid
224
259
 
@@ -232,7 +267,10 @@ class Cluster:
232
267
 
233
268
  process = subprocess.run(
234
269
  [self._pg_ctl, 'reload', '-D', self._data_dir],
235
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
270
+ stdout=subprocess.PIPE,
271
+ stderr=subprocess.PIPE,
272
+ cwd=self._data_dir,
273
+ )
236
274
 
237
275
  stderr = process.stderr
238
276
 
@@ -245,7 +283,10 @@ class Cluster:
245
283
  process = subprocess.run(
246
284
  [self._pg_ctl, 'stop', '-D', self._data_dir, '-t', str(wait),
247
285
  '-m', 'fast'],
248
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
286
+ stdout=subprocess.PIPE,
287
+ stderr=subprocess.PIPE,
288
+ cwd=self._data_dir,
289
+ )
249
290
 
250
291
  stderr = process.stderr
251
292
 
@@ -583,9 +624,9 @@ class TempCluster(Cluster):
583
624
  def __init__(self, *,
584
625
  data_dir_suffix=None, data_dir_prefix=None,
585
626
  data_dir_parent=None, pg_config_path=None):
586
- self._data_dir = tempfile.mkdtemp(suffix=data_dir_suffix,
587
- prefix=data_dir_prefix,
588
- dir=data_dir_parent)
627
+ self._data_dir = _mkdtemp(suffix=data_dir_suffix,
628
+ prefix=data_dir_prefix,
629
+ dir=data_dir_parent)
589
630
  super().__init__(self._data_dir, pg_config_path=pg_config_path)
590
631
 
591
632
 
@@ -0,0 +1,88 @@
1
+ # Copyright (C) 2016-present the asyncpg authors and contributors
2
+ # <see AUTHORS file>
3
+ #
4
+ # This module is part of asyncpg and is released under
5
+ # the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ from __future__ import annotations
8
+
9
+ import enum
10
+ import pathlib
11
+ import platform
12
+ import typing
13
+ import sys
14
+
15
+ if typing.TYPE_CHECKING:
16
+ import asyncio
17
+
18
+ SYSTEM: typing.Final = platform.uname().system
19
+
20
+
21
+ if sys.platform == 'win32':
22
+ import ctypes.wintypes
23
+
24
+ CSIDL_APPDATA: typing.Final = 0x001a
25
+
26
+ def get_pg_home_directory() -> pathlib.Path | None:
27
+ # We cannot simply use expanduser() as that returns the user's
28
+ # home directory, whereas Postgres stores its config in
29
+ # %AppData% on Windows.
30
+ buf = ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH)
31
+ r = ctypes.windll.shell32.SHGetFolderPathW(0, CSIDL_APPDATA, 0, 0, buf)
32
+ if r:
33
+ return None
34
+ else:
35
+ return pathlib.Path(buf.value) / 'postgresql'
36
+
37
+ else:
38
+ def get_pg_home_directory() -> pathlib.Path | None:
39
+ try:
40
+ return pathlib.Path.home()
41
+ except (RuntimeError, KeyError):
42
+ return None
43
+
44
+
45
+ async def wait_closed(stream: asyncio.StreamWriter) -> None:
46
+ # Not all asyncio versions have StreamWriter.wait_closed().
47
+ if hasattr(stream, 'wait_closed'):
48
+ try:
49
+ await stream.wait_closed()
50
+ except ConnectionResetError:
51
+ # On Windows wait_closed() sometimes propagates
52
+ # ConnectionResetError which is totally unnecessary.
53
+ pass
54
+
55
+
56
+ if sys.version_info < (3, 12):
57
+ def markcoroutinefunction(c): # type: ignore
58
+ pass
59
+ else:
60
+ from inspect import markcoroutinefunction # noqa: F401
61
+
62
+
63
+ if sys.version_info < (3, 12):
64
+ from ._asyncio_compat import wait_for as wait_for # noqa: F401
65
+ else:
66
+ from asyncio import wait_for as wait_for # noqa: F401
67
+
68
+
69
+ if sys.version_info < (3, 11):
70
+ from ._asyncio_compat import timeout_ctx as timeout # noqa: F401
71
+ else:
72
+ from asyncio import timeout as timeout # noqa: F401
73
+
74
+ if sys.version_info < (3, 9):
75
+ from typing import ( # noqa: F401
76
+ Awaitable as Awaitable,
77
+ )
78
+ else:
79
+ from collections.abc import ( # noqa: F401
80
+ Awaitable as Awaitable,
81
+ )
82
+
83
+ if sys.version_info < (3, 11):
84
+ class StrEnum(str, enum.Enum):
85
+ __str__ = str.__str__
86
+ __repr__ = enum.Enum.__repr__
87
+ else:
88
+ from enum import StrEnum as StrEnum # noqa: F401