Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,13 @@ jobs:
run: uvx ruff format --check .
- name: Python Lint - Ruff
run: uvx ruff check .
# PyRight needs the project for type information, so use uv run
- name: Python Lint - PyRight
env:
MATURIN_PEP517_ARGS: "--profile ci"
# ty needs the Python dependencies for type information, but resolves the
# vortex package itself from the in-tree sources and stubs, so skip the
# Rust extension build entirely.
- name: Python Lint - ty
run: |
uv sync --all-packages
uv run basedpyright vortex-python
uv sync --all-packages --no-install-package vortex-data --no-install-package vortex-data-cuda
uv run --no-sync ty check vortex-python

python-test:
name: "Python (test)"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/cuda.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,9 @@ jobs:
env:
MATURIN_PEP517_ARGS: "--profile ci"
run: |
# --all-packages installs the shared dev tooling (basedpyright) from the
# --all-packages installs the shared dev tooling (ty) from the
# root `dev` group; --extra cuda adds the vortex-data-cuda extension.
uv run --all-packages --extra cuda basedpyright vortex-python vortex-python-cuda
uv run --all-packages --extra cuda ty check vortex-python vortex-python-cuda
uv run --all-packages --extra cuda pytest --benchmark-disable vortex-python/test/test_cuda.py vortex-python-cuda/test

cuda-test-sanitizer:
Expand Down
17 changes: 14 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ packages = ["dummy"] # Required for workspace project
# Shared dev tooling. Member-specific dev deps live in each member's pyproject.toml.
# `uv sync --all-packages` picks up dev groups from all workspace members.
dev = [
"basedpyright>=1.31",
"ty>=0.0.73",
"ipython>=8.26.0",
"pip>=23.3.2",
"pytest>=7.4.0",
Expand Down Expand Up @@ -73,5 +73,16 @@ log_cli = true
log_cli_level = "INFO"
xfail_strict = true

[tool.basedpyright]
exclude = ["vortex-python/python/vortex/_lib/store/**.pyi"]
[tool.ty.environment]
# First-party source roots so `vortex` and `vortex_cuda` resolve without an
# editable install of the Rust extensions.
root = [
"vortex-python/python",
"vortex-python",
"vortex-python-cuda/python",
"vortex-python-cuda",
]

[tool.ty.src]
# Vendored obstore stubs are not checked.
exclude = ["vortex-python/python/vortex/_lib/store/**/*.pyi"]
125 changes: 61 additions & 64 deletions uv.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions vortex-python-cuda/python/vortex_cuda/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright the Vortex contributors
# pyright: reportAttributeAccessIssue=false, reportMissingModuleSource=false, reportPrivateUsage=false, reportUnknownMemberType=false, reportUnknownVariableType=false

import importlib
from types import ModuleType

from . import _lib

Expand Down Expand Up @@ -39,7 +39,7 @@ def _install_vortex_array_methods() -> None:
setattr(vortex.Array, "__arrow_c_device_array__", _Array___arrow_c_device_array__)


def _import_cudf_modules() -> tuple[object, object]:
def _import_cudf_modules() -> tuple[ModuleType, ModuleType]:
try:
cudf = importlib.import_module("cudf")
pylibcudf = importlib.import_module("pylibcudf")
Expand Down
1 change: 0 additions & 1 deletion vortex-python-cuda/test/test_cuda.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright the Vortex contributors
# pyright: reportAny=false, reportExplicitAny=false

import gc
import sys
Expand Down
5 changes: 2 additions & 3 deletions vortex-python-cuda/test/test_native_bridge.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright the Vortex contributors
# pyright: reportPrivateUsage=false

import gc

Expand Down Expand Up @@ -66,7 +65,7 @@ def test_metadata_bridge_struct_with_children():

arrow_table = pa.table({"a": [1, 2, 3], "b": [4.0, 5.0, 6.0]})
struct_array = vortex.Array.from_arrow(
pa.StructArray.from_arrays( # pyright: ignore[reportUnknownMemberType]
pa.StructArray.from_arrays(
[arrow_table.column("a").combine_chunks(), arrow_table.column("b").combine_chunks()],
names=["a", "b"],
)
Expand Down Expand Up @@ -128,7 +127,7 @@ def test_arrow_device_export_struct_array():

arrow_table = pa.table({"a": [1, 2, 3], "b": [4.0, 5.0, 6.0]})
struct_array = vortex.Array.from_arrow(
pa.StructArray.from_arrays( # pyright: ignore[reportUnknownMemberType]
pa.StructArray.from_arrays(
[arrow_table.column("a").combine_chunks(), arrow_table.column("b").combine_chunks()],
names=["a", "b"],
)
Expand Down
2 changes: 1 addition & 1 deletion vortex-python/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ If Python docstrings, `docs/api/python/`, or Sphinx configuration change, also f
## Linting and Formatting

```bash
uv run basedpyright vortex-python
uv run ty check vortex-python
uv run ruff format --check <changed-python-files>
uv run ruff check <changed-python-files>
```
Expand Down
8 changes: 4 additions & 4 deletions vortex-python/benchmark/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ def vxf(
length = 100_000

columns: dict[str, list[int] | list[float] | list[str]] = {}
assert "x" in request.param # pyright: ignore[reportAny]
assert "x" in request.param
columns["x"] = list(range(length))

if "y" in request.param: # pyright: ignore[reportAny]
if "y" in request.param:
columns["y"] = [hashlib.md5(x.to_bytes(length=4), usedforsecurity=False).hexdigest() for x in range(length)]
if "z" in request.param: # pyright: ignore[reportAny]
if "z" in request.param:
columns["z"] = [math.sqrt(x) for x in range(length)]

a = vx.array(pa.table(columns)) # pyright: ignore[reportCallIssue, reportUnknownArgumentType, reportArgumentType]
a = vx.array(pa.table(columns)) # ty: ignore[no-matching-overload]
vx.io.write(a, str(fname))
return vx.open(str(fname))

Expand Down
8 changes: 3 additions & 5 deletions vortex-python/benchmark/test_aggregation.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import pyarrow as pa
import pytest
from pyarrow.types import is_floating, is_integer
from pytest_benchmark.fixture import BenchmarkFixture # pyright: ignore[reportMissingTypeStubs]
from pytest_benchmark.fixture import BenchmarkFixture

import vortex as vx

Expand All @@ -19,9 +19,7 @@ def _has_mean(t: pa.DataType) -> bool:
@pytest.mark.benchmark(group="aggregation", disable_gc=True)
def test_arrow_table_aggregation(benchmark: BenchmarkFixture, vxf: vx.VortexFile):
aggregations: list[tuple[str, Literal["mean"]]] = [
(field.name, "mean")
for field in vxf.dtype.to_arrow_schema() # pyright: ignore[reportUnknownVariableType]
if _has_mean(field.type) # pyright: ignore[reportUnknownMemberType, reportUnknownArgumentType]
(field.name, "mean") for field in vxf.dtype.to_arrow_schema() if _has_mean(field.type)
]
benchmark(lambda: pa.concat_tables(x.to_arrow_table() for x in vxf.scan()).group_by([]).aggregate(aggregations))

Expand All @@ -44,7 +42,7 @@ def test_duckdb_aggregation(benchmark: BenchmarkFixture, vxf: vx.VortexFile):
ds = vxf.to_dataset()
_ = conn.register("ds", ds)
aggregations = ",".join(
[f"avg(ds.{field.name}) as {field.name}" for field in vxf.dtype.to_arrow_schema() if _has_mean(field.type)] # pyright: ignore[reportUnknownVariableType, reportUnknownMemberType, reportUnknownArgumentType]
[f"avg(ds.{field.name}) as {field.name}" for field in vxf.dtype.to_arrow_schema() if _has_mean(field.type)]
)
print(aggregations)
query = f"select {aggregations} from ds"
Expand Down
2 changes: 1 addition & 1 deletion vortex-python/benchmark/test_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import polars as pl
import pyarrow as pa
import pytest
from pytest_benchmark.fixture import BenchmarkFixture # pyright: ignore[reportMissingTypeStubs]
from pytest_benchmark.fixture import BenchmarkFixture

import vortex as vx
from vortex.expr import column
Expand Down
2 changes: 1 addition & 1 deletion vortex-python/benchmark/test_scalar_at.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import duckdb
import pyarrow as pa
import pytest
from pytest_benchmark.fixture import BenchmarkFixture # pyright: ignore[reportMissingTypeStubs]
from pytest_benchmark.fixture import BenchmarkFixture

import vortex as vx

Expand Down
2 changes: 1 addition & 1 deletion vortex-python/benchmark/test_scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import duckdb
import pyarrow as pa
import pytest
from pytest_benchmark.fixture import BenchmarkFixture # pyright: ignore[reportMissingTypeStubs]
from pytest_benchmark.fixture import BenchmarkFixture

import vortex as vx

Expand Down
4 changes: 2 additions & 2 deletions vortex-python/benchmark/test_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
from typing import cast

import pytest
from pytest_benchmark.fixture import BenchmarkFixture # pyright: ignore[reportMissingTypeStubs]
from pytest_benchmark.fixture import BenchmarkFixture

import vortex as vx


@pytest.mark.parametrize("protocol", [4, 5], ids=lambda p: f"p{p}") # pyright: ignore[reportAny]
@pytest.mark.parametrize("protocol", [4, 5], ids=lambda p: f"p{p}")
@pytest.mark.parametrize("operation", ["dumps", "loads", "roundtrip"])
@pytest.mark.benchmark(disable_gc=True)
def test_pickle(
Expand Down
2 changes: 1 addition & 1 deletion vortex-python/check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pushd $ROOT/vortex-python
maturin develop
ruff format --check
ruff check
basedpyright
ty check
popd

pushd $ROOT/docs
Expand Down
44 changes: 22 additions & 22 deletions vortex-python/clickbench.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import vortex as vx

# 0: No., 1: SQL, 2: Polars
queries: list[tuple[str, str, Callable[[pl.LazyFrame], Any]]] = [ # pyright: ignore[reportExplicitAny]
queries: list[tuple[str, str, Callable[[pl.LazyFrame], Any]]] = [
("Q0", "SELECT COUNT(*) FROM hits;", lambda x: x.select(pl.len()).collect().height),
(
"Q1",
Expand All @@ -35,17 +35,17 @@
(
"Q3",
"SELECT AVG(UserID) FROM hits;",
lambda x: x.select(pl.col("UserID").mean()).collect().item(), # pyright: ignore[reportAny]
lambda x: x.select(pl.col("UserID").mean()).collect().item(),
),
(
"Q4",
"SELECT COUNT(DISTINCT UserID) FROM hits;",
lambda x: x.select(pl.col("UserID").n_unique()).collect().item(), # pyright: ignore[reportAny]
lambda x: x.select(pl.col("UserID").n_unique()).collect().item(),
),
(
"Q5",
"SELECT COUNT(DISTINCT SearchPhrase) FROM hits;",
lambda x: x.select(pl.col("SearchPhrase").n_unique()).collect().item(), # pyright: ignore[reportAny]
lambda x: x.select(pl.col("SearchPhrase").n_unique()).collect().item(),
),
(
"Q6",
Expand Down Expand Up @@ -187,7 +187,7 @@
(
"Q20",
"SELECT COUNT(*) FROM hits WHERE URL LIKE '%google%';",
lambda x: x.filter(pl.col("URL").str.contains("google")).select(pl.len()).collect().item(), # pyright: ignore[reportAny]
lambda x: x.filter(pl.col("URL").str.contains("google")).select(pl.len()).collect().item(),
),
(
"Q21",
Expand Down Expand Up @@ -511,7 +511,7 @@
]


def run_timings(lf: pl.LazyFrame, name: str, src: str, load_time: int | None) -> dict[str, Any] | None: # pyright: ignore[reportExplicitAny]
def run_timings(lf: pl.LazyFrame, name: str, src: str, load_time: int | None) -> dict[str, Any] | None:
queries_times: list[list[float | None]] = []
for q in queries:
# if q[0] == "Q19":
Expand All @@ -523,7 +523,7 @@ def run_timings(lf: pl.LazyFrame, name: str, src: str, load_time: int | None) ->
for _ in range(3):
start = timeit.default_timer()
try:
result = q[2](lf) # pyright: ignore[reportAny]
result = q[2](lf)
except Exception as e:
print("Failed", e)
result = None
Expand Down Expand Up @@ -564,22 +564,22 @@ def run_timings(lf: pl.LazyFrame, name: str, src: str, load_time: int | None) ->

PARSER = argparse.ArgumentParser()

PARSER.add_argument( # pyright: ignore[reportUnusedCallResult]
PARSER.add_argument(
"--path",
type=str,
default="hits.parquet",
help="Path to the parquet file",
)

PARSER.add_argument( # pyright: ignore[reportUnusedCallResult]
PARSER.add_argument(
"--formats",
nargs="+",
choices=("vortex", "parquet"),
default=None,
help="Formats to run",
)

PARSER.add_argument( # pyright: ignore[reportUnusedCallResult]
PARSER.add_argument(
"-q",
"--queries",
nargs="+",
Expand All @@ -588,36 +588,36 @@ def run_timings(lf: pl.LazyFrame, name: str, src: str, load_time: int | None) ->
help="Queries to run",
)

PARSER.add_argument("-i", "--iterations", type=int, default=3, help="Number of iterations to run") # pyright: ignore[reportUnusedCallResult]
PARSER.add_argument("-i", "--iterations", type=int, default=3, help="Number of iterations to run")


def main(args: argparse.Namespace):
assert isinstance(args.path, str) # pyright: ignore[reportAny]
assert isinstance(args.queries, list) # pyright: ignore[reportAny]
assert isinstance(args.formats, list | None) # pyright: ignore[reportAny]
assert isinstance(args.path, str)
assert isinstance(args.queries, list)
assert isinstance(args.formats, list | None)

if not os.path.exists(args.path):
raise ValueError(f"File {args.path} does not exist")

results: defaultdict[str, list[float]] = defaultdict(list)

def run_queries(format: str, lf: pl.LazyFrame):
for q in args.queries: # pyright: ignore[reportAny]
for q in args.queries:
assert isinstance(q, int)

timings = []
for _ in range(args.iterations): # pyright: ignore[reportAny]
for _ in range(args.iterations):
start = timeit.default_timer()
try:
_result: Callable[[pl.LazyFrame], Any] = queries[q][2](lf) # pyright: ignore[reportExplicitAny, reportAny]
_result: Callable[[pl.LazyFrame], Any] = queries[q][2](lf)
except Exception as e:
print(f"Failed Q{q}", e)
timings.append(timeit.default_timer() - start) # pyright: ignore[reportUnknownMemberType]
average = sum(timings) / len(timings) # pyright: ignore[reportUnknownArgumentType]
timings.append(timeit.default_timer() - start)
average = sum(timings) / len(timings)
results[format].append(average)
print(f"{format} Q{q}", average)

if args.formats is None or "vortex" in args.formats: # pyright: ignore[reportUnknownMemberType]
if args.formats is None or "vortex" in args.formats:
vx_base, _ = os.path.splitext(args.path)
vx_path = f"{vx_base}.vortex"

Expand All @@ -628,7 +628,7 @@ def run_queries(format: str, lf: pl.LazyFrame):

def _iter():
for i in range(pf.num_row_groups):
arr = pf.read_row_group(i).to_struct_array() # pyright: ignore[reportUnknownMemberType]
arr = pf.read_row_group(i).to_struct_array()
arr = vx.Array.from_arrow(arr)
yield arr

Expand All @@ -638,7 +638,7 @@ def _iter():
lf = vx.open(vx_path).to_polars()
run_queries("vortex", lf)

if args.formats is None or "parquet" in args.formats: # pyright: ignore[reportUnknownMemberType]
if args.formats is None or "parquet" in args.formats:
lf = pl.scan_parquet(args.path)
run_queries("parquet", lf)

Expand Down
8 changes: 6 additions & 2 deletions vortex-python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -91,5 +91,9 @@ dev = [
"protobuf>=6.33.5",
]

[tool.basedpyright]
exclude = ["python/vortex/_lib/store/**.pyi"]
[tool.ty.environment]
root = ["python", "."]

[tool.ty.src]
# Vendored obstore stubs are not checked.
exclude = ["python/vortex/_lib/store/**/*.pyi"]
Loading
Loading