Summary
The three python-setup actions expose caching through inconsistent inputs — different value shapes,
different defaults, and different behavior when no lock/dependency file exists. A caller who learns the cache
input on one action cannot reuse that knowledge on another, and copying a cache: 'true' call from uv/pixi to pip
fails outright. This issue tracks aligning pip's caching UX with uv/pixi while respecting pip's real constraint:
unlike uv/pixi, it can run with no lock/dependency file to hash.
Current state
| Action |
cache value shape |
Default |
Cache key source |
Behavior with no lock/dep file |
| pip |
setup-python cache type string: pip / pipenv / poetry / '' |
'' (off) |
setup-python hashes pyproject.toml / requirements.txt |
Hard error (action guard + setup-python both fail) |
| uv |
boolean-string: true / false |
true (on) |
uv.lock |
N/A — uv.lock always present |
| pixi |
boolean-string: true / false |
false (off); writes only on main pushes |
pixi.lock hash |
N/A — pixi.lock required |
Relevant code: actions/python-setup/pip/action.yml:12-20 (input), :44-55 (validate-cache guard),
:57-62 (pass-through to actions/setup-python); actions/python-setup/uv/action.yml:12-19;
actions/python-setup/pixi/action.yml:21-29.
Problems
- [H1] Inconsistent input shape.
cache: 'true' is valid for uv/pixi but invalid for pip — pip forwards
the value to actions/setup-python's cache: input, which only accepts pip / pipenv / poetry / empty and
rejects true (Caching for 'true' is not supported). A user copying a uv/pixi invocation to pip gets a hard
failure with a confusing message.
- [H2] Divergent defaults. pip is off (
''), uv is on ('true'), pixi is off ('false'). There is no
coherent repo-wide policy, so callers cannot assume what "no cache: given" does.
- [M1] pip's on-path requires a dependency file. With caching enabled but no
pyproject.toml /
requirements.txt, both the action's own guard (pip/action.yml:49-52) and actions/setup-python error out.
uv/pixi have no equivalent failure mode because a lockfile is always present. This is exactly why pip cannot
simply default caching on.
- [M2] Contract conflict blocks a naive default flip.
test-pip-cache-validation-error deliberately asserts
a failure when cache is set but no dependency file exists. A composite action cannot distinguish an
explicit value from a defaulted one, so "on by default" and "explicit-cache-without-a-file errors" cannot both
hold as-is.
Proposed alignment
Recommended design — uniform UX, keeps pip's power, backward compatible where possible:
- [T1] Accept a boolean-string on pip's
cache input like uv/pixi: 'true' → use 'pip' internally,
'false' / '' → off. Keep accepting the literal setup-python values ('pip', 'pipenv', 'poetry') as a
pass-through for power users and backward compatibility. Update the input description/examples accordingly.
- [T2] Apply a consistent default. The requested direction is on-by-default (
cache: 'true') to match uv
— but note pixi is currently 'false'; a repo-wide on-vs-off decision is needed (see Q1).
- [T3] Make on-by-default safe: when caching is requested but no
pyproject.toml / requirements.txt is
present, auto-disable caching with a ::warning:: and pass an empty cache to setup-python (so setup-python
does not error either), instead of failing. Required because pip — unlike uv/pixi — can legitimately run with no
dependency file.
- [T4] Reconcile the negative test: update
test-pip-cache-validation-error to the new auto-skip behavior
(assert the run succeeds and emits the warning), since a hard error is incompatible with a safe
on-by-default.
- [T5] Confirm
test-pip-basic and test-pip-no-dependency-file still pass with cache on-by-default (they
should, via the T3 auto-skip); add assertions for the auto-skip path.
- [T6] Update docs (
docs/python-setup/pip.md, the caching rows in docs/python-setup/index.md, and any
README / getting-started references) for the new input shape, default, and auto-skip behavior, and note the
uv/pixi parity.
Backward compatibility & versioning
Changing the cache input's accepted values and default is a behavior change. Per the repo's Breaking Changes
policy, changing input defaults/semantics warrants a major version bump for the pip action (pip/v2). Keeping
the 'pip' / 'pipenv' / 'poetry' pass-through preserves existing callers; the default flip is the breaking
part (a consumer relying on "no cache unless I ask" would start caching). Call this out in the release notes and
move the pip/vN major tag accordingly.
Open questions (need a decision)
- [Q1] Repo-wide default policy: default all three cache on (flip pixi to
'true' as well), or align
pip to pixi's off? The requested direction is pip on-by-default like uv, but pixi is currently off — pick one
policy for the repo.
- [Q2] On a missing dependency file with cache on: auto-skip with warning (recommended, required for a safe
on-by-default) vs keep a hard error?
- [Q3] Keep the advanced pip cache types (
pipenv, poetry) overloaded on the same cache input, or split
them into a separate cache-dependency / cache-type input to keep cache a clean boolean?
Out of scope
- Changing uv/pixi cache internals — they already share the boolean-string shape. This issue aligns pip to that
shape and settles the repo-wide default policy.
Summary
The three
python-setupactions expose caching through inconsistent inputs — different value shapes,different defaults, and different behavior when no lock/dependency file exists. A caller who learns the
cacheinput on one action cannot reuse that knowledge on another, and copying a
cache: 'true'call from uv/pixi to pipfails outright. This issue tracks aligning pip's caching UX with uv/pixi while respecting pip's real constraint:
unlike uv/pixi, it can run with no lock/dependency file to hash.
Current state
cachevalue shapepip/pipenv/poetry/''''(off)pyproject.toml/requirements.txttrue/falsetrue(on)uv.lockuv.lockalways presenttrue/falsefalse(off); writes only onmainpushespixi.lockhashpixi.lockrequiredRelevant code:
actions/python-setup/pip/action.yml:12-20(input),:44-55(validate-cache guard),:57-62(pass-through toactions/setup-python);actions/python-setup/uv/action.yml:12-19;actions/python-setup/pixi/action.yml:21-29.Problems
cache: 'true'is valid for uv/pixi but invalid for pip — pip forwardsthe value to
actions/setup-python'scache:input, which only acceptspip/pipenv/poetry/ empty andrejects
true(Caching for 'true' is not supported). A user copying a uv/pixi invocation to pip gets a hardfailure with a confusing message.
''), uv is on ('true'), pixi is off ('false'). There is nocoherent repo-wide policy, so callers cannot assume what "no
cache:given" does.pyproject.toml/requirements.txt, both the action's own guard (pip/action.yml:49-52) andactions/setup-pythonerror out.uv/pixi have no equivalent failure mode because a lockfile is always present. This is exactly why pip cannot
simply default caching on.
test-pip-cache-validation-errordeliberately assertsa failure when
cacheis set but no dependency file exists. A composite action cannot distinguish anexplicit value from a defaulted one, so "on by default" and "explicit-cache-without-a-file errors" cannot both
hold as-is.
Proposed alignment
Recommended design — uniform UX, keeps pip's power, backward compatible where possible:
cacheinput like uv/pixi:'true'→ use'pip'internally,'false'/''→ off. Keep accepting the literal setup-python values ('pip','pipenv','poetry') as apass-through for power users and backward compatibility. Update the input description/examples accordingly.
cache: 'true') to match uv— but note pixi is currently
'false'; a repo-wide on-vs-off decision is needed (see Q1).pyproject.toml/requirements.txtispresent, auto-disable caching with a
::warning::and pass an emptycacheto setup-python (so setup-pythondoes not error either), instead of failing. Required because pip — unlike uv/pixi — can legitimately run with no
dependency file.
test-pip-cache-validation-errorto the new auto-skip behavior(assert the run succeeds and emits the warning), since a hard error is incompatible with a safe
on-by-default.
test-pip-basicandtest-pip-no-dependency-filestill pass with cache on-by-default (theyshould, via the T3 auto-skip); add assertions for the auto-skip path.
docs/python-setup/pip.md, the caching rows indocs/python-setup/index.md, and anyREADME / getting-started references) for the new input shape, default, and auto-skip behavior, and note the
uv/pixi parity.
Backward compatibility & versioning
Changing the
cacheinput's accepted values and default is a behavior change. Per the repo's Breaking Changespolicy, changing input defaults/semantics warrants a major version bump for the pip action (
pip/v2). Keepingthe
'pip'/'pipenv'/'poetry'pass-through preserves existing callers; the default flip is the breakingpart (a consumer relying on "no cache unless I ask" would start caching). Call this out in the release notes and
move the
pip/vNmajor tag accordingly.Open questions (need a decision)
'true'as well), or alignpip to pixi's off? The requested direction is pip on-by-default like uv, but pixi is currently off — pick one
policy for the repo.
on-by-default) vs keep a hard error?
pipenv,poetry) overloaded on the samecacheinput, or splitthem into a separate
cache-dependency/cache-typeinput to keepcachea clean boolean?Out of scope
shape and settles the repo-wide default policy.