Skip to content

fix(gfql): cudf 26.02 dt.date/dt.time removal in temporal predicates (replaces #1855) - #1862

Merged
lmeyerov merged 4 commits into
masterfrom
fix/gfql-cudf-2602-compat
Aug 9, 2026
Merged

fix(gfql): cudf 26.02 dt.date/dt.time removal in temporal predicates (replaces #1855)#1862
lmeyerov merged 4 commits into
masterfrom
fix/gfql-cudf-2602-compat

Conversation

@lmeyerov

@lmeyerov lmeyerov commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Replaces #1855, which was built on a false premise — reviewer caught it.

The premise was wrong

#1855 treated "cudf works but cupy cannot JIT" as a capability tier and threaded a two-tier gate through five modules. It is not a tier, it is a broken install:

  • cudf-cu12 hard-requires cupy-cuda12x>=13.6.0
  • importing cudf imports cupy
  • cudf.Series.values **returns a `cupy.ndarray```

The only place that configuration exists is a dev box with cudf installed and no GPU. I was coding graceful degradation for my own half-install.

What that removes, with no perf loss

Dropped: lazy_cupy_import's NVRTC probe, CudfRuntimeCaps, and the gating in engine_arrays.py, mercator.py, ring/util.py. Also _unary_ufunc_on_series, which was purely the cupy-broken fallback — it collapses to np_fn(s), exactly what master already does. No perf is lost, because those fallbacks cannot fire in a valid install.

row/pipeline.py needs no change either: hasattr(f, "ceil") already returns False on 26.02, falls through to np.ceil(f), dispatches to cupy, works.

What was actually real

comparison.py called Series.dt.date / Series.dt.time unguarded and cudf 26.02 removed both. The fallbacks rely on a stated equivalence — a day-truncated datetime compares against a midnight Timestamp exactly as a date does; a time-of-day timedelta against a Timedelta exactly as a time does — with the scalar paired to the dtype the truncation produces, so no comparison silently changes meaning.

168 lines across 8 files → 21 lines in 1, plus a pin that simulates the accessor removal so the regression is caught without a GPU.

I'd close #1855 in favour of this.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MF7uRZLKZaD6Q9FGWSmyXi

lmeyerov and others added 4 commits August 8, 2026 14:12
Replaces #1855, which was built on a false premise. That PR treated 'cudf works
but cupy cannot JIT' as a capability tier and threaded a two-tier gate through
five modules. It is not a tier -- it is a broken install: cudf-cu12 HARD-REQUIRES
cupy-cuda12x>=13.6.0, importing cudf imports cupy, and cudf.Series.values RETURNS
a cupy.ndarray. The only place that configuration exists is a dev box with cudf
installed and no GPU.

So the capability machinery goes: lazy_cupy_import's NVRTC probe, CudfRuntimeCaps,
and the gating in engine_arrays.py, mercator.py, ring/util.py. Also
_unary_ufunc_on_series, which was purely the cupy-broken fallback and collapses
to np_fn(s) -- what master already does. No perf is lost, because those fallbacks
cannot fire in a valid install.

row/pipeline.py needs NO change either: hasattr(f, 'ceil') already returns False
on 26.02, falls to np.ceil(f), dispatches to cupy, and works.

What was real: comparison.py called Series.dt.date / .dt.time unguarded, and
26.02 removed both. 168 lines across 8 files becomes 21 lines in 1, plus a pin
that simulates the accessor removal so it is caught without a GPU.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MF7uRZLKZaD6Q9FGWSmyXi
The first pin asserted pandas behaviour and never called comparison.py, so the
changed lines had ZERO coverage and CI's changed-line gate correctly failed it --
the fix was shipping untested while the test passed.

It now hides .dt.date/.dt.time (pd.Series.dt is a CachedAccessor, so the wrapper
goes around the value it produces, not the descriptor) and drives GT(DateValue)
and GT(TimeValue) end to end, exercising both fallbacks and the scalar pairing.
It then undoes the patch and asserts the un-hidden path agrees, which makes the
fallback an equivalence rather than just 'does not crash'.

All three changed ranges are now covered.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MF7uRZLKZaD6Q9FGWSmyXi
…ery engine

Review points, both resolved by removing the branch rather than typing around it.

STATIC TYPES: there is now nothing version-dependent to type. Series.dt.date /
.dt.time are gone in cudf 26.02, but the substitute -- day-truncate, and
time-of-day as a timedelta -- exists on every engine and every version. So the
predicate just always uses it: no hasattr probe, no minimum-cudf assumption, and
nothing to keep out of the public types pending a version floor. Verified the
substitute is an EQUIVALENCE for both ordering and boundary equality, which is
what makes the branch removable rather than merely convenient.

CROSS-PLATFORM: the pin is parametrized over pandas and cudf and asserts
identical answers, including the equality boundaries where object-dtype dates and
datetime64 could diverge. The earlier pin simulated the accessor removal on
pandas only, which is now moot -- nothing calls the removed accessor.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MF7uRZLKZaD6Q9FGWSmyXi
Real cudf 26.02 does not REMOVE Series.dt.date/.dt.time -- they exist and raise
NotImplementedError on access. hasattr only swallows AttributeError, so it does
not screen them: the capability branch this PR started with would have CRASHED
rather than fallen back, and the pandas simulation that covered it asserted the
wrong exception and passed anyway.

Verified on GB10, cudf 26.02.01: both accessors raise NotImplementedError, and
43 temporal tests pass with the branch gone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MF7uRZLKZaD6Q9FGWSmyXi
@lmeyerov
lmeyerov merged commit 568491c into master Aug 9, 2026
69 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant