Skip to content

Drop the Arrow dependency, document that this is Feather V1 - #25

Merged
davidanthoff merged 4 commits into
mainfrom
v1-only-drop-arrow
Aug 31, 2026
Merged

davidanthoff merged 4 commits into
mainfrom
v1-only-drop-arrow

Conversation

@davidanthoff

@davidanthoff davidanthoff commented Aug 31, 2026 •

Copy link
Copy Markdown
Member

Companion to queryverse/FeatherLib.jl#24. Together they make FeatherFiles installable again.

Why it was broken

FeatherFiles reached into the old Arrow.jl for ArrowVector, DictEncoding, unsafe_isnull and unsafe_getvalue. That package is gone — its registry slot (UUID 69666777-…) was taken over at version 0.3.0 by the unrelated Apache Arrow.jl — so Arrow = "0.2.1 - 0.4" resolved to a package with none of those names, and precompilation failed.

What this does

FeatherLib now vendors that code and exports what is needed, so the four references in missing-conversion.jl point at FeatherLib.ArrowCompat and the dependency goes away. No logic change.

Dropping the old Arrow dependency also lifts the CategoricalArrays ≤ 0.8 cap it imposed on every FeatherFiles and Queryverse user — the substance of #23. (Leaving that issue open for you to close.)

The README now says plainly that this package handles Feather V1 only. V1 and V2 are different formats: V2 is exactly the Arrow IPC file format, is what every current tool writes whether the file is named .feather or .arrow, and Apache deprecated V1 reading and writing at Arrow 25.0.0 with removal planned. Anyone hitting a modern .feather file needs Arrow.jl, not this package.

julia compat moves to 1.10 so the CI matrix only covers versions that actually run the testitem tooling — a deliberate deviation from the ecosystem-wide "restore pre-migration compat" decision, for this package.

Also: show no longer locks the file

Displaying a feather file left it memory mapped, so on Windows display(load("x.feather")) in the REPL kept it locked against deletion until the GC happened to run. The three show methods consume the rows they need and return — all three TableShowUtils printers are eager — so they now close! the ResultSet as soon as the printer is done.

The read-and-convert step is factored out of getiterator into read_converted, with a withtable helper that opens, prints and closes. getiterator is unchanged: it hands the iterator to the caller, so it cannot close anything, and its comment now points callers who want the file released at get_columns_copy_using_missing.

Measured, with no GC.gc() anywhere:

Path File afterwards
get_columns_copy_using_missing — what DataFrame(load(f)) uses released
show / HTML / dataresource released (was locked)
getiterator then collect still mapped, by design

Verification

No longer blocked — FeatherLib v1.0.0 is released and in the General registry, so this resolves.

Verified against the registered FeatherLib 1.0.0 rather than a deved path: a clean environment picks up FeatherLib v1.0.0 with CategoricalArrays 1.1.1, FlatBuffers 0.6.2 and no Arrow in the manifest. All 6 testitems pass on Julia 1.12, and the rendered show output is unchanged (the existing testitem asserts the exact string).

Also verified across both packages that format routing works purely by magic bytes, with no FileIO registry change: a V1 file named .feather resolves to format"Feather" and loads through FeatherFiles, while a V2 file named .feather resolves to format"Arrow".

🤖 Generated with Claude Code

davidanthoff and others added 4 commits August 30, 2026 22:46
FeatherFiles reached into the old Arrow.jl for ArrowVector, DictEncoding,
unsafe_isnull and unsafe_getvalue. That package is gone — its registry slot
was taken over at 0.3.0 by the unrelated Apache Arrow.jl — so
`Arrow = "0.2.1 - 0.4"` resolved to a package with none of those names and
precompilation failed.

FeatherLib now vendors that code and exports what is needed, so point the
four references in missing-conversion.jl at FeatherLib.ArrowCompat and drop
the dependency entirely. No logic change.

