Repository: pypa/pipx

Issue:
`pipx upgrade -e ...` does not parse correctly
<!--
Thank you for filing a bug! Please feel free to answer as much or as little of this template as you can.

Please check pipx's Troubleshooting page to see if any of those solutions help solve your issue:
https://pipx.pypa.io/stable/troubleshooting/
-->

**Describe the bug**

I can't seem to figure out a way to upgrade a package that I installed locally with `pipx install --editable /path/to/my_package`. It seems like  each way results in an invalid `pip install --upgrade`. Possibly related to https://github.com/pypa/pipx/issues/1441


<!-- Please be as detailed as possible! -->

**How to reproduce**

Install `my_package` in the current directory `pipx install --editable `

Perhaps the package requirements have changed, so we want to update `my_package`. First we attempt to upgrade using a relative path

```
(.venv) richard@Richards-MBP % pipx upgrade -e -v .
pipx >(setup:1110): pipx version is 1.7.1
pipx >(setup:1111): Default python interpreter is '/Library/Frameworks/Python.framework/Versions/3.12/bin/python3.12'
pipx >(run_pipx_command:241): Virtual Environment locations are:
- . : /Users/richard/.local/pipx/venvs/-
Package is not installed. Expected to find /Users/richard/.local/pipx/venvs/-, but it does not exist.
```

It seems like the name of the package was not correctly determined. We can try an absolute path

```
(.venv) richard@Richards-MBP % pipx upgrade -e -v $PWD
pipx >(setup:1110): pipx version is 1.7.1
pipx >(setup:1111): Default python interpreter is '/Library/Frameworks/Python.framework/Versions/3.12/bin/python3.12'
Error: '/Users/richard/my_package' looks like a path. Expected the name of an installed package.
```

An extra -v flag indicates that `pipx` is explicitly raising this error. If we use the name of the package as suggested

```
(.venv) richard@Richards-MBP my_package % pipx upgrade -v -e my_package
pipx >(setup:1110): pipx version is 1.7.1
pipx >(setup:1111): Default python interpreter is '/Library/Frameworks/Python.framework/Versions/3.12/bin/python3.12'
pipx >(run_pipx_command:241): Virtual Environment locations are:
- my_package : /Users/richard/.local/pipx/venvs/my_package
pipx >(_upgrade_venv:154): Ignoring --python as not combined with --install
pipx >(run_subprocess:175): running <checking pip's availability>
pipx >(needs_upgrade:99): Time since last upgrade of shared libs, in seconds: 8. Upgrade will be run by pipx if greater than 2592000.
pipx >(run_subprocess:175): running <checking pip's availability>
pipx >(run_subprocess:175): running <checking pip's availability>
pipx >(upgrade:119): Upgrading shared libraries in /Users/richard/.local/pipx/shared
upgrading shared libraries...
pipx >(run_subprocess:175): running /Users/richard/.local/pipx/shared/bin/python -m pip --no-input --disable-pip-version-check install --upgrade pip >= 23.1
pipx >(_parsed_package_to_package_or_url:139): cleaned package spec: /Users/richard/my_package
pipx >(upgrade_package:457): Upgrading package from spec '/Users/richard/my_package'
upgrading package from spec '/Users/richard/my_package'...
pipx >(run_subprocess:175): running /Users/richard/.local/pipx/venvs/my_package/bin/python -m pip --no-input install --editable --upgrade /Users/richard/my_package
ERROR: --upgrade is not a valid editable requirement. It should either be a path to a local project or a VCS URL (beginning with bzr+http, bzr+https, bzr+ssh, bzr+sftp, bzr+ftp, bzr+lp, bzr+file, git+http, git+https, git+ssh, git+git, git+file, hg+file, hg+http, hg+https, hg+ssh, hg+static-http, svn+ssh, svn+http, svn+https, svn+svn, svn+file).
'/Users/richard/.local/pipx/venvs/my_package/bin/python -m pip --no-input install --editable --upgrade
/Users/richard/my_package' failed
```

Here the error comes from pip, since the syntax of the pip install is incorrect (`--upgrade` should come before `--editable`)


<!-- If possible, include output of `pipx --verbose ...` -->

**Expected behavior**

In all cases, I would expect the upgrade to be successful. In the first case, I'd expect pipx to find
/Users/richard/.local/pipx/venvs/my_package. In the second, I'd expect a path to be an acceptable argument in this
context. In the third, I'd expect pipx to generate a syntactically correct pip command.

<!-- What should have happened? -->



Discussion:
Hi @rtumin2,

The argument for `pipx upgrade` is the package name not the path, so what you need to do is `pipx upgrade my_package` actually. There is no need to have the `-e` option, and pipx will remember the path. Everything should work as expected only if the path does not chanage.

Let me know there is still any problem.

