Difference between pip install options "ignore-installed" and "force-reinstall"

There are two pip install options related to reinstalling the packages, which are --ignore-installed and --force-reinstall.

These two options described as following in the official doc

--force-reinstall
Reinstall all packages even if they are already up-to-date.

-I, --ignore-installed
Ignore the installed packages (reinstalling instead).

It seems that they all ignore something and do the reinstallation but I cannot tell the difference between them (I can see some difference if I actually execute them ... but I cannot explain). If I search "force reinstall packages in pip", the result lists both --ignore-installed and --force-reinstall, which confuses me for a long time.


--force-reinstall

Before installing a package, will uninstall it first if already installed. Pretty much the same as running pip uninstall -y dep && pip install dep for package and its every dependency.

--ignore-installed

Ignores whether the package and its deps are already installed, overwriting installed files. This means that you can have a situation where --ignore-installed does not uninstall a file, leaving it in site-packages forever. Imagine you have pkgname==1.0 that provides module spam:

$ pip show -f pkgname
Name: pkgname
Version: 1.0
...
spam.py

and the next version pkgname==2.0 renamed spam to eggs. When running pip install pkgname==2.0 --ignore-installed, spam.py will not be removed, left orphaned forever until you remove it manually.

Consequence

--force-reinstall should always be preferred; use --ignore-installed only if you know what you're doing are sure that the reinstall will overwrite currently installed files. Otherwise, you may get obscure import errors after reinstall due to stale modules still available in sys.path.

Example

Example to reproduce with the latest pip changes where all its packages were moved under _internal package: create a new virtual environment and downgrade pip to version 9:

$ mkvirtualenv testenv
$ workon testenv
(testenv) $ pip install "pip<10"

If you would now upgrade pip to the latest version via --force-reinstall, a clean upgrade is performed. Afterwards, you have the correct package structure with the _internal and _vendor:

(testenv) $ pip install pip --upgrade --force-reinstall
(testenv) $ ls -l $VIRTUAL_ENV/lib/python3.7/site-packages/pip
total 16
-rw-r--r--   1 hoefling  staff   21 19 Aug 11:47 __init__.py
-rw-r--r--   1 hoefling  staff  623 19 Aug 11:47 __main__.py
drwxr-xr-x   4 hoefling  staff  128 19 Aug 11:47 __pycache__
drwxr-xr-x  25 hoefling  staff  800 19 Aug 11:47 _internal
drwxr-xr-x  26 hoefling  staff  832 19 Aug 11:47 _vendor

If you would do the upgrade with --ignore-installed instead:

(testenv) $ pip install pip --upgrade --ignore-installed
(testenv) $ ls -l $VIRTUAL_ENV/lib/python3.7/site-packages/pip
total 392
-rw-r--r--   1 hoefling  staff     21 19 Aug 12:33 __init__.py
-rw-r--r--   1 hoefling  staff    623 19 Aug 12:33 __main__.py
drwxr-xr-x  14 hoefling  staff    448 19 Aug 12:33 __pycache__
drwxr-xr-x  25 hoefling  staff    800 19 Aug 12:33 _internal
drwxr-xr-x  28 hoefling  staff    896 19 Aug 12:33 _vendor
-rw-r--r--   1 hoefling  staff  11910 19 Aug 12:33 basecommand.py
-rw-r--r--   1 hoefling  staff  10465 19 Aug 12:33 baseparser.py
-rw-r--r--   1 hoefling  staff  16474 19 Aug 12:33 cmdoptions.py
drwxr-xr-x  16 hoefling  staff    512 19 Aug 12:33 commands
drwxr-xr-x   5 hoefling  staff    160 19 Aug 12:33 compat
-rw-r--r--   1 hoefling  staff  32153 19 Aug 12:33 download.py
-rw-r--r--   1 hoefling  staff   8121 19 Aug 12:33 exceptions.py
-rw-r--r--   1 hoefling  staff  39950 19 Aug 12:33 index.py
-rw-r--r--   1 hoefling  staff   5626 19 Aug 12:33 locations.py
drwxr-xr-x   5 hoefling  staff    160 19 Aug 12:33 models
drwxr-xr-x   6 hoefling  staff    192 19 Aug 12:33 operations
-rw-r--r--   1 hoefling  staff  10980 19 Aug 12:33 pep425tags.py
drwxr-xr-x   8 hoefling  staff    256 19 Aug 12:33 req
-rw-r--r--   1 hoefling  staff    156 19 Aug 12:33 status_codes.py
drwxr-xr-x  16 hoefling  staff    512 19 Aug 12:33 utils
drwxr-xr-x   8 hoefling  staff    256 19 Aug 12:33 vcs
-rw-r--r--   1 hoefling  staff  32010 19 Aug 12:33 wheel.py

Upgrading pip with --ignore-installed did not uninstall previous package version first, and due to new file structure, new files did not overwrite the old ones. As a consequence, old files are now orphaned and not picked up by any package; even pip uninstall pip will not remove the orphaned files. One would need to clean them up manually.


--ignore-installed can also be used if you have a virtual env that inherits the global site-package and you want to override the global installation (without uninstalling it).
For example you can have version N in the global python installation and version N+1 in the venv.
It is very convenient to test/debug a new version of a package in a virtual env.