Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

In reinstall-all if shared_lib python is invalid, create it instead of upgrading to avoid error. #634

Merged
merged 5 commits into from
Feb 23, 2021

Conversation

itsayellow
Copy link
Contributor

@itsayellow itsayellow commented Feb 20, 2021

  • I have added an entry to docs/changelog.md

Summary of changes

Closes #633.

#554 introduced a regression where pipx reinstall-all would try to upgrade the shared_libs without verifying that they were still valid (i.e. their python link still existed). If the python did not exist, this would output an error.

This PR first checks if the shared_libs are valid: if they are, it forces an upgrade, if they are not, it instead creates (or re-creates) the shared libs.

Test plan

Tested by running

rm ~/.local/pipx/shared/bin/python3
ln -s /path/that/does/not/exist ~/.local/pipx/shared/bin/python3
pipx reinstall-all

This PR:
(no error)

pipx 0.16.0.0:

Failed to upgrade shared libraries
Traceback (most recent call last):
  File "/usr/local/Cellar/pipx/0.16.0.0/libexec/lib/python3.9/site-packages/pipx/shared_libs.py", line 92, in upgrade
    upgrade_process = run_subprocess(
  File "/usr/local/Cellar/pipx/0.16.0.0/libexec/lib/python3.9/site-packages/pipx/util.py", line 123, in run_subprocess
    completed_process = subprocess.run(
  File "/usr/local/Cellar/python@3.9/3.9.1_8/Frameworks/Python.framework/Versions/3.9/lib/python3.9/subprocess.py", line 501, in run
    with Popen(*popenargs, **kwargs) as process:
  File "/usr/local/Cellar/python@3.9/3.9.1_8/Frameworks/Python.framework/Versions/3.9/lib/python3.9/subprocess.py", line 947, in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
  File "/usr/local/Cellar/python@3.9/3.9.1_8/Frameworks/Python.framework/Versions/3.9/lib/python3.9/subprocess.py", line 1819, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: '/Users/mclapp/.local/pipx/shared/bin/python'

[...]

@@ -84,7 +84,10 @@ def reinstall_all(
skip: Sequence[str],
) -> ExitCode:
"""Returns pipx exit code."""
pipx.shared_libs.shared_libs.upgrade(verbose=verbose)
if pipx.shared_libs.shared_libs.is_valid:
Copy link
Member

@cs01 cs01 Feb 20, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about moving this check to the internals of the upgrade method? That way any other calls to upgrade wouldn’t be subject to this issue either.

Copy link
Contributor Author

@itsayellow itsayellow Feb 20, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I thought about that. It's probably good to do.

I hesitated to do that because it makes the code inside of shared_libs.py look almost like a circular reference (even though it's not) because create ends with upgrade, and then with that code change upgrade would fall back to create. It's actually not a circular reference, just worried it might read funny.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, made that fix. 👍

@@ -73,6 +73,10 @@ def needs_upgrade(self) -> bool:
def upgrade(
self, *, pip_args: Optional[List[str]] = None, verbose: bool = False
) -> None:
if not self.is_valid:
Copy link
Member

@uranusjr uranusjr Feb 20, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_valid would still return True if a Windows venv’s underlying interpreter goes away. I wonder if we should actually try to execute the virtual environment instead (e.g. run python -c "import sys; print(sys.executable)"). This can probably be fixed separately though (when someone complains about it).

Also create() already has an is_valid guard.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also create() already has an is_valid guard.

I think that should be ok, do you see a problem? I put this one in upgrade for when external code calls upgrade itself, not for when create() calls upgrade().

Copy link
Member

@uranusjr uranusjr Feb 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you ask me, create() probably shouldn’t contain an is_valid guard, since it feels weird to me calling create() may not actually create anything. But removing that guard probably has consequences.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah that's a different matter. I think it was meant as an optimization to save time. We use shared_libs.create() every time we create a new venv. I think probably this is meant to prevent having to recreate the shared_libs if it's already fine.

We could move the guard to the calling code in venv.py, which would functionally be the same thing just factored differently. Personally I'm ok either way.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let’s keep this guard here and aim to remove the one in create() then.

@itsayellow itsayellow requested review from cs01 and uranusjr February 22, 2021 07:10
@itsayellow itsayellow merged commit 87efddf into pypa:master Feb 23, 2021
@itsayellow itsayellow deleted the shared-bad-python branch February 23, 2021 19:56
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.

Updating Python from 3.9.1_4 to 3.9.1_8 causes No such file or directory
3 participants