Removing the old Arrow dependency also lifts the CategoricalArrays <= 0.8
cap it imposed on every FeatherFiles and Queryverse user, which is the
substance of #23.

The README now says plainly that this package handles Feather V1 only. V1
and V2 are different formats — V2 is exactly the Arrow IPC file format, is
what every current tool writes whether the file is named .feather or .arrow,
and is deprecated-for-removal upstream as of Arrow 25.0.0. Readers hitting a
modern .feather file need Arrow.jl, not this package.

julia compat moves to 1.10 so the CI matrix only covers versions that
actually run the testitem tooling.

Note for CI: this needs FeatherLib 1.0, which is not registered yet, so the
build here stays red until that release is tagged. Verified locally against
the FeatherLib branch — all 25 tests pass on Julia 1.12 with
CategoricalArrays 1.1.1 and no Arrow in the manifest.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Feather stores dates and times as the Arrow wire types Datestamp, Timestamp
and TimeOfDay. Those leaked all the way to users: DataFrame(load("x.feather"))
gave a column of FeatherLib.ArrowCompat.Datestamp rather than Date. This is
Feather.jl#99, still open upstream. Unwrap them here, in the layer users
actually see, rather than in FeatherLib -- FeatherLib is deliberately a
faithful low-level mirror of the file layout.

Doing so uncovered a second bug. getiterator decided whether a column was
nullable with `col_eltype.b <: Missing`, but Julia does not order the members
of a Union predictably: for Union{Datestamp,Missing} the non-Missing half is
`.a`, so that test silently skipped the column and it came back neither
DataValue-wrapped nor converted. Use Base.nonmissingtype instead, which is
correct whatever the ordering.

get_columns_copy_using_missing copies every column out, so it now close!s its
ResultSet before returning and the file is immediately deletable -- no GC
required, which is what matters on Windows. getiterator cannot do the same:
its rows read straight through the memory mapping, so the mapping has to
outlive the iterator.

That distinction is why the long-commented-out rm() in the first testitem
stays commented out. It sits after a getiterator call, and it is that
mapping, not the one from get_columns_copy_using_missing, that holds the
file. The comment there now says so. The deterministic half has its own
testitem, which does rm() with no GC at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Displaying a feather file left it memory mapped, so on Windows merely
running `display(load("x.feather"))` in the REPL kept the file locked
against deletion or overwriting until the GC happened to run.

Unlike getiterator, which hands the iterator to the caller and therefore
cannot close anything, the show methods consume the rows they need and
return. All three TableShowUtils printers are eager -- printtable and
printHTMLtable collect or count a bounded prefix, printdataresource
iterates and writes -- so the ResultSet can be closed as soon as the
printer returns.

Factor the read-and-convert step out of getiterator into read_converted,
and add a withtable helper that opens a ResultSet, runs the printer, and
close!s in a finally. getiterator keeps its existing behaviour; its comment
now points callers who want the file released at
get_columns_copy_using_missing, which copies and then closes.

Verified with no GC.gc() anywhere: after each of the three show methods the
file is immediately deletable, and the rendered output is unchanged (the
existing testitem asserts the exact sprint(show, ...) string).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The "Date and time columns" testitem checked that a non-temporal column
survives untouched with `typeof(rows[1].n) == Int64`, but the column is
written as [1, 2], whose eltype is Int -- and Int is Int32 on the x86 CI
legs. All five failures in the last run were this one assertion, on
ubuntu-x86 and windows-x86; every x64 leg passed.

Verified on 32-bit Julia locally (julia +release~x86, Sys.WORD_SIZE=32,
Int=Int32) rather than only on x64: 45/45 tests pass there, and the x64
run still passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@davidanthoff
davidanthoff merged commit 7e61bd2 into main Aug 31, 2026
31 checks passed
@davidanthoff
davidanthoff deleted the v1-only-drop-arrow branch August 31, 2026 23:58
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