Skip to content

Commit 8f87938

Browse files
gordthompsonrafiss
authored andcommitted
Add preliminary support for asyncpg
1 parent 3902559 commit 8f87938

18 files changed

+219
-54
lines changed

README.asyncpg.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
## asyncpg support (experimental)
2+
3+
Preliminary support for asyncpg has been added. The connection URI is of the form::
4+
5+
cockroachdb+asyncpg://root@localhost:26257/defaultdb
6+
7+
The example code at
8+
9+
https://docs.sqlalchemy.org/en/14/_modules/examples/asyncio/async_orm.html
10+
11+
runs without error and seems to be producing reasonable results.
12+
13+
There is also a customized version of the FastAPI SQL database tutorial for
14+
`cockroachdb+asyncpg` available at
15+
16+
https://github.com/gordthompson/fastapi-tutorial-cockroachdb-async
17+
18+
### Testing
19+
20+
asyncpg support has not yet been added to CI testing. There is at least one outstanding issue
21+
that needs to be resolved before the Alembic test suite can be successfully run on an asyncpg
22+
connection:
23+
24+
https://github.com/cockroachdb/cockroach/issues/71908
25+
26+
So, if you want to run all of the tests *except* the Alembic tests then invoke pytest
27+
using a command like
28+
29+
pytest --db=asyncpg --ignore-glob='*test_suite_alembic.py'
30+
31+
assuming that you have an entry in test.cfg that looks something like
32+
33+
[db]
34+
asyncpg=cockroachdb+asyncpg://root@localhost:26257/defaultdb

README.md

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,49 @@
22

33
## Prerequisites
44

5-
You must install either:
5+
For psycopg2 support you must install either:
66

