diff --git a/sqly/__init__.py b/sqly/__init__.py index 645fc4d..f2a9d7e 100644 --- a/sqly/__init__.py +++ b/sqly/__init__.py @@ -1,3 +1,3 @@ from .dialect import Dialect from .query import Q -from .sql import SQL, ASQL +from .sql import ASQL, SQL diff --git a/sqly/__main__.py b/sqly/__main__.py index 5360969..13f5b47 100644 --- a/sqly/__main__.py +++ b/sqly/__main__.py @@ -85,8 +85,8 @@ def migrate(migration_key, database_url=None, dialect=None, dryrun=False): sys.exit(1) # force psycopg instead of asyncpg for migrations - if dialect == 'asyncpg': - dialect = 'psycopg' + if dialect == "asyncpg": + dialect = "psycopg" dialect = Dialect(dialect) adaptor = dialect.adaptor() diff --git a/sqly/dialect.py b/sqly/dialect.py index 83dc440..8d067d3 100644 --- a/sqly/dialect.py +++ b/sqly/dialect.py @@ -121,4 +121,4 @@ def must_async(self) -> bool: @property def can_async(self) -> bool: - return self in [self.ASYNCPG, self.PSYCOPG] \ No newline at end of file + return self in [self.ASYNCPG, self.PSYCOPG] diff --git a/sqly/lib.py b/sqly/lib.py index b271699..bb335d2 100644 --- a/sqly/lib.py +++ b/sqly/lib.py @@ -1,6 +1,7 @@ -import asyncio +import asyncio import inspect + def walk(iterator): """ Walk a nested iterator and yield items in a single stream. @@ -24,11 +25,12 @@ def run(f): f = asyncio.run(f) return f + def gen(g): async def unasync(g): return [item async for item in g] + if inspect.isasyncgen(g): return asyncio.run(unasync(g)) else: return list(g) - diff --git a/sqly/migration.py b/sqly/migration.py index 09cc049..6e2fd37 100644 --- a/sqly/migration.py +++ b/sqly/migration.py @@ -17,7 +17,7 @@ import networkx as nx import yaml -from . import queries, lib +from . import lib, queries from .dialect import Dialect from .query import Q from .sql import ASQL, SQL diff --git a/sqly/queries.py b/sqly/queries.py index baf541b..19785b8 100644 --- a/sqly/queries.py +++ b/sqly/queries.py @@ -106,7 +106,9 @@ def UPDATE(relation: str, fields: Iterable, filters: Iterable[str]) -> str: return " ".join(query) -def UPSERT(relation: str, fields: Iterable[str], key: Iterable[str], returning=False) -> str: +def UPSERT( + relation: str, fields: Iterable[str], key: Iterable[str], returning=False +) -> str: query = [ INSERT(relation, fields, returning=False), f"ON CONFLICT ({Q.fields(key)})", @@ -117,7 +119,6 @@ def UPSERT(relation: str, fields: Iterable[str], key: Iterable[str], returning=F return " ".join(query) - def DELETE(relation: str, filters: Iterable[str]) -> str: """ Build a DELETE query with the following form: diff --git a/sqly/sql.py b/sqly/sql.py index 287a86c..29d1424 100644 --- a/sqly/sql.py +++ b/sqly/sql.py @@ -3,10 +3,10 @@ from dataclasses import dataclass from typing import Any, Iterator, Mapping, Optional +from . import queries from .dialect import Dialect, ParamFormat from .lib import walk from .query import Q -from . import queries @dataclass diff --git a/tests/fixtures.py b/tests/fixtures.py index 44f22d5..454e465 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -36,7 +36,6 @@ ("sqlite", f"file://{PATH}/test.db"), ("psycopg", POSTGRESQL_URL), # ("asyncpg", POSTGRESQL_URL), - # ( # "mysql", # dedent( diff --git a/tests/test_database.py b/tests/test_database.py index 1a6f5c8..b570592 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -3,8 +3,8 @@ import pytest -from sqly.dialect import Dialect from sqly import lib +from sqly.dialect import Dialect from sqly.sql import SQL from tests import fixtures @@ -35,23 +35,33 @@ def test_execute_query_ok(dialect_name, database_url): connection = lib.run(adaptor.connect(database_url)) # execute a query that should have visible results - cursor = lib.run(connection.execute("CREATE TABLE widgets (id int, sku varchar)")) + cursor = lib.run( + connection.execute("CREATE TABLE widgets (id int, sku varchar)") + ) print(f"{connection=}") widget = {"id": 1, "sku": "COG-01"} # - the following table exists (and using the cursor to execute is fine) - lib.run(sql.execute(cursor, "INSERT INTO widgets (id, sku) VALUES (:id, :sku)", widget)) + lib.run( + sql.execute( + cursor, "INSERT INTO widgets (id, sku) VALUES (:id, :sku)", widget + ) + ) print(f"{connection=}") # - the row is in the table - row = lib.run(sql.select_one(cursor, "SELECT * from widgets WHERE id=:id", widget)) + row = lib.run( + sql.select_one(cursor, "SELECT * from widgets WHERE id=:id", widget) + ) assert row == widget # after we rollback, the table doesn't exist (NOTE: This might not work on all # databases, because not all have transactional DDL. ) lib.run(connection.rollback()) with pytest.raises(Exception): - row = lib.run(sql.select_one(connection, "SELECT * from widgets WHERE id=:id", widget)) + row = lib.run( + sql.select_one(connection, "SELECT * from widgets WHERE id=:id", widget) + ) # If the DDL wasn't transactional, the row still doesn't exist - is None assert row @@ -146,14 +156,18 @@ def test_cursor_as_connection(dialect_name, database_url): # connection = adaptor.connect(**conn_info) # else: connection = lib.run(adaptor.connect(database_url)) - cursor = lib.run(sql.execute(connection, "CREATE TABLE WIDGETS (id int, sku varchar)")) + cursor = lib.run( + sql.execute(connection, "CREATE TABLE WIDGETS (id int, sku varchar)") + ) lib.run(connection.commit()) with pytest.raises(Exception, match="foo"): lib.run(sql.execute(cursor, "INSERT INTO foo VALUES (1, 2)")) lib.run(connection.rollback()) widget = {"id": 1, "sku": "COG-01"} - cursor2 = lib.run(sql.execute(cursor, "INSERT INTO widgets VALUES (:id, :sku)", widget)) + cursor2 = lib.run( + sql.execute(cursor, "INSERT INTO widgets VALUES (:id, :sku)", widget) + ) assert cursor2 == cursor record = lib.run(sql.select_one(cursor, "SELECT * FROM widgets")) assert record == widget