From 36df25aa35a2a6cd8303958cfbb0e5237df7b959 Mon Sep 17 00:00:00 2001 From: PrettyWood Date: Mon, 15 Mar 2021 22:33:21 +0100 Subject: [PATCH] build(deps): switch to sqlalchemy 1.4 --- databases/backends/aiopg.py | 35 ++++++++++++++++++++++++++-------- databases/backends/mysql.py | 35 ++++++++++++++++++++++++++-------- databases/backends/postgres.py | 5 +++-- databases/backends/sqlite.py | 35 ++++++++++++++++++++++++++-------- requirements.txt | 2 +- tests/test_databases.py | 2 +- 6 files changed, 86 insertions(+), 28 deletions(-) diff --git a/databases/backends/aiopg.py b/databases/backends/aiopg.py index 2fecb1b5..e8ccc99d 100644 --- a/databases/backends/aiopg.py +++ b/databases/backends/aiopg.py @@ -7,11 +7,11 @@ import aiopg from aiopg.sa.engine import APGCompiler_psycopg2 from sqlalchemy.dialects.postgresql.psycopg2 import PGDialect_psycopg2 +from sqlalchemy.engine.cursor import CursorResultMetaData from sqlalchemy.engine.interfaces import Dialect, ExecutionContext -from sqlalchemy.engine.result import ResultMetaData, RowProxy +from sqlalchemy.engine.result import Row from sqlalchemy.sql import ClauseElement from sqlalchemy.sql.ddl import DDLElement -from sqlalchemy.types import TypeEngine from databases.core import DatabaseURL from databases.interfaces import ConnectionBackend, DatabaseBackend, TransactionBackend @@ -119,9 +119,15 @@ async def fetch_all(self, query: ClauseElement) -> typing.List[typing.Mapping]: try: await cursor.execute(query, args) rows = await cursor.fetchall() - metadata = ResultMetaData(context, cursor.description) + metadata = CursorResultMetaData(context, cursor.description) return [ - RowProxy(metadata, row, metadata._processors, metadata._keymap) + Row( + metadata, + metadata._processors, + metadata._keymap, + Row._default_key_style, + row, + ) for row in rows ] finally: @@ -136,8 +142,14 @@ async def fetch_one(self, query: ClauseElement) -> typing.Optional[typing.Mappin row = await cursor.fetchone() if row is None: return None - metadata = ResultMetaData(context, cursor.description) - return RowProxy(metadata, row, metadata._processors, metadata._keymap) + metadata = CursorResultMetaData(context, cursor.description) + return Row( + metadata, + metadata._processors, + metadata._keymap, + Row._default_key_style, + row, + ) finally: cursor.close() @@ -169,9 +181,15 @@ async def iterate( cursor = await self._connection.cursor() try: await cursor.execute(query, args) - metadata = ResultMetaData(context, cursor.description) + metadata = CursorResultMetaData(context, cursor.description) async for row in cursor: - yield RowProxy(metadata, row, metadata._processors, metadata._keymap) + yield Row( + metadata, + metadata._processors, + metadata._keymap, + Row._default_key_style, + row, + ) finally: cursor.close() @@ -196,6 +214,7 @@ def _compile( compiled._result_columns, compiled._ordered_columns, compiled._textual_ordered_columns, + compiled._loose_column_name_matching, ) else: args = {} diff --git a/databases/backends/mysql.py b/databases/backends/mysql.py index b6476add..b615488d 100644 --- a/databases/backends/mysql.py +++ b/databases/backends/mysql.py @@ -5,11 +5,11 @@ import aiomysql from sqlalchemy.dialects.mysql import pymysql +from sqlalchemy.engine.cursor import CursorResultMetaData from sqlalchemy.engine.interfaces import Dialect, ExecutionContext -from sqlalchemy.engine.result import ResultMetaData, RowProxy +from sqlalchemy.engine.result import Row from sqlalchemy.sql import ClauseElement from sqlalchemy.sql.ddl import DDLElement -from sqlalchemy.types import TypeEngine from databases.core import LOG_EXTRA, DatabaseURL from databases.interfaces import ConnectionBackend, DatabaseBackend, TransactionBackend @@ -107,9 +107,15 @@ async def fetch_all(self, query: ClauseElement) -> typing.List[typing.Mapping]: try: await cursor.execute(query, args) rows = await cursor.fetchall() - metadata = ResultMetaData(context, cursor.description) + metadata = CursorResultMetaData(context, cursor.description) return [ - RowProxy(metadata, row, metadata._processors, metadata._keymap) + Row( + metadata, + metadata._processors, + metadata._keymap, + Row._default_key_style, + row, + ) for row in rows ] finally: @@ -124,8 +130,14 @@ async def fetch_one(self, query: ClauseElement) -> typing.Optional[typing.Mappin row = await cursor.fetchone() if row is None: return None - metadata = ResultMetaData(context, cursor.description) - return RowProxy(metadata, row, metadata._processors, metadata._keymap) + metadata = CursorResultMetaData(context, cursor.description) + return Row( + metadata, + metadata._processors, + metadata._keymap, + Row._default_key_style, + row, + ) finally: await cursor.close() @@ -159,9 +171,15 @@ async def iterate( cursor = await self._connection.cursor() try: await cursor.execute(query, args) - metadata = ResultMetaData(context, cursor.description) + metadata = CursorResultMetaData(context, cursor.description) async for row in cursor: - yield RowProxy(metadata, row, metadata._processors, metadata._keymap) + yield Row( + metadata, + metadata._processors, + metadata._keymap, + Row._default_key_style, + row, + ) finally: await cursor.close() @@ -186,6 +204,7 @@ def _compile( compiled._result_columns, compiled._ordered_columns, compiled._textual_ordered_columns, + compiled._loose_column_name_matching, ) else: args = {} diff --git a/databases/backends/postgres.py b/databases/backends/postgres.py index 8c1d75b1..c8340072 100644 --- a/databases/backends/postgres.py +++ b/databases/backends/postgres.py @@ -104,8 +104,9 @@ def __init__( self._dialect = dialect self._column_map, self._column_map_int, self._column_map_full = column_maps - def values(self) -> typing.ValuesView: - return self._row.values() + @property + def _mapping(self) -> asyncpg.Record: + return self._row def __getitem__(self, key: typing.Any) -> typing.Any: if len(self._column_map) == 0: # raw query diff --git a/databases/backends/sqlite.py b/databases/backends/sqlite.py index 28ceb6fb..a7e30cc4 100644 --- a/databases/backends/sqlite.py +++ b/databases/backends/sqlite.py @@ -4,11 +4,11 @@ import aiosqlite from sqlalchemy.dialects.sqlite import pysqlite +from sqlalchemy.engine.cursor import CursorResultMetaData from sqlalchemy.engine.interfaces import Dialect, ExecutionContext -from sqlalchemy.engine.result import ResultMetaData, RowProxy +from sqlalchemy.engine.result import Row from sqlalchemy.sql import ClauseElement from sqlalchemy.sql.ddl import DDLElement -from sqlalchemy.types import TypeEngine from databases.core import LOG_EXTRA, DatabaseURL from databases.interfaces import ConnectionBackend, DatabaseBackend, TransactionBackend @@ -92,9 +92,15 @@ async def fetch_all(self, query: ClauseElement) -> typing.List[typing.Mapping]: async with self._connection.execute(query, args) as cursor: rows = await cursor.fetchall() - metadata = ResultMetaData(context, cursor.description) + metadata = CursorResultMetaData(context, cursor.description) return [ - RowProxy(metadata, row, metadata._processors, metadata._keymap) + Row( + metadata, + metadata._processors, + metadata._keymap, + Row._default_key_style, + row, + ) for row in rows ] @@ -106,8 +112,14 @@ async def fetch_one(self, query: ClauseElement) -> typing.Optional[typing.Mappin row = await cursor.fetchone() if row is None: return None - metadata = ResultMetaData(context, cursor.description) - return RowProxy(metadata, row, metadata._processors, metadata._keymap) + metadata = CursorResultMetaData(context, cursor.description) + return Row( + metadata, + metadata._processors, + metadata._keymap, + Row._default_key_style, + row, + ) async def execute(self, query: ClauseElement) -> typing.Any: assert self._connection is not None, "Connection is not acquired" @@ -129,9 +141,15 @@ async def iterate( assert self._connection is not None, "Connection is not acquired" query, args, context = self._compile(query) async with self._connection.execute(query, args) as cursor: - metadata = ResultMetaData(context, cursor.description) + metadata = CursorResultMetaData(context, cursor.description) async for row in cursor: - yield RowProxy(metadata, row, metadata._processors, metadata._keymap) + yield Row( + metadata, + metadata._processors, + metadata._keymap, + Row._default_key_style, + row, + ) def transaction(self) -> TransactionBackend: return SQLiteTransaction(self) @@ -158,6 +176,7 @@ def _compile( compiled._result_columns, compiled._ordered_columns, compiled._textual_ordered_columns, + compiled._loose_column_name_matching, ) query_message = compiled.string.replace(" \n", " ").replace("\n", " ") diff --git a/requirements.txt b/requirements.txt index 1b06551a..9b957088 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ # Notes... # The JSONField tests require sqlalchemy 1.3+. Other cases work at lower versions. # The aiocontextvars package is only required as a backport for Python 3.6. -sqlalchemy>=1.3.0 +sqlalchemy>=1.4.0 aiocontextvars;python_version<"3.7" # Async database drivers diff --git a/tests/test_databases.py b/tests/test_databases.py index c7317688..f4fa83a6 100644 --- a/tests/test_databases.py +++ b/tests/test_databases.py @@ -337,7 +337,7 @@ async def test_result_values_allow_duplicate_names(database_url): row = await database.fetch_one(query=query) assert list(row.keys()) == ["id", "id"] - assert list(row.values()) == [1, 2] + assert list(row._mapping.values()) == [1, 2] @pytest.mark.parametrize("database_url", DATABASE_URLS)