77
* [psycopg2](https://pypi.org/project/psycopg2/), which has some
88
[prerequisites](https://www.psycopg.org/docs/install.html#prerequisites) of
99
its own.
1010

1111
* [psycopg2-binary](https://pypi.org/project/psycopg2-binary/)
1212

13-
The binary package is a practical choice for development and testing but in
14-
production it is advised to use the package built from sources.
13+
(The binary package is a practical choice for development and testing but in
14+
production it is advised to use the package built from sources.)
1515

16+
Or, for asyncpg support (experimental) you must install
17+
18+
* [asyncpg](https://pypi.org/project/asyncpg/)
19+
1620
## Install and usage
1721

1822
Use `pip` to install the latest version.
1923

2024
`pip install sqlalchemy-cockroachdb`
2125

2226
Use a `cockroachdb` connection string when creating the `Engine`. For example,
23-
to connect to an insecure, local CockroachDB cluster:
27+
to connect to an insecure, local CockroachDB cluster using psycopg2:
2428

2529
```
2630
from sqlalchemy import create_engine
2731
engine = create_engine('cockroachdb://root@localhost:26257/defaultdb?sslmode=disable')
2832
```
2933

34+
or
35+
36+
```
37+
from sqlalchemy import create_engine
38+
engine = create_engine('cockroachdb+psycopg2://root@localhost:26257/defaultdb?sslmode=disable')
39+
```
40+
41+
To connect using asyncpg:
42+
43+
```
44+
from sqlalchemy import create_async_engine
45+
engine = create_async_engine('cockroachdb+asyncpg://root@localhost:26257/defaultdb')
46+
```
47+
3048
## Changelog
3149

3250
See [CHANGES.md](https://github.com/cockroachdb/sqlalchemy-cockroachdb/blob/master/CHANGES.md)

dev-requirements.txt

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,64 @@
1-
appdirs==1.4.4
1+
backports.entry-points-selectable==1.1.1
22
# via virtualenv
3-
bleach==3.3.0
3+
bleach==4.1.0
44
# via readme-renderer
5-
certifi==2021.5.30
5+
certifi==2021.10.8
66
# via requests
7-
cffi==1.14.5
7+
cffi==1.15.0
88
# via cryptography
9-
chardet==4.0.0
9+
charset-normalizer==2.0.7
1010
# via requests
1111
colorama==0.4.4
1212
# via twine
13-
cryptography==3.4.7
13+
cryptography==35.0.0
1414
# via secretstorage
15-
distlib==0.3.2
15+
distlib==0.3.3
1616
# via virtualenv
17-
docutils==0.17.1
17+
docutils==0.18
1818
# via readme-renderer
19-
filelock==3.0.12
19+
filelock==3.3.2
2020
# via
2121
# tox
2222
# virtualenv
23-
idna==2.10
23+
idna==3.3
2424
# via requests
25-
importlib-metadata==4.5.0
25+
importlib-metadata==4.8.2
2626
# via
27+
# backports.entry-points-selectable
2728
# keyring
29+
# pluggy
30+
# tox
2831
# twine
29-
jeepney==0.6.0
32+
# virtualenv
33+
jeepney==0.7.1
3034
# via
3135
# keyring
3236
# secretstorage
33-
keyring==23.0.1
37+
keyring==23.2.1
3438
# via twine
35-
packaging==20.9
39+
packaging==21.2
3640
# via
3741
# bleach
3842
# tox
39-
pkginfo==1.7.0
43+
pkginfo==1.7.1
4044
# via twine
41-
pluggy==0.13.1
45+
platformdirs==2.4.0
46+
# via virtualenv
47+
pluggy==1.0.0
4248
# via tox
43-
py==1.10.0
49+
py==1.11.0
4450
# via tox
45-
pycparser==2.20
51+
pycparser==2.21
4652
# via cffi
47-
pygments==2.9.0
53+
pygments==2.10.0
4854
# via readme-renderer
4955
pyparsing==2.4.7
5056
# via packaging
51-
readme-renderer==29.0
57+
readme-renderer==30.0
5258
# via twine
5359
requests-toolbelt==0.9.1
5460
# via twine
55-
requests==2.25.1
61+
requests==2.26.0
5662
# via
5763
# requests-toolbelt
5864
# twine
@@ -63,22 +69,23 @@ secretstorage==3.3.1
6369
six==1.16.0
6470
# via
6571
# bleach
66-
# readme-renderer
6772
# tox
6873
# virtualenv
6974
toml==0.10.2
7075
# via tox
71-
tox==3.23.1
76+
tox==3.24.4
7277
# via -r dev-requirements.in
73-
tqdm==4.61.1
78+
tqdm==4.62.3
7479
# via twine
75-
twine==3.4.1
80+
twine==3.6.0
7681
# via -r dev-requirements.in
77-
urllib3==1.26.5
82+
typing-extensions==3.10.0.2
83+
# via importlib-metadata
84+
urllib3==1.26.7
7885
# via requests
79-
virtualenv==20.4.7
86+
virtualenv==20.10.0
8087
# via tox
8188
webencodings==0.5.1
8289
# via bleach
83-
zipp==3.4.1
90+
zipp==3.6.0
8491
# via importlib-metadata

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,9 @@
4040
zip_safe=False,
4141
entry_points={
4242
"sqlalchemy.dialects": [
43-
"cockroachdb.psycopg2 = sqlalchemy_cockroachdb.psycopg2:CockroachDBDialect_psycopg2",
4443
"cockroachdb = sqlalchemy_cockroachdb.psycopg2:CockroachDBDialect_psycopg2",
44+
"cockroachdb.psycopg2 = sqlalchemy_cockroachdb.psycopg2:CockroachDBDialect_psycopg2",
45+
"cockroachdb.asyncpg = sqlalchemy_cockroachdb.asyncpg:CockroachDBDialect_asyncpg",
4546
],
4647
},
4748
)

sqlalchemy_cockroachdb/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,8 @@
88
"sqlalchemy_cockroachdb.psycopg2",
99
"CockroachDBDialect_psycopg2",
1010
)
11+
_registry.register(
12+
"cockroachdb.asyncpg",
13+
"sqlalchemy_cockroachdb.asyncpg",
14+
"CockroachDBDialect_asyncpg",
15+
)

sqlalchemy_cockroachdb/asyncpg.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import json as _py_json
2+
3+
from sqlalchemy.dialects.postgresql.asyncpg import PGDialect_asyncpg
4+
from .base import CockroachDBDialect
5+
from .ddl_compiler import CockroachDDLCompiler
6+
from .stmt_compiler import CockroachIdentifierPreparer
7+
8+
9+
class CockroachDBDialect_asyncpg(PGDialect_asyncpg, CockroachDBDialect):
10+
driver = "asyncpg" # driver name
11+
preparer = CockroachIdentifierPreparer
12+
ddl_compiler = CockroachDDLCompiler
13+
14+
supports_statement_cache = True
15+
16+
async def setup_asyncpg_json_codec(self, conn):
17+
# https://github.com/cockroachdb/cockroach/issues/9990#issuecomment-579202144
18+
pass

sqlalchemy_cockroachdb/base.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from sqlalchemy.dialects.postgresql import INET
77
from sqlalchemy.dialects.postgresql import UUID
88
from sqlalchemy.ext.compiler import compiles
9+
from sqlalchemy.util import asbool
910
from sqlalchemy.util import warn
1011
import sqlalchemy.sql as sql
1112

@@ -96,16 +97,11 @@ class CockroachDBDialect(PGDialect):
9697
# Override connect so we can take disable_cockroachdb_telemetry as a connect_arg to sqlalchemy.
9798
def connect(
9899
self,
99-
dsn=None,
100-
connection_factory=None,
101-
cursor_factory=None,
102100
disable_cockroachdb_telemetry=False,
103101
**kwargs,
104102
):
105-
self.disable_cockroachdb_telemetry = disable_cockroachdb_telemetry
106-
return super().connect(
107-
dsn, connection_factory,
108-
cursor_factory, **kwargs)
103+
self.disable_cockroachdb_telemetry = asbool(disable_cockroachdb_telemetry)
104+
return super().connect(**kwargs)
109105

110106
def __init__(self, *args, **kwargs):
111107
if kwargs.get("use_native_hstore", False):

sqlalchemy_cockroachdb/psycopg2.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66

77
class CockroachDBDialect_psycopg2(PGDialect_psycopg2, CockroachDBDialect):
8-
name = "cockroachdb" # dialect name
98
driver = "psycopg2" # driver name
109
preparer = CockroachIdentifierPreparer
1110
ddl_compiler = CockroachDDLCompiler

sqlalchemy_cockroachdb/requirements.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,10 @@ class Requirements(SuiteRequirementsSQLA, SuiteRequirementsAlembic):
7070
lambda config: not config.db.dialect._is_v202plus,
7171
"older versions don't support this correctly.",
7272
)
73-
# TODO: enable after 20.2 beta comes out
74-
# check_constraint_reflection = \
75-
# exclusions.skip_if(lambda config: not config.db.dialect._is_v202plus,
76-
# "older versions don't support this correctly.")
73+
check_constraint_reflection = exclusions.skip_if(
74+
lambda config: not config.db.dialect._is_v202plus,
75+
"older versions don't support this correctly.",
76+
)
7777
cross_schema_fk_reflection = exclusions.closed()
7878
non_updating_cascade = exclusions.open()
7979
deferrable_fks = exclusions.closed()
@@ -155,10 +155,16 @@ class Requirements(SuiteRequirementsSQLA, SuiteRequirementsAlembic):
155155
implicitly_named_constraints = exclusions.open()
156156
supports_distinct_on = exclusions.open()
157157

158+
@property
159+
def sync_driver(self):
160+
return exclusions.skip_if(
161+
lambda config: config.db.dialect.is_async
162+
)
163+
158164
def get_isolation_levels(self, config):
159165
return {"default": "SERIALIZABLE", "supported": ["SERIALIZABLE"]}
160166

161-
# non-default requirements for Alembic test suite
167+
# non-default requirements for Alembic test suite
162168

163169
@property
164170
def autoincrement_on_composite_pk(self):

test-requirements.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
# generated test-requirements.txt) and run make update-requirements
66

77
alembic
8+
asyncpg
89
futures
910
mock
1011
more-itertools

0 commit comments

Comments
 (0)