Skip to content

🐛 fix(activation): restore PKG_CONFIG_PATH that was not set before - #3233

Open
darrenhuai wants to merge 2 commits into
pypa:mainfrom
darrenhuai:fix/pkg-config-path-restore
Open

🐛 fix(activation): restore PKG_CONFIG_PATH that was not set before#3233
darrenhuai wants to merge 2 commits into
pypa:mainfrom
darrenhuai:fix/pkg-config-path-restore

Conversation

@darrenhuai

@darrenhuai darrenhuai commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Thanks for contributing, make sure you address all the checklists (for details on how see development documentation)

  • ran the linter to address style issues (tox -e fix)
  • wrote descriptive pull request text
  • ensured there are test(s) validating the fix
  • added news fragment in docs/changelog folder
  • updated/extended the documentation

Activation prepends the environment's lib/pkgconfig to PKG_CONFIG_PATH and saves the previous value so deactivation can put it back. When the variable was not set to begin with — the normal case, since most systems leave it alone — there is no previous value, and bash, fish and PowerShell all read that as "nothing to restore" and leave the environment's directory in place:

$ echo "${PKG_CONFIG_PATH:-<unset>}"
<unset>
$ source env/bin/activate
$ deactivate
$ echo "${PKG_CONFIG_PATH:-<unset>}"
/tmp/env/lib/pkgconfig

So PKG_CONFIG_PATH goes from unset to permanently pointing into a virtual environment that is no longer active, and later builds keep resolving .pc files out of it.

bash and fish save the empty string and then test it with -n, which is false both for "was unset" and for "nothing saved". They now tell the two apart: the variable is unset again when it had not been set, and restored when it had. PowerShell only created _OLD_PKG_CONFIG_PATH when the variable already existed, so its deactivation could not distinguish the cases either; it now always records one, using $null for "was not set", which the existing restore already turns back into a removal.

The same lines built the new value by pasting the old one on unconditionally, so batch, fish, nushell and PowerShell left a trailing separator — an empty entry in the path — when there was nothing to prepend to:

C:\> set PKG_CONFIG_PATH=
C:\> call env\Scripts\activate.bat
C:\> echo %PKG_CONFIG_PATH%
C:\env\lib\pkgconfig;

Each now only writes a separator when something follows it.

csh and activate_this.py already handled both cases and are unchanged — the csh if ($?PKG_CONFIG_PATH) ... else ... and its || unsetenv PKG_CONFIG_PATH are what the others are being brought in line with.

Four round-trip tests (bash, batch, fish, PowerShell) activate with PKG_CONFIG_PATH unset and check the value both while active and after deactivating, so they cover the missing restore and the trailing separator together.

I could reproduce and re-check bash, batch and PowerShell directly — bash also under set -o nounset, since this touches the lines from #3044 — and reverting each change individually puts the corresponding failure back. fish and nushell I could not run locally, so those two rest on review and CI.

One thing I left alone, in case it is worth a separate issue: activate.bat does not call deactivate first the way the bash and PowerShell scripts do, so re-activating stacks PKG_CONFIG_PATH — activate A, then B, then deactivate, and A's lib/pkgconfig stays behind. That is a different cause from the above and this PR neither fixes nor worsens it.

Activation prepends the environment's `lib/pkgconfig` to `PKG_CONFIG_PATH`
and saves the previous value so deactivation can put it back. When the
variable was not set to begin with - which is the normal case, since most
systems leave it alone - there is no previous value to put back, and bash,
fish and PowerShell all read that as "nothing to restore" and leave the
environment's directory in place. `PKG_CONFIG_PATH` goes from unset to
permanently pointing into a virtual environment that is no longer active,
so later builds keep resolving `.pc` files out of it.

bash and fish save the empty string and then test it with `-n`, which is
false for both "unset before" and "nothing saved". They now distinguish the
two: the variable is unset again when it had not been set, and restored when
it had. PowerShell only created its `_OLD_PKG_CONFIG_PATH` when the variable
existed, so deactivation could not tell the two cases apart either; it now
always records one, using `$null` for "was not set", which the existing
restore already turns back into a removal.

The same lines built the new value by pasting the old one on unconditionally,
so batch, fish, nushell and PowerShell ended up with a trailing separator and
an empty entry in the path when there was nothing to prepend to. Each now
only appends a separator when there is something after it.

csh and the Python activator already handled both cases and are unchanged.

Round-trip tests for bash, batch, fish and PowerShell activate with
`PKG_CONFIG_PATH` unset and check the value both while active and after
deactivating.
@darrenhuai

Copy link
Copy Markdown
Contributor Author

The one red job, test 3.13 - windows-2025-vs2026, hit the 30 minute step timeout rather than failing an assertion, and I do not think it is this branch.

It stalled at 98%, in tests/unit/seed/embed/test_bootstrap_link_via_app_data.py::test_seed_link_via_app_data[False] — about 26 minutes between that test starting and the timeout killing the run. That file takes 25s here on this branch. Nothing in the diff outside docs/changelog touches seeding or app data; it is activation script templates and their tests.

The four new tests all ran in that same job before the stall:

[gw1] [ 60%] SKIPPED tests/unit/activation/test_bash.py::test_bash_deactivate_unsets_pkg_config_path_that_was_not_set
[gw1] [ 64%] PASSED  tests/unit/activation/test_batch.py::test_batch_deactivate_unsets_pkg_config_path_that_was_not_set
[gw1] [ 66%] SKIPPED tests/unit/activation/test_fish.py::test_fish_deactivate_unsets_pkg_config_path_that_was_not_set
[gw1] [ 73%] PASSED  tests/unit/activation/test_powershell.py::test_powershell_deactivate_unsets_pkg_config_path_that_was_not_set

1.1s and 1.2s for the two that run on Windows, the other two skipped there as intended. The same commit is green on 3.10, 3.11, 3.12 and 3.14 on the same Windows image, which is not what a real regression from this change would look like.

A re-run should clear it — I cannot trigger one from a fork. Happy to push a change if you would rather see it go green on a fresh run.

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