Skip to content

Keep relative archive bindings stable - #28

Merged
tvanreenen merged 2 commits into
mainfrom
stable-archive-bindings-0.7.0
Aug 23, 2026
Merged

Keep relative archive bindings stable#28
tvanreenen merged 2 commits into
mainfrom
stable-archive-bindings-0.7.0

Conversation

@tvanreenen

Copy link
Copy Markdown
Owner

What this changes

This PR makes a store's archive binding stable when the caller supplies a relative path.

save(path) and VectorStore.open(path) now anchor a relative path to the working directory in effect at that call. The resulting file_path is absolute, so later save() and reload() operations continue using the same lexical path even if the application changes its working directory.

Extensionless paths still receive the .npz suffix. Path anchoring does not resolve symlinks or remove .. components, so those parts keep their normal filesystem meaning.

Why this is needed

The persistence API describes file_path as the store's current binding. Before this change, a relative binding was saved as a relative Path. Every later pathless save or reload interpreted that value against the process's current working directory.

That allowed unrelated application state to redirect persistence. A store saved to "vectors.npz" in one directory could write a new vectors.npz somewhere else after os.chdir(). A store opened from a relative path could likewise reload a different archive or fail because the new directory had no matching file.

Anchoring once gives the binding a stable identity for the lifetime of the store. It also makes file_path an accurate, self-contained description of where the store will read or write.

Implementation details

The existing private path-normalization boundary now applies extension handling and then calls Path.absolute(). Python defines Path.absolute() as making a path absolute without normalization or symlink resolution. This is a better fit than Path.resolve(), which would inspect and canonicalize the filesystem target and change the behavior of symlinked or deliberately lexical paths.

Both save(path) and open(path) already pass through this boundary, so no new path state or abstraction is needed. A successful explicit Save As still replaces the binding; a failed Save As still preserves the previous binding. Absolute inputs retain their supplied lexical form.

User impact

Applications that use absolute persistence paths behave as before. Applications that use relative paths now remain attached to the directory where save(path) or open(path) was called, even after a working-directory change.

The observable change is that store.file_path is absolute after binding from a relative path. Code that compares this property with a relative Path should compare it with the corresponding absolute path instead.

An explicit later save(relative_path) remains a Save As operation and anchors that new path against the working directory at the time of that call. Archive format version 1 is unchanged, and existing archives require no migration.

Documentation

  • The README explains when relative paths are anchored, why file_path is absolute, and how working-directory changes affect pathless operations.
  • The migration guide records the same rule for applications moving from the older persistence lifecycle.
  • The 0.7 roadmap now describes stable archive bindings as delivered behavior rather than future work.

Verification

  • uv lock --check
  • ruff check .
  • ruff format --check .
  • mypy src/ benchmarks/
  • 327 tests passed with warnings treated as errors on the locked Python 3.13 environment, with 98% source coverage.
  • 327 tests passed with warnings treated as errors on Python 3.11 and the minimum supported NumPy 1.23.2.
  • New public-behavior tests cover pathless saves after chdir, reloads after chdir, absolute file_path values, extensionless relative paths, and lexical symlink preservation.

Release note

This is the second PR planned for 0.7.0. It does not change the package version or changelog; final release notes will be prepared after the complete 0.7 milestone is merged.

@tvanreenen tvanreenen self-assigned this Aug 23, 2026
@tvanreenen
tvanreenen marked this pull request as ready for review August 23, 2026 02:05
@tvanreenen
tvanreenen merged commit 812be04 into main Aug 23, 2026
6 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