Use pipenv and poetry in a way that works

2020-08-23

Strange as the title is, they just don't work conveniently "out of box"

 

Preface #

I am not an expert. Wrote this section just for completion.

Why not simply pip #

pip install is simple and naive. You will get crazy if you come from JavaScript world where both npm and yarn are excellent package managers. It even takes effort to manage production and development dependecies in two separate list. A tool for handling all the dependencies and virtual env works is handy, and can simplify workflows.

What do they actuallly do #

Pretty much like mentioned above. Quoting from pipenv's doc for some detailed feature:

  • Enables truly deterministic builds, while easily specifying only what you want.
  • Generates and checks file hashes for locked dependencies.
  • Automatically install required Pythons, if pyenv is available.
  • Automatically finds your project home, recursively, by looking for a Pipfile.
  • Automatically generates a Pipfile, if one doesn’t exist.
  • Automatically creates a virtualenv in a standard location.
  • Automatically adds/removes packages to a Pipfile when they are installed or uninstalled.
  • Automatically loads .env files, if they exist.

Major Problem: Speed #

Resolving and locking dependencies are too slow. A typical jupyterlab workspace takes hours to finish.

Why this happens #

In node package ecosystem a project has hundreds of dependencies to resolve, install and lock, but still faster than pipenv or poetry. That's because in python package ecosystem, many packages are not properly formed and it's impossible to get it's dependencies without downloading it.

See also https://python-poetry.org/docs/faq/#why-is-the-dependency-resolution-process-slow

How to solve in pipenv #

pipenv is slow at locking, so you may want use --skip-lock flag or set PIPENV_SKIP_LOCK env when the network is poor, then pipenv lock when network connection is good.

How to solve in poetry #

Poetry is slow at dependency resolution. Quoting from poetry doc:

At the moment there is no way around it.

Other pipenv problems #

Lock file not cross platform #

Ironically, deterministic builds actually leads to wrong packages installed on different platforms.

Lock files may be version-dependent. Take pytest for example, if you install and lock on python 3.8, the importlib_metadata is not installed because py3.8 already have importlib.metadata included. But when you install based on the lock file on py3.6, importlib_metadata is not found. This is not happening when directly install pytest on py3.6

How to solve #

  1. Use markers for subdependencies: importlib_metadata = {version="~=1.7.0", python_version="<'3.8'"}
  2. Or use pyenv / pyenv-win

apt install may be outdated #

Better install via pip(x)

Script shortcut cannot combine multi commands #

pypa/pipenv#2283

You can write command A && command B in package.json, but not Pipfile. All you can do is write in a shell script.

No python version range #

pypa/pipenv#1050

If you are sure that your program and Pipfile.lock works for python > 3.5.2, remove python requirement 😏

No extra denpendencies #

This can be frustrating if you want uWSGI to be installed only on production machine, but not on your PC.

Other poetry problems #

Prefer python #

Use poetry with pyenv if python -V outputs 2.7.x. Or it will be an awful develop experience.

Or if you are sure you just need to create venv with system's python3, you can choose pip(x) install it.

No built-in dot-env and handy scripts #

But you can make use of the power of pyproject.toml and use tools like taskipy and poethepoet

Not supported by github dependency graph #

https://docs.github.com/en/github/visualizing-repository-data-with-graphs/about-the-dependency-graph#supported-package-ecosystems

But dependabot does support it.


More info and solutions welcome!

Leave your comments and reactions on GitHub