diff --git a/ibis/backends/duckdb/__init__.py b/ibis/backends/duckdb/__init__.py index 7f8e1337aa57..4018f6149d92 100644 --- a/ibis/backends/duckdb/__init__.py +++ b/ibis/backends/duckdb/__init__.py @@ -1036,13 +1036,15 @@ def read_sqlite( >>> import ibis >>> import sqlite3 >>> ibis.options.interactive = True - >>> with sqlite3.connect("/tmp/sqlite.db") as con: + >>> con = sqlite3.connect("/tmp/sqlite.db") + >>> with con: ... con.execute("DROP TABLE IF EXISTS t") # doctest: +ELLIPSIS ... con.execute("CREATE TABLE t (a INT, b TEXT)") # doctest: +ELLIPSIS ... con.execute( ... "INSERT INTO t VALUES (1, 'a'), (2, 'b'), (3, 'c')" ... ) # doctest: +ELLIPSIS <...> + >>> con.close() >>> con = ibis.connect("duckdb://") >>> t = con.read_sqlite(path="/tmp/sqlite.db", table_name="t") >>> t @@ -1130,13 +1132,15 @@ def attach_sqlite( -------- >>> import ibis >>> import sqlite3 - >>> with sqlite3.connect("/tmp/attach_sqlite.db") as con: + >>> con = sqlite3.connect("/tmp/attach_sqlite.db") + >>> with con: ... con.execute("DROP TABLE IF EXISTS t") # doctest: +ELLIPSIS ... con.execute("CREATE TABLE t (a INT, b TEXT)") # doctest: +ELLIPSIS ... con.execute( ... "INSERT INTO t VALUES (1, 'a'), (2, 'b'), (3, 'c')" ... ) # doctest: +ELLIPSIS <...> + >>> con.close() >>> con = ibis.connect("duckdb://") >>> con.list_tables() [] diff --git a/ibis/backends/duckdb/tests/test_client.py b/ibis/backends/duckdb/tests/test_client.py index 463414e4f484..8477535fbd33 100644 --- a/ibis/backends/duckdb/tests/test_client.py +++ b/ibis/backends/duckdb/tests/test_client.py @@ -413,11 +413,10 @@ def test_read_csv_with_types(tmp_path, input, all_varchar): assert t.schema()["geom"].is_geospatial() -def test_memtable_doesnt_leak(con, monkeypatch): - monkeypatch.setattr(ibis.options, "default_backend", con) - name = "memtable_doesnt_leak" +def test_memtable_doesnt_leak(con): + name = gen_name("memtable_doesnt_leak") assert name not in con.list_tables() - df = ibis.memtable({"a": [1, 2, 3]}, name=name).execute() + df = con.execute(ibis.memtable({"a": [1, 2, 3]}, name=name)) assert name not in con.list_tables() assert len(df) == 3 diff --git a/ibis/backends/duckdb/tests/test_io.py b/ibis/backends/duckdb/tests/test_io.py index 439e26bcdeca..3367fda6c127 100644 --- a/ibis/backends/duckdb/tests/test_io.py +++ b/ibis/backends/duckdb/tests/test_io.py @@ -2,7 +2,6 @@ import os import sqlite3 -from pathlib import Path import duckdb import numpy as np @@ -211,8 +210,12 @@ def test_read_mysql(con, mysqlurl): # pragma: no cover def test_read_sqlite(con, tmp_path): path = tmp_path / "test.db" - with sqlite3.connect(str(path)) as sqlite_con: - sqlite_con.execute("CREATE TABLE t AS SELECT 1 a UNION SELECT 2 UNION SELECT 3") + scon = sqlite3.connect(str(path)) + try: + with scon: + scon.execute("CREATE TABLE t AS SELECT 1 a UNION SELECT 2 UNION SELECT 3") + finally: + scon.close() ft = con.read_sqlite(path, table_name="t") assert ft.count().execute() @@ -221,11 +224,14 @@ def test_read_sqlite(con, tmp_path): def test_read_sqlite_no_table_name(con, tmp_path): path = tmp_path / "test.db" - with sqlite3.connect(str(path)) as _: + scon = sqlite3.connect(str(path)) + try: assert path.exists() with pytest.raises(ValueError): con.read_sqlite(path) + finally: + scon.close() # Because we create a new connection and the test requires loading/installing a @@ -238,41 +244,42 @@ def test_read_sqlite_no_table_name(con, tmp_path): def test_attach_sqlite(data_dir, tmp_path): import sqlite3 - test_db_path = tmp_path / "test.db" - with sqlite3.connect(test_db_path) as scon: - for line in ( - Path(data_dir.parent / "schema" / "sqlite.sql").read_text().split(";") - ): - scon.execute(line) - # Create a new connection here because we already have the `ibis_testing` # tables loaded in to the `con` fixture. con = ibis.duckdb.connect() - con.attach_sqlite(test_db_path) - assert set(con.list_tables()) >= { - "functional_alltypes", - "awards_players", - "batting", - "diamonds", - } - - fa = con.tables.functional_alltypes - assert len(set(fa.schema().types)) > 1 - - # overwrite existing sqlite_db and force schema to all strings - con.attach_sqlite(test_db_path, overwrite=True, all_varchar=True) - assert set(con.list_tables()) >= { - "functional_alltypes", - "awards_players", - "batting", - "diamonds", - } - - fa = con.tables.functional_alltypes - types = fa.schema().types - assert len(set(types)) == 1 - assert dt.String(nullable=True) in set(types) + test_db_path = tmp_path / "test.db" + scon = sqlite3.connect(test_db_path) + try: + with scon: + scon.executescript((data_dir.parent / "schema" / "sqlite.sql").read_text()) + + con.attach_sqlite(test_db_path) + assert set(con.list_tables()) >= { + "functional_alltypes", + "awards_players", + "batting", + "diamonds", + } + + fa = con.tables.functional_alltypes + assert len(set(fa.schema().types)) > 1 + + # overwrite existing sqlite_db and force schema to all strings + con.attach_sqlite(test_db_path, overwrite=True, all_varchar=True) + assert set(con.list_tables()) >= { + "functional_alltypes", + "awards_players", + "batting", + "diamonds", + } + + fa = con.tables.functional_alltypes + types = fa.schema().types + assert len(set(types)) == 1 + assert dt.String(nullable=True) in set(types) + finally: + scon.close() def test_memtable_with_nullable_dtypes(con): diff --git a/ibis/backends/sqlite/tests/test_client.py b/ibis/backends/sqlite/tests/test_client.py index f039cd5fabba..8b6b767c28b2 100644 --- a/ibis/backends/sqlite/tests/test_client.py +++ b/ibis/backends/sqlite/tests/test_client.py @@ -71,8 +71,9 @@ def total(x) -> float: ) def test_connect(url, ext, tmp_path): path = os.path.abspath(tmp_path / f"test.{ext}") - with sqlite3.connect(path): - pass + + sqlite3.connect(path).close() + con = ibis.connect(url(path)) one = ibis.literal(1) assert con.execute(one) == 1 diff --git a/ibis/backends/sqlite/tests/test_types.py b/ibis/backends/sqlite/tests/test_types.py index ddc96577ac6e..fb6598391f41 100644 --- a/ibis/backends/sqlite/tests/test_types.py +++ b/ibis/backends/sqlite/tests/test_types.py @@ -36,24 +36,30 @@ @pytest.fixture(scope="session") def db(tmp_path_factory): path = str(tmp_path_factory.mktemp("databases") / "formats.db") - with sqlite3.connect(path) as con: - con.execute("CREATE TABLE timestamps (ts TIMESTAMP)") - con.execute("CREATE TABLE timestamps_tz (ts TIMESTAMPTZ)") - con.execute("CREATE TABLE weird (str_col STRING, date_col ITSADATE)") - con.execute("CREATE TABLE basic (a INTEGER, b REAL, c BOOLEAN, d BLOB)") - con.executemany("INSERT INTO timestamps VALUES (?)", [(t,) for t in TIMESTAMPS]) - con.executemany( - "INSERT INTO timestamps_tz VALUES (?)", [(t,) for t in TIMESTAMPS_TZ] - ) - con.executemany( - "INSERT INTO weird VALUES (?, ?)", - [ - ("a", "2022-01-01"), - ("b", "2022-01-02"), - ("c", "2022-01-03"), - ("d", "2022-01-04"), - ], - ) + con = sqlite3.connect(path) + try: + with con: + con.execute("CREATE TABLE timestamps (ts TIMESTAMP)") + con.execute("CREATE TABLE timestamps_tz (ts TIMESTAMPTZ)") + con.execute("CREATE TABLE weird (str_col STRING, date_col ITSADATE)") + con.execute("CREATE TABLE basic (a INTEGER, b REAL, c BOOLEAN, d BLOB)") + con.executemany( + "INSERT INTO timestamps VALUES (?)", [(t,) for t in TIMESTAMPS] + ) + con.executemany( + "INSERT INTO timestamps_tz VALUES (?)", [(t,) for t in TIMESTAMPS_TZ] + ) + con.executemany( + "INSERT INTO weird VALUES (?, ?)", + [ + ("a", "2022-01-01"), + ("b", "2022-01-02"), + ("c", "2022-01-03"), + ("d", "2022-01-04"), + ], + ) + finally: + con.close() return path