3

I'm running into an issue where I'm trying to run a Django application within a virtual environment but it kept showing me errors regarding missing packages that need installation although I did install them previously using pip install <package-name>.

The issues couldn't be resolved until I used python -m pip install <package-name> to install the missing packages.

My question is what is the difference between the two commands? Does one of the commands install packages to the virtual environment and the other one does that globally? I'm confused.

Side Note: Also when running pip freeze shows different installed packages than those showing when I run python -m pip freeze.


Update No.1:

Running pip list -v shows that the packages installed using pip install <package-name> are located inside the virtual environment folder under lib/python3.8/site-packages directory, while running python -m pip list -v shows that the packages installed using python -m pip <package-name> are located under /usr/lib/<python3 or python3.8>/<site-packages or dist-packages> directory.


Update No.2:

I experienced all of the above using a VPS that is running Ubuntu 20.04 64 bit. The weird thing is that when I'm on my local machine that is running Linux Mint, executing both commands does the exact same thing, there is no difference between pip install <package-name> and python -m pip install <package-name> on my local machine.


Update No.3:

What I did is that I removed my virtual environment and created a new one with the same name and in the same directory. To make things clearer, the following screenshots demonstrate the issue I'm facing:

1- The below screenshot shows the result of pip list -v when I run it globally: enter image description here

2- The below screenshot shows the result of python -m pip list -v when I run it globally: enter image description here

The above 2 screenshots are showing the same result (both commands are executed globally, no active virtual environment).

3- The below screenshot shows the result of pip list -v when I run it inside my virtual environment:

enter image description here

4- The below screenshot shows the result of python -m pip list -v when I run it inside my virtual environment:

enter image description here

The above 2 screenshots show different results (both commands are executed while the virtual environment is activated).

The issue still persists although I created a new virtual environment.

Osama Radwan
  • 521
  • 1
  • 5
  • 17

3 Answers3

2

Welcome to the world of symlinks, binary discoverability with PATH, and shebang #!/... (*nix).

Before getting into the weaves of it all, let's talk about what pip is:

pip is both a python module (i.e python -m pip (the -m mean module)) and a binary $ pip. pip as a binary (the program) is optional, it's not required. pip as the module (inside site-packages where all the python modules are installed) is ABSOLUTELY required! Without it nothing will work, or worse yet you would use the pip module from another python version you have installed and then you're really going to slam your head against the wall...

PATH variable

echo $PATH (*nix) will show you something like this:

$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin

That means that the system, through the terminal will look for the binaries first inside /usr/local/bin then /usr/bin then ... so on. If it found it in the first place it looked, great, it's done, no need to keep searching. You can have the same binary name in different directories, the order of PATH is what matters, the second one will never be executed, EVER.

Why am I mentioning this? Well let's say you installed multiple versions of python or pip, and you installed it in the wrong place? Where the order is wrong? Well that binary will not run as expected.

Symlinks

You can have different versions of pip and python -- they're independent from one another, meaning just because you're using python with version 3.8, does not mean your pip is pointing to the same python binary.

A quick way to check is to do: (this applies to pip)

$ ls -ls $(which python)
0 lrwxr-xr-x  1 root  wheel  75 Jan  1  2020 /usr/bin/python -> ../../System/Library/Frameworks/Python.framework/Versions/2.7/bin/python2.7

You can see it's using the system version of python and any other version I have are being ignored if I just run python, I can "fix" this by specifying the version number such as python3.8...

You can fix this one by first figuring out what binaries you have available to you:

$ echo $PATH | sed 's/:/ /g' | xargs -n1 -I{} find {} -name 'python*' | grep -v 'config$'
/usr/local/bin/python3
/usr/local/bin/python3.9
/usr/local/bin/python3.8
/usr/bin/python3
/usr/bin/python
/usr/bin/python2
/usr/bin/pythonw
/usr/bin/pythonw2.7
/usr/bin/python2.7

Say I want my default python to be python3.8, then do:

$ ln -s /usr/local/bin/python3.8 /usr/local/bin/python
$ hash -r

hash -r tells the the terminal to forget all the banaries it found before, otheriwise your new symlink will not work

Q. Why not just override it?

The astute among you might ask, if the default python is inside /usr/bin/python why not just override it using?

$ ln -sf /usr/local/bin/python3.8 /usr/bin/python

/usr/bin/ is protected, and it's bad form to mess around with system prefernces. It's best not to touch it, /usr/local/bin is yours (the user of the machine) to mess with.

Shebang #!/...

For the most part you will find the pip binary file (thats just plain text by the way) to start like this:

#!/usr/bin/env python

or

#!/usr/local/bin/python3.9

This says, i'm a text file that needs to be ran with the following executable. In the first example, it's using the system to fin the binary python the same way you would find it in the terminal, see PATH variable above. The second example is a direct path to the binary for the system to use.

Remember pip is a module, the binary pip literaly says this: (this is slighly modified to make it as basic as possible and illustrate a point)

#!/usr/bin/env python
import sys
from pip._internal.cli.main import main
sys.exit(main())
  1. use the python binary
  2. import the sys module to be used later...
  3. import the main function from the pip module
  4. run main, what ever code it returns pass it to sys.exit(..) usually will return 0 but thats neighter here nor there.. just an FYI
Javier Buzzi
  • 6,296
  • 36
  • 50
  • Okay so I really appreciate your effort. I understood your answer. But I wasn't able to find where your answer solves my problem. – Osama Radwan Oct 27 '21 at 09:34
1

There is no difference typically.

The python -m flag allows you to run a specific module. For example you can run python -m http.server to start a simple server in the current directory.

Python includes a "scripts" directory. Depending on your system setup, modules from the scripts directory can be run directly from the shell without the python -m prefix. For example, typically you run black directly from the shell.

However, there are various reasons you might not want to trust running python modules directly from the shell, including:

  • Running a specific python version. You can do py -3.6 -m pip install something to specify a python version
  • You don't want to add the scripts folder to your PATH for whatever reason. Maybe because some names conflict with other programs on your computer.

Inside a virtualenv both commands will typically do the same thing, since virtualenv will properly set your PATH

mousetail
  • 7,009
  • 4
  • 25
  • 45
  • But what is the reason that each command installs packages in different directories? Shouldn't they install packages to the same location? – Osama Radwan Oct 27 '21 at 09:08
  • They will not install in different locations if both use the same python installation. (same version, same virtualenv). If it's different it will install in a different location. – mousetail Oct 27 '21 at 10:48
  • Somtimes when updating/modifying python exiting vitualenvironments stop working properly. If you are experiencing issues with virutalenv try deleting the environment and creating it again – mousetail Oct 27 '21 at 10:49
  • Alright, I will try that – Osama Radwan Oct 27 '21 at 10:51
0

It's pretty handy when you need to define which python version you need your package to be installed to (sorry for my bad english). Say, you have both python3.8 and python3.10. You can do python3.8 -m pip install django and python3.10 -m pip install django

reppon
  • 81
  • 4