Skip to content

Add per-index exclude-newer overrides for PyPI extra indexes#6360

Draft
pavelzw wants to merge 1 commit into
mainfrom
claude/exciting-feynman-dyq7at
Draft

Add per-index exclude-newer overrides for PyPI extra indexes#6360
pavelzw wants to merge 1 commit into
mainfrom
claude/exciting-feynman-dyq7at

Conversation

@pavelzw

@pavelzw pavelzw commented Jun 14, 2026

Copy link
Copy Markdown
Collaborator

Description

This PR adds support for per-index exclude-newer overrides on PyPI extra indexes, allowing users to customize the package cutoff date for individual indexes. This is particularly useful for private mirrors that do not expose upload-time metadata.

Key Changes:

  1. New PypiExtraIndex struct (crates/pixi_manifest/src/pypi/pypi_options.rs):

    • Replaces Vec<Url> with Vec<PypiExtraIndex> to carry both URL and optional per-index exclude-newer override
    • Implements custom serialization to maintain backward compatibility (bare URL strings when no override is set)
    • Provides from_url() constructor for simple cases
  2. New IndexExcludeNewer enum (crates/pixi_spec/src/exclude_newer.rs):

    • Allows disabling the cutoff entirely (false) or specifying a custom cutoff for an index
    • Differs from ExcludeNewer by supporting explicit disabling, which is required for indexes without upload-time metadata
  3. TOML deserialization support (crates/pixi_manifest/src/toml/pypi_options.rs):

    • Implements custom deserializers for PypiExtraIndex and IndexExcludeNewer
    • Supports both simple URL strings and inline tables: { url = "...", exclude-newer = ... }
    • Validates that exclude-newer = true is rejected with a helpful error message
  4. uv integration (crates/pixi_uv_conversions/src/conversions.rs):

    • Maps per-index overrides to uv's ExcludeNewerOverride on each Index
    • Properly handles None (inherit global), Disabled, and custom cutoff values
  5. Schema updates (schema/model.py and JSON schemas):

    • Added ExtraIndexInlineTable definition
    • Updated extra-index-urls to accept both URL strings and inline tables
    • Updated documentation with examples and guidance

Example Usage:

[tool.pixi.pypi-options]
extra-index-urls = [
    "https://pypi.org/simple",                                    # inherits global cutoff
    { url = "https://internal/simple", exclude-newer = false },   # disables cutoff
    { url = "https://other/simple", exclude-newer = "2025-01-01" } # custom cutoff
]

How Has This Been Tested?

  • Added comprehensive unit tests in crates/pixi_manifest/src/toml/pypi_options.rs:
    • test_deserialize_extra_index_urls_with_exclude_newer: validates parsing of all three formats
    • test_extra_index_exclude_newer_true_is_error: ensures true is rejected with helpful error
    • test_extra_index_missing_url_is_error: validates required url field
  • Added integration test in crates/pixi_uv_conversions/src/conversions.rs:
    • test_per_index_exclude_newer_is_applied: verifies per-index overrides are correctly applied to uv indexes
  • Updated existing snapshot tests to reflect new PypiExtraIndex structure
  • All schema changes validated against model definitions

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have added sufficient tests to cover my changes
  • I have verified that changes that would impact the JSON schema have been made in schema/model.py

https://claude.ai/code/session_01DbjDfm5GLLeu9pR8XQy7hy

Allow `[pypi-options].extra-index-urls` entries to be inline tables that
carry their own `exclude-newer`, mirroring conda's `ChannelInlineTable`:

    extra-index-urls = [
        "https://pypi.org/simple",
        { url = "https://internal/simple", exclude-newer = false },
        { url = "https://other/simple", exclude-newer = "2025-01-01" },
    ]

`exclude-newer = false` disables the cutoff for that index, which fixes
solves against private mirrors that do not expose PEP 700 `upload-time`
(where any global cutoff would otherwise exclude every file). A
date/duration/timestamp sets a per-index cutoff; omitting it inherits the
global one.

The override is plumbed onto uv's `Index.exclude_newer` and applied during
resolution. pixi now opts into uv's `index-exclude-newer` preview feature so
this works without panicking on the uninitialized preview state.

Closes #6158

https://claude.ai/code/session_01DbjDfm5GLLeu9pR8XQy7hy
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.

2 participants