PyENV has done a fairly good job in managing user-local python versions, as well as per-version pip packages. However, some packages such as PyQt5 need deep system integration and can not be installed using pip. This makes packages such as jupyter-qtconsole upset. The use of shims in PyENV also forces any provided command to be started with a shell script (so a shell process). This makes packages such as python-powerline much slower.
To solve the above problems, I choose to go back to the standard python user-local install process and choose pip user install. pip user install relys on system python to work correctly, and integrates tightly with system packages. That is to say, if one install PyQt5 using system package manager, he/she can use it in a user-local toyplot package, avoiding install PyQt5 manually (since not possible with pip). Moreover, binaries provided by packages are directly avaliable to the system, which greatly improves the speed of packages such as python-powerline 
pip user install is good. Then how we do it? It’s as easy as following the listed steps:
Choose the user install location, say
$MYINSTALL. By default, it’s
Install packages we want using the following command:
PYTHONUSERBASE=$MYINSTALL pip install --user --upgrade <PACKAGES>
Setup python path and binary path
export PYTHONPATH=$MYINSTALL:$PYTHONPATH export PATH=$MYINSTALL/bin:$PATH
Integrate with system packages
If you install packages such as numpy, scipy and matplotlib with system package manager, and then install packages such as toyplot or jupyter, you may find that numpy, scipy will not be upgraded since the requirements of toyplot is already meet. This is the power of local install, which integrates well with existing packages.
But if you do want a more recent version that system provided, you can append