Hi @huxuan , thanks for the quick response, and you're right that `pipx upgrade my_package` works. Perhaps the [docs](https://pipx.pypa.io/stable/docs/#pipx-upgrade) should be updated to remove the `--editable` flag.  

Thanks for the help!
> Hi @huxuan , thanks for the quick response, and you're right that `pipx upgrade my_package` works. Perhaps the [docs](https://pipx.pypa.io/stable/docs/#pipx-upgrade) should be updated to remove the `--editable` flag.  
> 
> Thanks for the help!

Thanks for pointing out! Let us keep this issue open for documentation fix.

Contributions are always welcome!
There **is** a need for `-e,--editable` option! It just needs fixing in pipx, please do not remove it.

This will install package in `editable` mode:
```
pip install --editable .
pipx install --editable .
```


This will upgrade the package **and remove the editable mode**:
```
pip install --upgrade .
pipx upgrade my_package
```

This will upgrade the package with keeping the `editable` mode.
```bash
pip install --upgrade --editable .
pipx upgrade --editable my_package # This will fail because of pipx incorrect implementation
```

The problem is that it results with an error when done with *pipx*:
```
ERROR: --upgrade is not a valid editable requirement. It should either be a path to a local project or a VCS URL (beginning with bzr+http, bzr+https, bzr+ssh, bzr+sftp, bzr+ftp, bzr+lp, bzr+file, git+http, git+https, git+ssh, git+git, git+file, hg+file, hg+http, hg+https, hg+ssh, hg+static-http, svn+ssh, svn+http, svn+https, svn+svn, svn+file).
'/home/user/.local/share/pipx/venvs/my_package/bin/python -m pip --no-input install --editable --upgrade /home/user/projects/my_package -q' failed
```

pipx tries to execute:
```
pip --no-input install --editable --upgrade /home/user/projects/my_package -q
```
And it's incorrect. `--editable` and `--upgrade` parameters should be swapped. It should be:
```
pip --no-input install --upgrade --editable /home/user/projects/my_package -q
```
After this change it should work. So I think it is a problem with *pipx*.



Patch:
diff --git a/changelog.d/1610.bugfix.md b/changelog.d/1610.bugfix.md
new file mode 100644
index 000000000..79d5fc17f
--- /dev/null
+++ b/changelog.d/1610.bugfix.md
@@ -0,0 +1,1 @@
+Fix incorrect order of flags when using `pipx upgrade`.
diff --git a/src/pipx/venv.py b/src/pipx/venv.py
index 123eea6ee..be761d529 100644
--- a/src/pipx/venv.py
+++ b/src/pipx/venv.py
@@ -441,7 +441,7 @@ def has_package(self, package_name: str) -> bool:
     def upgrade_package_no_metadata(self, package_name: str, pip_args: List[str]) -> None:
         logger.info("Upgrading %s", package_descr := full_package_description(package_name, package_name))
         with animate(f"upgrading {package_descr}", self.do_animation):
-            pip_process = self._run_pip(["--no-input", "install"] + pip_args + ["--upgrade", package_name])
+            pip_process = self._run_pip(["--no-input", "install", "--upgrade"] + pip_args + [package_name])
         subprocess_post_check(pip_process)
 
     def upgrade_package(
@@ -456,7 +456,7 @@ def upgrade_package(
     ) -> None:
         logger.info("Upgrading %s", package_descr := full_package_description(package_name, package_or_url))
         with animate(f"upgrading {package_descr}", self.do_animation):
-            pip_process = self._run_pip(["--no-input", "install"] + pip_args + ["--upgrade", package_or_url])
+            pip_process = self._run_pip(["--no-input", "install", "--upgrade"] + pip_args + [package_or_url])
         subprocess_post_check(pip_process)
 
         self.update_package_metadata(


Test patch:
diff --git a/tests/test_upgrade.py b/tests/test_upgrade.py
index b95bc687e..a991909ca 100644
--- a/tests/test_upgrade.py
+++ b/tests/test_upgrade.py
@@ -78,6 +78,14 @@ def test_upgrade_specifier(pipx_temp_env, capsys):
     assert f"upgraded package {name} from {initial_version} to" in captured.out
 
 
+def test_upgrade_editable(pipx_temp_env, capsys, root):
+    empty_project_path_as_string = (root / "testdata" / "empty_project").as_posix()
+    assert not run_pipx_cli(["install", "--editable", empty_project_path_as_string, "--force"])
+    assert not run_pipx_cli(["upgrade", "--editable", "empty_project"])
+    captured = capsys.readouterr()
+    assert "empty-project is already at latest version" in captured.out
+
+
 def test_upgrade_include_injected(pipx_temp_env, capsys):
     assert not run_pipx_cli(["install", PKG["pylint"]["spec"]])
     assert not run_pipx_cli(["inject", "pylint", PKG["black"]["spec"]])