Install python package to a custom location
The goal: get the aws
command from the awscli
package to a custom directory, namely /home/transang/my-python-packages
.
Initially, everything was simple. But latterly it got complicated as many bugs appeared.
I will introduce the bugs one by one before coming up with a stable solution for this use case.
If you arrive here to know the post title's solution, I highly recommend reading from the end of the post.
Environments
There are 3 PC, called PC1, PC2, PC3
In PC1:
$ python3 --version
Python 3.6.9
$ pip3 --version
pip 9.0.1 from /usr/lib/python3/dist-packages (python 3.6)
l% $ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 18.04.3 LTS
Release: 18.04
Codename: bionic
In PC2:
ubuntu@ip-10-0-102-219:~$ python3 --version
Python 3.6.9
ubuntu@ip-10-0-102-219:~$ pip3 --version
lsb_pip 9.0.1 from /usr/lib/python3/dist-packages (python 3.6)
ubuntu@ip-10-0-102-219:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 18.04.1 LTS
Release: 18.04
Codename: bionic
In PC3:
piproot@vultr:~# python3 --version
Python 3.7.3
root@vultr:~# pip3 --version
lsb_repip 18.1 from /usr/lib/python3/dist-packages/pip (python 3.7)
root@vultr:~# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 19.04
Release: 19.04
Codename: disco
PC1 and PC2 introduce bugs while the first solution works perfectly in PC3.
Initial solution
Very simple with the --target
option
pip3 install --target=/home/transang/my-python-packages awscli
The command successfully finished in the PC3. Yet the PC1 and PC2. There was the following error
Exception:
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/pip/basecommand.py", line 215, in main
status = self.run(options, args)
File "/usr/lib/python3/dist-packages/pip/commands/install.py", line 360, in run
prefix=options.prefix_path,
File "/usr/lib/python3/dist-packages/pip/req/req_set.py", line 784, in install
**kwargs
File "/usr/lib/python3/dist-packages/pip/req/req_install.py", line 851, in install
self.move_wheel_files(self.source_dir, root=root, prefix=prefix)
File "/usr/lib/python3/dist-packages/pip/req/req_install.py", line 1064, in move_wheel_files
isolated=self.isolated,
File "/usr/lib/python3/dist-packages/pip/wheel.py", line 247, in move_wheel_files
prefix=prefix,
File "/usr/lib/python3/dist-packages/pip/locations.py", line 153, in distutils_scheme
i.finalize_options()
File "/usr/lib/python3.6/distutils/command/install.py", line 274, in finalize_options
raise DistutilsOptionError("can't combine user with prefix, "
distutils.errors.DistutilsOptionError: can't combine user with prefix, exec_prefix/home, or install_(plat)base
I found the bug report here. I needed to specify --system
option to prevent the default --user
option.
Second step solution
After adding the --system
option
pip3 install --system --target=/home/transang/my-python-packages awscli
The command successfully ended in all PCs.
However, another bug was introduced. In the PC3, there is binary installed in /home/transang/my-python-packages
. Yet the PC1, PC2.
I again discovered the solution from here. With the new solution, I came up with the next solution
The currently stable solution
Adding --install-option
solves the problem introduced previously.
pip3 install --system --target=/home/transang/my-python-packages --install-option=--install-scripts=/home/transang/my-python-packages/bin awscli
Now, the binary appears in all PCs in /home/transang/my-python-packages/bin
directory.
The story does not end yet
I wonder about the stability of python's package management. Compared to npm
, yarn
of nodejs, there are too many errors for even a straightforward operation.
When I ran the above solution in Mac, python 3.8.1, pip3 20.0.2, it complains no such option: --system
.
Removing the --system
flag makes the command work. However, it throws another warning
DEPRECATION: Location-changing options found in --install-option: ['--install-scripts'] from command line. This configuration may cause unexpected behavior and is unsupported. pip 20.2 will remove support for this functionality. A possible replacement is using pip-level options like --user, --prefix, --root, and --target. You can find discussion regarding this at https://github.com/pypa/pip/issues/7309.
The deprecation warning links to this github issue. It looks like the pip maintainers are going to remove the --install-option
flag. This issue is relatively new and is on the TODO list at the time of writing this blog.
After ignoring the warning and finished the installation, I tried to run the installed package. Another error was thrown
Traceback (most recent call last):
File "/Users/transang/python/packages-bin/aws", line 19, in <module>
import awscli.clidriver
ModuleNotFoundError: No module named 'awscli'
Actually, installing the package by this way in my Mac is just optional. So I temporarily installed the package directly to the root directory of python. I will come back and continue this part later.
Now, the most stable version
- Upgrade pip. The following command also specifies a custom location for the pip package
PYTHONUSERBASE=/home/transang/my-site-packages python3 -m pip install --user --upgrade pip
- Check pip version
PYTHONUSERBASE=/home/transang/my-site-packages python3 -m pip --version
It should show the installed latest version of pip (current is 20.0.2
with the customized package location)
- Install awscli
PYTHONUSERBASE=/home/transang/my-site-packages python3 -m pip install --upgrade --target /home/transang/my-python-packages awscli
- Invoke the installed package
PYTHONPATH=/home/transang/my-python-packages /home/transang/my-python-packages/bin/aws --version
Happy coding!