diff --git a/docs/api/expressions/top_level.md b/docs/api/expressions/top_level.md index 9b85ee04eee5..f2af4a282314 100644 --- a/docs/api/expressions/top_level.md +++ b/docs/api/expressions/top_level.md @@ -36,6 +36,7 @@ These methods and objects are available directly in the `ibis` module. ::: ibis.read_parquet ::: ibis.row_number ::: ibis.schema +::: ibis.set_backend ::: ibis.struct ::: ibis.table ::: ibis.time diff --git a/ibis/backends/tests/test_client.py b/ibis/backends/tests/test_client.py index 48157f33e703..1217b6799761 100644 --- a/ibis/backends/tests/test_client.py +++ b/ibis/backends/tests/test_client.py @@ -878,6 +878,57 @@ def test_get_backend(con, alltypes, monkeypatch): assert ibis.get_backend(expr) is con +def test_set_backend(con, monkeypatch): + monkeypatch.setattr(ibis.options, "default_backend", None) + ibis.set_backend(con) + assert ibis.get_backend() is con + + +@pytest.mark.parametrize( + "name", + [ + param("duckdb", marks=mark.duckdb, id="duckdb"), + param("polars", marks=mark.polars, id="polars"), + param("sqlite", marks=mark.sqlite, id="sqlite"), + ], +) +def test_set_backend_name(name, monkeypatch): + # Don't need to test with all backends, only checking that things are + # plumbed through correctly. + monkeypatch.setattr(ibis.options, "default_backend", None) + ibis.set_backend(name) + assert ibis.get_backend().name == name + + +@pytest.mark.parametrize( + "url", + [ + param( + "clickhouse://default@localhost:9000/ibis_testing", + marks=mark.clickhouse, + id="clickhouse", + ), + param( + "mysql://ibis:ibis@localhost:3306/ibis_testing", + marks=mark.mysql, + id="mysql", + ), + param( + "postgres://postgres:postgres@localhost:5432/ibis_testing", + marks=mark.postgres, + id="postgres", + ), + ], +) +def test_set_backend_url(url, monkeypatch): + # Don't need to test with all backends, only checking that things are + # plumbed through correctly. + monkeypatch.setattr(ibis.options, "default_backend", None) + name = url.split("://")[0] + ibis.set_backend(url) + assert ibis.get_backend().name == name + + @pytest.mark.notyet( [ "bigquery", diff --git a/ibis/expr/api.py b/ibis/expr/api.py index ee333384113f..e3a05fce6f13 100644 --- a/ibis/expr/api.py +++ b/ibis/expr/api.py @@ -147,6 +147,7 @@ 'schema', 'Schema', 'sequence', + 'set_backend', 'show_sql', 'to_sql', 'struct', @@ -871,6 +872,40 @@ def read_parquet(sources: str | Path, **kwargs: Any) -> ir.Table: return con.read_parquet(sources, **kwargs) +def set_backend(backend: str | BaseBackend) -> None: + """Set the default Ibis backend. + + Parameters + ---------- + backend + May be a backend name or URL, or an existing backend instance. + + Examples + -------- + May pass the backend as a name: + >>> ibis.set_backend("polars") + + Or as a URI: + >>> ibis.set_backend("postgres://user:password@hostname:5432") + + Or as an existing backend instance: + >>> ibis.set_backend(ibis.duckdb.connect()) + """ + import ibis + + if isinstance(backend, str) and backend.isidentifier(): + try: + backend_type = getattr(ibis, backend) + except AttributeError: + pass + else: + backend = backend_type.connect() + if isinstance(backend, str): + backend = ibis.connect(backend) + + ibis.options.default_backend = backend + + def get_backend(expr: Expr | None = None) -> BaseBackend: """Get the current Ibis backend to use for a given expression.