Repository: pypa/pipx (Python)

Issue:
Pipx 1.3.0 adds `--force-reinstall` in a wrong place when `--editable` installation is used
The `pipx 1.3.0` released ~1 hour ago started to fail our CI builds in Airflow 

Example here: https://github.com/apache/airflow/actions/runs/7071524725/job/19249611301?pr=36026

We are running:

```
python -m pipx install --editable ./dev/breeze/ --force
```

```
    ERROR: --force-reinstall 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).
⚠️  Not removing existing venv
    /home/runner/.local/pipx/venvs/apache-airflow-breeze because it was not
    created in this session
Error installing apache-airflow-breeze from spec
'/home/runner/actions-runner/_work/airflow/airflow/dev/breeze'.
```

It looks like the resuling `pip` installation is wrong after this change in the changelog of `pipx 1.3.0`:

> Add --force-reinstall to pip arguments when --force was passed

Likely the resulting command is:

```
pip install --editable --force-reinstall ./dev/breeze/
```

This is wrong becasue `--editable` expects URL/PATH right after it.


<!--
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://pypa.github.io/pipx/troubleshooting/
-->

**Describe the bug**
<!-- Please be as detailed as possible! -->

**How to reproduce**
<!-- If possible, include output of `pipx --verbose ...` -->

**Expected behavior**
<!-- What should have happened? -->



Discussion:

Patch:
diff --git a/.gitignore b/.gitignore
index 8233c4d398..fbc7e56f46 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,4 +17,6 @@ docs/docs.md
 pipx.1
 .pipx_tests
 /.coverage*
-testdata
+/testdata
+!/testdata/empty_project
+/.idea
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7c43662f80..5ee639dae3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,7 @@
 ## dev
 
+- Fix combining of --editable and --force flag
+
 ## 1.3.0
 
 - Check whether pip module exists in shared lib before performing any actions, such as `reinstall-all`.
diff --git a/src/pipx/commands/install.py b/src/pipx/commands/install.py
index 2f51b9d663..66cfac1f14 100644
--- a/src/pipx/commands/install.py
+++ b/src/pipx/commands/install.py
@@ -58,7 +58,7 @@ def install(
             )
         if force:
             print(f"Installing to existing venv {venv.name!r}")
-            pip_args += ["--force-reinstall"]
+            pip_args = ["--force-reinstall"] + pip_args
         else:
             print(
                 pipx_wrap(


Test patch:
diff --git a/testdata/empty_project/README.md b/testdata/empty_project/README.md
new file mode 100644
index 0000000000..90b452a4d9
--- /dev/null
+++ b/testdata/empty_project/README.md
@@ -0,0 +1,1 @@
+Empty project used for testing only
diff --git a/testdata/empty_project/empty_project/__init__.py b/testdata/empty_project/empty_project/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/testdata/empty_project/empty_project/main.py b/testdata/empty_project/empty_project/main.py
new file mode 100644
index 0000000000..cd9ac48059
--- /dev/null
+++ b/testdata/empty_project/empty_project/main.py
@@ -0,0 +1,6 @@
+def main():
+    pass
+
+
+if __name__ == "__main__":
+    main()
diff --git a/testdata/empty_project/pyproject.toml b/testdata/empty_project/pyproject.toml
new file mode 100644
index 0000000000..cc60e229a8
--- /dev/null
+++ b/testdata/empty_project/pyproject.toml
@@ -0,0 +1,21 @@
+[build-system]
+requires = [
+  "setuptools",
+  "wheel",
+]
+
+[project]
+name = "empty-project"
+version = "0.1.0"
+description = "Empty Python Project"
+authors = [{ name = "My Name", email = "me@example.com" }]
+requires-python = ">=3.8"
+classifiers = [
+  "Programming Language :: Python :: 3 :: Only",
+  "Programming Language :: Python :: 3.8",
+  "Programming Language :: Python :: 3.9",
+  "Programming Language :: Python :: 3.10",
+  "Programming Language :: Python :: 3.11",
+  "Programming Language :: Python :: 3.12",
+]
+scripts.empty-project = "empty_project.main:cli"
diff --git a/tests/test_install.py b/tests/test_install.py
index 1ece9bdc4f..429e1360dd 100644
--- a/tests/test_install.py
+++ b/tests/test_install.py
@@ -285,6 +285,17 @@ def test_force_install_changes(pipx_temp_env, capsys):
     assert "2022.1.7" not in captured.out
 
 
+def test_force_install_changes_editable(pipx_temp_env, root, capsys):
+    empty_project_path_as_string = (root / "testdata" / "empty_project").as_posix()
+    assert not run_pipx_cli(["install", "--editable", empty_project_path_as_string])
+    captured = capsys.readouterr()
+    assert "empty-project" in captured.out
+
+    assert not run_pipx_cli(["install", "--editable", empty_project_path_as_string, "--force"])
+    captured = capsys.readouterr()
+    assert "Installing to existing venv 'empty-project'" in captured.out
+
+
 def test_preinstall(pipx_temp_env, caplog):
     assert not run_pipx_cli(["install", "--preinstall", "black", "nox"])
     assert "black" in caplog.text