diff --git a/arbalister/__init__.py b/arbalister/__init__.py index 1c93ea4..51ae515 100644 --- a/arbalister/__init__.py +++ b/arbalister/__init__.py @@ -15,6 +15,7 @@ import jupyterlab.labapp from . import arrow as arrow +from . import file_format as file_format from . import params as params from . import routes as routes diff --git a/arbalister/adbc.py b/arbalister/adbc.py new file mode 100644 index 0000000..54579d7 --- /dev/null +++ b/arbalister/adbc.py @@ -0,0 +1,88 @@ +import dataclasses +import pathlib +from typing import Any, Literal, Self + +import adbc_driver_sqlite.dbapi as adbc_sqlite +import pyarrow as pa + + +def write_sqlite( + table: pa.Table, + path: str | pathlib.Path, + table_name: str = "Table", + catalog_name: str | None = None, + mode: Literal["append", "create", "replace", "create_append"] = "create", +) -> None: + """Write a table as an Sqlite file.""" + with adbc_sqlite.connect(str(path)) as connection: + with connection.cursor() as cursor: + cursor.adbc_ingest(table_name=table_name, data=table, mode=mode, catalog_name=catalog_name) + connection.commit() + + +@dataclasses.dataclass +class SqliteDataFrame: + """A DataFrame plan on a Sqlite file. + + This plan is made to resemble a Apache Datafusion DataFrame despite some performances penalty. + This is because in the Rust Datafusion implementation of as a Sqlite contrib which we would + like to eventually use so this class is only filling in temporarily. + """ + + _path: str + _table_name: str + _schema: pa.Schema + _num_rows: int + _limit: int | None = None + _offset: int | None = None + _select: list[str] | None = None + + @classmethod + def read_sqlite(cls, context: Any, path: pathlib.Path | str, table_name: str | None = None) -> Self: + """Read an Sqlite file metadata and start a new DataFrame plan.""" + with adbc_sqlite.connect(str(path)) as connection: + with connection.cursor() as cursor: + cursor.execute("SELECT name FROM sqlite_master WHERE type='table'") + tables = [row for (row,) in cursor.fetchall()] + + if table_name is None and len(tables) > 0: + table_name = tables[0] + if table_name not in tables or table_name is None: + raise ValueError(f"Invalid table name {table_name}") + + cursor.execute(f'SELECT COUNT(*) FROM "{table_name}"') + num_rows = cursor.fetchone()[0] # type: ignore[index] + + schema = connection.adbc_get_table_schema(table_name) + + return cls(_path=str(path), _table_name=table_name, _schema=schema, _num_rows=num_rows) + + def schema(self) -> pa.Schema: + """Return the :py:class:`pyarrow.Schema` of this DataFrame.""" + return self._schema + + def limit(self, count: int, offset: int = 0) -> Self: + """Return a new DataFrame with a limited number of rows.""" + return dataclasses.replace(self, _limit=count, _offset=offset) + + def select(self, *columns: str) -> Self: + """Project arbitrary expressions into a new `DataFrame`.""" + return dataclasses.replace(self, _select=list(columns)) + + def count(self) -> int: + """Return the total number of rows in this DataFrame.""" + return self._num_rows + + def to_arrow_table(self) -> pa.Table: + """Execute the DataFrame and convert it into an Arrow Table.""" + limit = ( + f"LIMIT {self._limit} OFFSET {self._offset}" + if self._limit is not None and self._offset is not None + else "" + ) + columns = self._select if self._select is not None else ["*"] + + with adbc_sqlite.connect(self._path) as connection: + with connection.cursor() as cursor: + cursor.execute(f'SELECT {",".join(columns)} FROM "{self._table_name}" {limit}') + return cursor.fetch_arrow_table() diff --git a/arbalister/arrow.py b/arbalister/arrow.py index 3157b36..d24882d 100644 --- a/arbalister/arrow.py +++ b/arbalister/arrow.py @@ -1,39 +1,10 @@ -import enum import pathlib -from typing import Any, Callable, Self +from typing import Any, Callable import datafusion as dn import pyarrow as pa - -class FileFormat(enum.StrEnum): - """Known file format that we can read into an Arrow format. - - Todo: - - ADBC (Sqlite/Postgres) - - """ - - Avro = "avro" - Csv = "csv" - Ipc = "ipc" - Orc = "orc" - Parquet = "parquet" - - @classmethod - def from_filename(cls, file: pathlib.Path | str) -> Self: - """Get the file format from a filename extension.""" - file_type = pathlib.Path(file).suffix.removeprefix(".").strip().lower() - - # Match again their default value - if ft := next((ft for ft in FileFormat if str(ft) == file_type), None): - return ft - # Match other known values - match file_type: - case "ipc" | "feather": - return cls.Ipc - raise ValueError(f"Unknown file type {file_type}") - +from . import file_format as ff ReadCallable = Callable[..., dn.DataFrame] @@ -84,7 +55,7 @@ def _write_avro( writer.close() -def get_table_reader(format: FileFormat) -> ReadCallable: +def get_table_reader(format: ff.FileFormat) -> ReadCallable: """Get the datafusion reader factory function for the given format.""" # TODO: datafusion >= 50.0 # def read(ctx: dtfn.SessionContext, path: str | pathlib.Path, *args, **kwargs) -> dtfn.DataFrame: @@ -92,13 +63,13 @@ def get_table_reader(format: FileFormat) -> ReadCallable: # return ctx.read_table(ds, *args, **kwargs) out: ReadCallable match format: - case FileFormat.Avro: + case ff.FileFormat.Avro: out = dn.SessionContext.read_avro - case FileFormat.Csv: + case ff.FileFormat.Csv: out = dn.SessionContext.read_csv - case FileFormat.Parquet: + case ff.FileFormat.Parquet: out = dn.SessionContext.read_parquet - case FileFormat.Ipc: + case ff.FileFormat.Ipc: import pyarrow.feather def read_ipc( @@ -109,7 +80,7 @@ def read_ipc( return ctx.from_arrow(table) out = read_ipc - case FileFormat.Orc: + case ff.FileFormat.Orc: # Watch for https://github.com/datafusion-contrib/datafusion-orc # Evolution for native datafusion reader import pyarrow.orc @@ -121,6 +92,13 @@ def read_orc( return ctx.from_arrow(table) out = read_orc + case ff.FileFormat.Sqlite: + from . import adbc as adbc + + # FIXME: For now we just pretend SqliteDataFrame is a datafision DataFrame + # Either we integrate it properly into Datafusion, or we create a DataFrame as a + # typing.protocol. + out = adbc.SqliteDataFrame.read_sqlite # type: ignore[assignment] return out @@ -128,26 +106,30 @@ def read_orc( WriteCallable = Callable[..., None] -def get_table_writer(format: FileFormat) -> WriteCallable: +def get_table_writer(format: ff.FileFormat) -> WriteCallable: """Get the arrow writer factory function for the given format.""" out: WriteCallable match format: - case FileFormat.Avro: + case ff.FileFormat.Avro: out = _write_avro - case FileFormat.Csv: + case ff.FileFormat.Csv: import pyarrow.csv out = pyarrow.csv.write_csv - case FileFormat.Parquet: + case ff.FileFormat.Parquet: import pyarrow.parquet out = pyarrow.parquet.write_table - case FileFormat.Ipc: + case ff.FileFormat.Ipc: import pyarrow.feather out = pyarrow.feather.write_feather - case FileFormat.Orc: + case ff.FileFormat.Orc: import pyarrow.orc out = pyarrow.orc.write_table + case ff.FileFormat.Sqlite: + from . import adbc as adbc + + out = adbc.write_sqlite return out diff --git a/arbalister/file_format.py b/arbalister/file_format.py new file mode 100644 index 0000000..d2d687a --- /dev/null +++ b/arbalister/file_format.py @@ -0,0 +1,35 @@ +import enum +import pathlib +from typing import Self + + +class FileFormat(enum.StrEnum): + """Known file format that we can read into an Arrow format. + + Todo: + - ADBC (Sqlite/Postgres) + + """ + + Avro = "avro" + Csv = "csv" + Ipc = "ipc" + Orc = "orc" + Parquet = "parquet" + Sqlite = "sqlite" + + @classmethod + def from_filename(cls, file: pathlib.Path | str) -> Self: + """Get the file format from a filename extension.""" + file_type = pathlib.Path(file).suffix.removeprefix(".").strip().lower() + + # Match again their default value + if ft := next((ft for ft in FileFormat if str(ft) == file_type), None): + return ft + # Match other known values + match file_type: + case "ipc" | "feather": + return cls.Ipc + case "sqlite3" | "db" | ".db3", ".s3db", ".sl3": + return cls.Sqlite + raise ValueError(f"Unknown file type {file_type}") diff --git a/arbalister/routes.py b/arbalister/routes.py index 4d12ddf..0cb2ce1 100644 --- a/arbalister/routes.py +++ b/arbalister/routes.py @@ -11,9 +11,25 @@ from jupyter_server.utils import url_path_join from . import arrow as abw +from . import file_format as ff from . import params as params +@dataclasses.dataclass(frozen=True, slots=True) +class SqliteReadParams: + """Query parameter for the Sqlite reader.""" + + table_name: str | None = None + + +@dataclasses.dataclass(frozen=True, slots=True) +class NoReadParams: + """Query parameter for readers with no parameters.""" + + +FileReadParams = SqliteReadParams | NoReadParams + + class BaseRouteHandler(jupyter_server.base.handlers.APIHandler): """A base handler to share common methods.""" @@ -33,13 +49,22 @@ def dataframe(self, path: str) -> dn.DataFrame: Note: On some file type, the file is read eagerly when calling this method. """ file = self.data_file(path) - read_table = abw.get_table_reader(format=abw.FileFormat.from_filename(file)) - return read_table(self.context, file) + file_format = ff.FileFormat.from_filename(file) + file_params = self.get_file_read_params(file_format) + read_table = abw.get_table_reader(format=file_format) + return read_table(self.context, file, **dataclasses.asdict(file_params)) def get_query_params_as[T](self, dataclass_type: type[T]) -> T: """Extract query parameters into a dataclass type.""" return params.build_dataclass(dataclass_type, self.get_query_argument) + def get_file_read_params(self, file_format: ff.FileFormat) -> FileReadParams: + """Read the parameters associated with the relevant file format.""" + match file_format: + case ff.FileFormat.Sqlite: + return self.get_query_params_as(SqliteReadParams) + return NoReadParams() + @dataclasses.dataclass(frozen=True, slots=True) class IpcParams: diff --git a/arbalister/tests/test_routes.py b/arbalister/tests/test_routes.py index 90c1cf0..9b571b0 100644 --- a/arbalister/tests/test_routes.py +++ b/arbalister/tests/test_routes.py @@ -3,115 +3,188 @@ import pathlib import random import string -from typing import Awaitable, Callable, Final +from typing import Awaitable, Callable import pyarrow as pa import pytest import tornado import arbalister as arb +import arbalister.file_format as ff + + +@pytest.fixture( + params=[ + (ff.FileFormat.Avro, arb.routes.NoReadParams()), + (ff.FileFormat.Csv, arb.routes.NoReadParams()), + (ff.FileFormat.Ipc, arb.routes.NoReadParams()), + (ff.FileFormat.Orc, arb.routes.NoReadParams()), + (ff.FileFormat.Parquet, arb.routes.NoReadParams()), + (ff.FileFormat.Sqlite, arb.routes.NoReadParams()), + (ff.FileFormat.Sqlite, arb.routes.SqliteReadParams(table_name="dummy_table_2")), + ], + ids=lambda f_p: f"{f_p[0].value}-{dataclasses.asdict(f_p[1])}", + scope="module", +) +def file_format_and_params(request: pytest.FixtureRequest) -> tuple[ff.FileFormat, arb.routes.FileReadParams]: + """Parametrize the file format and file parameters used in the tests. - -@pytest.fixture(params=list(arb.arrow.FileFormat), scope="session") -def file_format(request: pytest.FixtureRequest) -> arb.arrow.FileFormat: - """Parametrize the file format used in the test.""" - out: arb.arrow.FileFormat = request.param + This is used to to build test cases with a give set of parameters since each file format may be tested + with a different number of parameters. + """ + out: tuple[ff.FileFormat, arb.routes.FileReadParams] = request.param return out -DUMMY_TABLE_ROW_COUNT: Final = 10 -DUMMY_TABLE_COL_COUNT: Final = 4 +@pytest.fixture(scope="module") +def file_format(file_format_and_params: tuple[ff.FileFormat, arb.routes.FileReadParams]) -> ff.FileFormat: + """Extract the the file format fixture value used in the tests.""" + return file_format_and_params[0] + + +@pytest.fixture(scope="module") +def file_params( + file_format_and_params: tuple[ff.FileFormat, arb.routes.FileReadParams], +) -> arb.routes.FileReadParams: + """Extract the the file parameters fixture value used in the tests.""" + return file_format_and_params[1] @pytest.fixture(scope="module") -def dummy_table() -> pa.Table: +def dummy_table_1(num_rows: int = 10) -> pa.Table: """Generate a table with fake data.""" data = { - "lower": random.choices(string.ascii_lowercase, k=DUMMY_TABLE_ROW_COUNT), - "sequence": list(range(DUMMY_TABLE_ROW_COUNT)), - "upper": random.choices(string.ascii_uppercase, k=DUMMY_TABLE_ROW_COUNT), - "number": [random.random() for _ in range(DUMMY_TABLE_ROW_COUNT)], + "lower": random.choices(string.ascii_lowercase, k=num_rows), + "sequence": list(range(num_rows)), + "upper": random.choices(string.ascii_uppercase, k=num_rows), + "number": [random.random() for _ in range(num_rows)], + } + table = pa.table(data) + return table + + +@pytest.fixture(scope="module") +def dummy_table_2(num_rows: int = 13) -> pa.Table: + """Generate a table with different fake data.""" + data = { + "id": list(range(num_rows)), + "flag": [random.choice([True, False]) for _ in range(num_rows)], + "letter": random.choices(string.ascii_letters, k=num_rows), + "score": [random.randint(0, 100) for _ in range(num_rows)], + "timestamp": [random.randint(1_600_000_000, 1_700_000_000) for _ in range(num_rows)], } table = pa.table(data) - assert len(table.schema) == DUMMY_TABLE_COL_COUNT return table +@pytest.fixture(scope="module") +def full_table(file_params: ff.FileFormat, dummy_table_1: pa.Table, dummy_table_2: pa.Table) -> pa.Table: + """Return the full table on which we are executed queries.""" + if isinstance(file_params, arb.routes.SqliteReadParams): + return { + "dummy_table_1": dummy_table_1, + "dummy_table_2": dummy_table_2, + }[file_params.table_name] + return dummy_table_1 + + @pytest.fixture -def dummy_table_file( - jp_root_dir: pathlib.Path, dummy_table: pa.Table, file_format: arb.arrow.FileFormat +def table_file( + jp_root_dir: pathlib.Path, + dummy_table_1: pa.Table, + dummy_table_2: pa.Table, + file_format: ff.FileFormat, + file_params: arb.routes.FileReadParams, ) -> pathlib.Path: """Write the dummy table to file.""" write_table = arb.arrow.get_table_writer(file_format) table_path = jp_root_dir / f"test.{str(file_format).lower()}" - write_table(dummy_table, table_path) + + match file_format: + case ff.FileFormat.Sqlite: + write_table(dummy_table_1, table_path, table_name="dummy_table_1", mode="create_append") + write_table(dummy_table_2, table_path, table_name="dummy_table_2", mode="create_append") + case _: + write_table(dummy_table_1, table_path) + return table_path.relative_to(jp_root_dir) JpFetch = Callable[..., Awaitable[tornado.httpclient.HTTPResponse]] -@pytest.mark.parametrize( - "params", - [ - arb.routes.IpcParams(), +@pytest.fixture( + params=[ + # No limits + lambda table: arb.routes.IpcParams(), # Limit only number of rows - arb.routes.IpcParams(row_chunk=0, row_chunk_size=3), - arb.routes.IpcParams(row_chunk=1, row_chunk_size=2), - arb.routes.IpcParams(row_chunk=0, row_chunk_size=DUMMY_TABLE_ROW_COUNT), - arb.routes.IpcParams(row_chunk=1, row_chunk_size=DUMMY_TABLE_ROW_COUNT // 2 + 1), + lambda table: arb.routes.IpcParams(row_chunk=0, row_chunk_size=3), + lambda table: arb.routes.IpcParams(row_chunk=1, row_chunk_size=2), + lambda table: arb.routes.IpcParams(row_chunk=0, row_chunk_size=table.num_rows), + lambda table: arb.routes.IpcParams(row_chunk=1, row_chunk_size=table.num_rows // 2 + 1), # Limit only number of cols - arb.routes.IpcParams(col_chunk=0, col_chunk_size=3), - arb.routes.IpcParams(col_chunk=1, col_chunk_size=2), - arb.routes.IpcParams(col_chunk=0, col_chunk_size=DUMMY_TABLE_COL_COUNT), - arb.routes.IpcParams(col_chunk=1, col_chunk_size=DUMMY_TABLE_COL_COUNT // 2 + 1), + lambda table: arb.routes.IpcParams(col_chunk=0, col_chunk_size=3), + lambda table: arb.routes.IpcParams(col_chunk=1, col_chunk_size=2), + lambda table: arb.routes.IpcParams(col_chunk=0, col_chunk_size=table.num_columns), + lambda table: arb.routes.IpcParams(col_chunk=1, col_chunk_size=table.num_columns // 2 + 1), # Limit both - arb.routes.IpcParams( + lambda table: arb.routes.IpcParams( row_chunk=0, row_chunk_size=3, col_chunk=1, - col_chunk_size=DUMMY_TABLE_COL_COUNT // 2 + 1, + col_chunk_size=table.num_columns // 2 + 1, ), - arb.routes.IpcParams( + lambda table: arb.routes.IpcParams( row_chunk=0, - row_chunk_size=DUMMY_TABLE_ROW_COUNT, + row_chunk_size=table.num_rows, col_chunk=1, col_chunk_size=2, ), # Schema only - arb.routes.IpcParams( + lambda table: arb.routes.IpcParams( row_chunk=0, row_chunk_size=0, ), - ], + ] ) -async def test_ipc_route_limit_row( +def ipc_params(request: pytest.FixtureRequest, dummy_table_1: pa.Table) -> arb.routes.IpcParams: + """Parameters used to select the IPC data in the response.""" + make_table: Callable[[pa.Table], arb.routes.IpcParams] = request.param + return make_table(dummy_table_1) + + +async def test_ipc_route_limit( jp_fetch: JpFetch, - dummy_table: pa.Table, - dummy_table_file: pathlib.Path, - params: arb.routes.IpcParams, + full_table: pa.Table, + table_file: pathlib.Path, + ipc_params: arb.routes.IpcParams, + file_params: arb.routes.SqliteReadParams, ) -> None: """Test fetching a file returns the limited rows and columns in IPC.""" response = await jp_fetch( "arrow/stream", - str(dummy_table_file), - params={k: v for k, v in dataclasses.asdict(params).items() if v is not None}, + str(table_file), + params={ + k: v + for k, v in {**dataclasses.asdict(ipc_params), **dataclasses.asdict(file_params)}.items() + if v is not None + }, ) assert response.code == 200 assert response.headers["Content-Type"] == "application/vnd.apache.arrow.stream" payload = pa.ipc.open_stream(response.body).read_all() - expected = dummy_table + expected = full_table # Row slicing - if (size := params.row_chunk_size) is not None and (cidx := params.row_chunk) is not None: + if (size := ipc_params.row_chunk_size) is not None and (cidx := ipc_params.row_chunk) is not None: expected_num_rows = min((size * (cidx + 1)), expected.num_rows) - (size * cidx) assert payload.num_rows == expected_num_rows expected = expected.slice(cidx * size, size) # Col slicing - if (size := params.col_chunk_size) is not None and (cidx := params.col_chunk) is not None: + if (size := ipc_params.col_chunk_size) is not None and (cidx := ipc_params.col_chunk) is not None: expected_num_cols = min((size * (cidx + 1)), len(expected.schema)) - (size * cidx) assert len(payload.schema) == expected_num_cols col_names = expected.schema.names @@ -122,13 +195,22 @@ async def test_ipc_route_limit_row( assert expected.cast(payload.schema) == payload -async def test_stats_route(jp_fetch: JpFetch, dummy_table: pa.Table, dummy_table_file: pathlib.Path) -> None: +async def test_stats_route( + jp_fetch: JpFetch, + full_table: pa.Table, + table_file: pathlib.Path, + file_params: arb.routes.SqliteReadParams, +) -> None: """Test fetching a file returns the correct metadata in Json.""" - response = await jp_fetch("arrow/stats/", str(dummy_table_file)) + response = await jp_fetch( + "arrow/stats/", + str(table_file), + params={k: v for k, v in dataclasses.asdict(file_params).items() if v is not None}, + ) assert response.code == 200 assert response.headers["Content-Type"] == "application/json; charset=UTF-8" payload = json.loads(response.body) - assert payload["num_cols"] == len(dummy_table.schema) - assert payload["num_rows"] == dummy_table.num_rows + assert payload["num_cols"] == len(full_table.schema) + assert payload["num_rows"] == full_table.num_rows diff --git a/data/generate.py b/data/generate.py index b14e24e..ec8f2be 100644 --- a/data/generate.py +++ b/data/generate.py @@ -9,6 +9,7 @@ import pyarrow.parquet as paq import arbalister.arrow as aa +from arbalister import file_format as ff MAX_FAKER_ROWS = 100_000 @@ -81,7 +82,7 @@ def sink_coordinate_table( num_rows: int, num_cols: int, path: pathlib.Path, chunk_size: int = 1_000_000 ) -> None: """Write iteratively a table where each cell has its coordinates.""" - assert aa.FileFormat.from_filename(path) == aa.FileFormat.Parquet + assert ff.FileFormat.from_filename(path) == ff.FileFormat.Parquet print("Initializing session context", flush=True) ctx = dn.SessionContext() print("Generating schema", flush=True) @@ -97,7 +98,7 @@ def configure_command_single(cmd: argparse.ArgumentParser) -> argparse.ArgumentP cmd.add_argument( "--output-type", "-t", - choices=[t.name.lower() for t in aa.FileFormat], + choices=[t.name.lower() for t in ff.FileFormat], default=None, help="Output file type", ) @@ -149,7 +150,7 @@ def shuffle_table(table: pa.Table, seed: int | None = None) -> pa.Table: return table.select(col_order).take(row_indices) -def save_table(table: pa.Table, path: pathlib.Path, file_type: aa.FileFormat) -> None: +def save_table(table: pa.Table, path: pathlib.Path, file_type: ff.FileFormat) -> None: """Save a table to file with the given file type.""" path.parent.mkdir(exist_ok=True, parents=True) write_table = aa.get_table_writer(file_type) @@ -165,13 +166,13 @@ def main() -> None: match args.command: case "single": - ft = next((t for t in aa.FileFormat if t.name.lower() == args.output_type), None) + ft = next((t for t in ff.FileFormat if t.name.lower() == args.output_type), None) if ft is None: - ft = aa.FileFormat.from_filename(args.output_file) + ft = ff.FileFormat.from_filename(args.output_file) save_table(shuffle_table(table), args.output_file, ft) case "batch": for p in args.output_file: - ft = aa.FileFormat.from_filename(p) + ft = ff.FileFormat.from_filename(p) save_table(shuffle_table(table), p, ft) case "coordinate": sink_coordinate_table(num_rows=args.num_rows, num_cols=args.num_cols, path=args.output_file) diff --git a/pixi.lock b/pixi.lock index d5fd5a3..8d20e59 100644 --- a/pixi.lock +++ b/pixi.lock @@ -12,6 +12,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/adbc-driver-manager-1.9.0-py312h1289d80_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/adbc-driver-sqlite-1.9.0-pyha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.12.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-25.1.0-py312h4c3975b_2.conda @@ -54,6 +56,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpointer-3.0.0-pyhcf101f3_3.conda @@ -71,6 +74,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/lark-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20250127.1-cxx17_hbbce691_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libadbc-driver-sqlite-1.9.0-hcea63bf_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-20.0.0-h1b9301b_8_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-20.0.0-hcb10f89_8_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-20.0.0-hcb10f89_8_cpu.conda @@ -176,6 +180,8 @@ environments: - pypi: ./ osx-64: - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/adbc-driver-manager-1.9.0-py312h1eb2caf_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/adbc-driver-sqlite-1.9.0-pyha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.12.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/argon2-cffi-bindings-25.1.0-py312h80b0991_2.conda @@ -218,6 +224,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-75.1-h120a0e1_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpointer-3.0.0-pyhcf101f3_3.conda @@ -233,6 +240,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/krb5-1.21.3-h37d8d59_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/lark-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libabseil-20250127.1-cxx17_h0e468a2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libadbc-driver-sqlite-1.9.0-h4340512_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-20.0.0-h7601d43_8_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-acero-20.0.0-hdc53af8_8_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-dataset-20.0.0-hdc53af8_8_cpu.conda @@ -333,6 +341,8 @@ environments: - pypi: ./ osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/adbc-driver-manager-1.9.0-py312h455b684_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/adbc-driver-sqlite-1.9.0-pyha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.12.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/argon2-cffi-bindings-25.1.0-py312h4409184_2.conda @@ -375,6 +385,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpointer-3.0.0-pyhcf101f3_3.conda @@ -390,6 +401,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/lark-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20250127.1-cxx17_h07bc746_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libadbc-driver-sqlite-1.9.0-hdd8eb12_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-20.0.0-hd5f8272_8_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-20.0.0-hf07054f_8_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-20.0.0-hf07054f_8_cpu.conda @@ -490,6 +502,8 @@ environments: - pypi: ./ win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/adbc-driver-manager-1.9.0-py312hbb81ca0_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/adbc-driver-sqlite-1.9.0-pyha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.12.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/argon2-cffi-bindings-25.1.0-py312he06e257_2.conda @@ -524,6 +538,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpointer-3.0.0-pyhcf101f3_3.conda @@ -539,6 +554,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/krb5-1.21.3-hdf4eb48_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/lark-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libabseil-20250127.1-cxx17_h4eb7d71_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libadbc-driver-sqlite-1.9.0-h18ee088_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-20.0.0-h3e40a90_8_cpu.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-acero-20.0.0-h7d8d6a5_8_cpu.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-dataset-20.0.0-h7d8d6a5_8_cpu.conda @@ -639,6 +655,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/adbc-driver-manager-1.9.0-py312h1289d80_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/adbc-driver-sqlite-1.9.0-pyha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.12.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-25.1.0-py312h4c3975b_2.conda @@ -716,6 +734,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.15-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.1.0-pyha191276_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.8.0-pyh53cf698_0.conda @@ -744,6 +763,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/lark-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20250127.1-cxx17_hbbce691_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libadbc-driver-sqlite-1.9.0-hcea63bf_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-20.0.0-h1b9301b_8_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-20.0.0-hcb10f89_8_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-20.0.0-hcb10f89_8_cpu.conda @@ -907,6 +927,8 @@ environments: - pypi: ./ osx-64: - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/adbc-driver-manager-1.9.0-py312h1eb2caf_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/adbc-driver-sqlite-1.9.0-pyha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.12.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/appnope-0.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda @@ -985,6 +1007,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.15-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.1.0-pyh5552912_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.8.0-pyh53cf698_0.conda @@ -1011,6 +1034,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/krb5-1.21.3-h37d8d59_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/lark-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libabseil-20250127.1-cxx17_h0e468a2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libadbc-driver-sqlite-1.9.0-h4340512_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-20.0.0-h7601d43_8_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-acero-20.0.0-hdc53af8_8_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-dataset-20.0.0-hdc53af8_8_cpu.conda @@ -1168,6 +1192,8 @@ environments: - pypi: ./ osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/adbc-driver-manager-1.9.0-py312h455b684_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/adbc-driver-sqlite-1.9.0-pyha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.12.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/appnope-0.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda @@ -1246,6 +1272,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.15-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.1.0-pyh5552912_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.8.0-pyh53cf698_0.conda @@ -1272,6 +1299,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/lark-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20250127.1-cxx17_h07bc746_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libadbc-driver-sqlite-1.9.0-hdd8eb12_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-20.0.0-hd5f8272_8_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-20.0.0-hf07054f_8_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-20.0.0-hf07054f_8_cpu.conda @@ -1429,6 +1457,8 @@ environments: - pypi: ./ win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/adbc-driver-manager-1.9.0-py312hbb81ca0_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/adbc-driver-sqlite-1.9.0-pyha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.12.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/argon2-cffi-bindings-25.1.0-py312he06e257_2.conda @@ -1498,6 +1528,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.15-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.1.0-pyh6dadd2b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.8.0-pyhe2676ad_0.conda @@ -1524,6 +1555,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/krb5-1.21.3-hdf4eb48_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/lark-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libabseil-20250127.1-cxx17_h4eb7d71_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libadbc-driver-sqlite-1.9.0-h18ee088_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-20.0.0-h3e40a90_8_cpu.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-acero-20.0.0-h7d8d6a5_8_cpu.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-dataset-20.0.0-h7d8d6a5_8_cpu.conda @@ -1682,6 +1714,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/adbc-driver-manager-1.9.0-py312h1289d80_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/adbc-driver-sqlite-1.9.0-pyha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.12.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-25.1.0-py312h4c3975b_2.conda @@ -1745,6 +1779,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.1.0-pyha191276_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.8.0-pyh53cf698_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda @@ -1770,6 +1805,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/lark-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20250127.1-cxx17_hbbce691_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libadbc-driver-sqlite-1.9.0-hcea63bf_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-20.0.0-h1b9301b_8_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-20.0.0-hcb10f89_8_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-20.0.0-hcb10f89_8_cpu.conda @@ -1898,6 +1934,8 @@ environments: - pypi: ./ osx-64: - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/adbc-driver-manager-1.9.0-py312h1eb2caf_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/adbc-driver-sqlite-1.9.0-pyha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.12.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/appnope-0.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda @@ -1962,6 +2000,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-75.1-h120a0e1_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.1.0-pyh5552912_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.8.0-pyh53cf698_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda @@ -1985,6 +2024,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/krb5-1.21.3-h37d8d59_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/lark-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libabseil-20250127.1-cxx17_h0e468a2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libadbc-driver-sqlite-1.9.0-h4340512_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-20.0.0-h7601d43_8_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-acero-20.0.0-hdc53af8_8_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-dataset-20.0.0-hdc53af8_8_cpu.conda @@ -2107,6 +2147,8 @@ environments: - pypi: ./ osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/adbc-driver-manager-1.9.0-py312h455b684_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/adbc-driver-sqlite-1.9.0-pyha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.12.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/appnope-0.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda @@ -2171,6 +2213,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.1.0-pyh5552912_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.8.0-pyh53cf698_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda @@ -2194,6 +2237,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/lark-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20250127.1-cxx17_h07bc746_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libadbc-driver-sqlite-1.9.0-hdd8eb12_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-20.0.0-hd5f8272_8_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-20.0.0-hf07054f_8_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-20.0.0-hf07054f_8_cpu.conda @@ -2316,6 +2360,8 @@ environments: - pypi: ./ win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/adbc-driver-manager-1.9.0-py312hbb81ca0_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/adbc-driver-sqlite-1.9.0-pyha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.12.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/argon2-cffi-bindings-25.1.0-py312he06e257_2.conda @@ -2371,6 +2417,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.1.0-pyh6dadd2b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.8.0-pyhe2676ad_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda @@ -2394,6 +2441,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/krb5-1.21.3-hdf4eb48_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/lark-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libabseil-20250127.1-cxx17_h4eb7d71_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libadbc-driver-sqlite-1.9.0-h18ee088_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-20.0.0-h3e40a90_8_cpu.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-acero-20.0.0-h7d8d6a5_8_cpu.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-dataset-20.0.0-h7d8d6a5_8_cpu.conda @@ -2538,6 +2586,89 @@ packages: purls: [] size: 8191 timestamp: 1744137672556 +- conda: https://conda.anaconda.org/conda-forge/linux-64/adbc-driver-manager-1.9.0-py312h1289d80_1.conda + sha256: cee0eb8a92563267ded63f61901922fe8e115cf0b694bfd39548fee59d25ff2f + md5: 04f547f5f1f2022bdb0a556dcc566d5a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - pyarrow >=8.0.0 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/adbc-driver-manager?source=hash-mapping + size: 346251 + timestamp: 1764031540423 +- conda: https://conda.anaconda.org/conda-forge/osx-64/adbc-driver-manager-1.9.0-py312h1eb2caf_1.conda + sha256: 88fa88081d6376152383c7964340403f6a6e998212cddc87965c2e55957dcb96 + md5: 83a2664f12bfc21d7f2a21e312472935 + depends: + - __osx >=11.0 + - libcxx >=19 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - pyarrow >=8.0.0 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/adbc-driver-manager?source=hash-mapping + size: 311547 + timestamp: 1764031904949 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/adbc-driver-manager-1.9.0-py312h455b684_1.conda + sha256: f4e18f38f7d1b8c569c83cd4caff30ffeb3949d99b6f3718685d47bf201c3916 + md5: 220b002cc30013d69b584b64ff82113a + depends: + - __osx >=11.0 + - libcxx >=19 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - pyarrow >=8.0.0 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/adbc-driver-manager?source=hash-mapping + size: 298419 + timestamp: 1764031973405 +- conda: https://conda.anaconda.org/conda-forge/win-64/adbc-driver-manager-1.9.0-py312hbb81ca0_1.conda + sha256: 0613da8a43fb4f7b0b15604e2acbfcf8084bce3c2725df89b983156f525fb0bf + md5: 16a774881beceaa6f3dd41da99dec7b5 + depends: + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + constrains: + - pyarrow >=8.0.0 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/adbc-driver-manager?source=hash-mapping + size: 430584 + timestamp: 1764031648991 +- conda: https://conda.anaconda.org/conda-forge/noarch/adbc-driver-sqlite-1.9.0-pyha770c72_1.conda + sha256: 83c209bb144840e3375b2b525a4ed2dd7fc0e3c6a8da7273b122b08c29c8f567 + md5: 80c8e7a339ad9423faa0b515ec0ea0a2 + depends: + - adbc-driver-manager >=1.9.0,<2.0a0 + - importlib_resources + - libadbc-driver-sqlite >=1.9.0,<1.9.1.0a0 + - python >=3.10 + constrains: + - pyarrow >=8.0.0 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/adbc-driver-sqlite?source=hash-mapping + size: 24709 + timestamp: 1764032240842 - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.12.0-pyhcf101f3_0.conda sha256: 830fc81970cd9d19869909b9b16d241f4d557e4f201a1030aa6ed87c6aa8b930 md5: 9958d4a1ee7e9c768fe8f4fb51bd07ea @@ -2570,8 +2701,10 @@ packages: - pypi: ./ name: arbalister version: 0.1.0 - sha256: b3c23e0901a5247469d7f00a61e13669ae82d954e1ae19f5074a2678a84efb23 + sha256: 3173db325467de56d1c311749ceec1fa1e50196d0cdd0a6bb5d201ee2ed9c8c7 requires_dist: + - adbc-driver-manager + - adbc-driver-sqlite - datafusion>=40.0 - jupyter-server>=2.4.0,<3 - pyarrow>=20.0 @@ -4883,6 +5016,20 @@ packages: - pkg:pypi/importlib-metadata?source=hash-mapping size: 34641 timestamp: 1747934053147 +- conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.2-pyhd8ed1ab_0.conda + sha256: acc1d991837c0afb67c75b77fdc72b4bf022aac71fedd8b9ea45918ac9b08a80 + md5: c85c76dc67d75619a92f51dfbce06992 + depends: + - python >=3.9 + - zipp >=3.1.0 + constrains: + - importlib-resources >=6.5.2,<6.5.3.0a0 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/importlib-resources?source=hash-mapping + size: 33781 + timestamp: 1736252433366 - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda sha256: e1a9e3b1c8fe62dc3932a616c284b5d8cbe3124bbfbedcf4ce5c828cb166ee19 md5: 9614359868482abba1bd15ce465e3c42 @@ -5504,6 +5651,56 @@ packages: purls: [] size: 1836732 timestamp: 1742370096247 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libadbc-driver-sqlite-1.9.0-hcea63bf_1.conda + sha256: 313d4ab39d50036b0816b01b290174d591e342f83b18eaaa6d21073f408b83e8 + md5: 053a42e10fba063db539a8116d251a44 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libsqlite >=3.51.0,<4.0a0 + - libstdcxx >=14 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 194057 + timestamp: 1764032114809 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libadbc-driver-sqlite-1.9.0-h4340512_1.conda + sha256: 098a1ebc11cd605c128cd32434b21d57b0d87777b0a4b7becb120995d1ec7f97 + md5: e243907376ea9c4ad3fb1c1c99baee6f + depends: + - __osx >=11.0 + - libcxx >=19 + - libsqlite >=3.51.0,<4.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 204299 + timestamp: 1764032925091 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libadbc-driver-sqlite-1.9.0-hdd8eb12_1.conda + sha256: 1e40620e0ba150615708143d95c88923614173eb0ebacdf1db7312a9abd8fa74 + md5: db776abb92bddec261c64bf3bc760d26 + depends: + - __osx >=11.0 + - libcxx >=19 + - libsqlite >=3.51.0,<4.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 189010 + timestamp: 1764033020392 +- conda: https://conda.anaconda.org/conda-forge/win-64/libadbc-driver-sqlite-1.9.0-h18ee088_1.conda + sha256: e2f593e5c80c8959a090fd90adca0962927878fe9d392d5db58e30737796be4c + md5: d46c2a845fd78d99872a8bcefa2b217c + depends: + - libsqlite >=3.51.0,<4.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 164032 + timestamp: 1764032117306 - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-20.0.0-h1b9301b_8_cpu.conda build_number: 8 sha256: e218ae6165e6243d8850352640cee57f06a8d05743647918a0370cc5fcc8b602 diff --git a/pyproject.toml b/pyproject.toml index 778a04f..8d9dafc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,8 +24,15 @@ classifiers = [ "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", ] -dependencies = ["jupyter_server>=2.4.0,<3", "pyarrow>=20.0", "datafusion>=40.0"] dynamic = ["version", "description", "authors", "urls", "keywords"] +dependencies = [ + "jupyter_server>=2.4.0,<3", + "pyarrow>=20.0", + "datafusion>=40.0", + # Optional dependencies + "adbc_driver_manager", + "adbc_driver_sqlite", +] [tool.hatch.version] source = "nodejs" @@ -115,6 +122,8 @@ nodejs = "*" jupyter_server = "*" pyarrow = "*" datafusion = "*" +adbc-driver-manager = "*" +adbc-driver-sqlite = "*" [tool.pixi.pypi-dependencies] arbalister = { path = ".", editable = true } @@ -220,6 +229,7 @@ python data/generate.py batch \ -o data/gen/test.ipc \ -o data/gen/test.orc \ -o data/gen/test.parquet \ + -o data/gen/test.sqlite \ """ outputs = ["data/gen/**/test.*"] description = """Generate different data files to use in development in data/gen."""