Sígueme! https://github.com/astrojuanlu/
✨✨✨ Instale el paquete deseado en dos sencillos pasos: ✨✨✨
$ source .venv/bin/activate
(.venv) $ python -m pip install ipython
«¿Y no había otra cosa que se llamaba
easy
...?»
Hagamos como que nunca existió 😇
«Sí, pero... ¡es que yo lo quiero en uno!»
Mal menor: pip nuevo ⚠️⚠️⚠️
$ pip install ipython # Hmmm...
Por defecto se instala en ${HOME}/.local/lib
(a escondidas se ha usado --user
). ¿Queremos eso?
In [1]: import sys
In [2]: sys.prefix
Out[2]: '/usr'
In [3]: import urllib3
In [4]: urllib3.__file__
Out[4]: '/usr/lib/python3/dist-packages/urllib3/__init__.py'
In [3]: import IPython
In [4]: IPython.__file__
Out[4]: '/home/juanlu/.local/lib/python3.8/site-packages/IPython/__init__.py'
Ya tenemos dos localizaciones distintas 😨
«Sí, pero... ¡es que yo lo quiero en uno!»
Catástrofe en ciernes: pip viejo 🚨🚨🚨
$ pip install urllib3 # Hmmm...
[Errno 13] Permission denied: '/usr/lib/python3.8'
$ sudo pip install urllib3 # NOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
sudo pip install
🚫 puede desestabilizar dependencias críticas de tu sistemaOpciones:
venv
https://docs.python.org/3/library/venv.html ¡en la biblioteca estándar desde Python 3.3 (2012)!virtualenv
https://pypi.org/project/virtualenv/, más rápido y flexible (recientemente resucitado por Bernát Gábor)Otras opciones:
virtualenvwrapper
no tiene actividad desde febrero de 2019 😓 (¡aunque aún funciona!)pyenv
rompe el $PATH
y algunos scripts 😓 https://github.com/pyenv/pyenv/issues/1112Pipenv
trata de hacer demasiadas cosas, avanza con mucho esfuerzo, y tiene ciertos problemasPoetry
nunca lo usé 🤔¿¿Y qué pasa con Alpine??
$ docker run -it --rm --name pip-wheel python:3.8-alpine sh
/ # python -m pip install numpy
Collecting numpy
Downloading numpy-1.20.1.zip (7.8 MB)
|████████████████████████████████| 7.8 MB 10.8 MB/s
Installing build dependencies ... done
Getting requirements to build wheel ... done
Preparing wheel metadata ... error
ERROR: Command errored out with exit status 1:
command: /usr/local/bin/python /usr/local/lib/python3.8/site-packages/pip/_vendor/pep517/_in_process.py prepare_metadata_for_build_wheel /tmp/tmpzz6m5yah
cwd: /tmp/pip-install-9mlwuba9/numpy
Complete output (226 lines):
...
File "numpy/core/setup.py", line 676, in get_mathlib_info
raise RuntimeError("Broken toolchain: cannot link a simple C program")
RuntimeError: Broken toolchain: cannot link a simple C program
🤯
wheel
que resuelve este problema ofreciendo un Built DistributionEl formato del nombre del archivo indica la compatibilidad:
{distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl
Es decir:
distribution
: Nombre del paquete (Django
, numpy
)version
: Versión (compatible con el PEP 440)python tag
y abi tag
: Implementación de Python (cp38
, pp372-pypy3_72
)platform
: PlataformaPor ejemplo:
numpy-1.20.1-cp38-cp38-manylinux1_x86_64.whl
..."many what"???
For Linux, the situation is much more delicate.
PEP 513 -- A Platform Tag for Portable Linux Built Distributions
glibc
manylinux1
manylinux2010
(CentOS 6), manylinux2014
(CentOS 7), y vendrán másglibc
, sino musl
! 😭😭😭musl
, pero de momento hay que usar métodos alternativosEn resumen:
python -m pip install
+ venv
= ❤️✨✨✨ Especifique las dependencias deseadas en dos sencillos pasos: ✨✨✨
(.venv) $ echo "django<3" > requirements.in
(.venv) $ python -m pip install pip-tools && pip-compile
pip-compile toma como entrada un archivo requirements.in
y produce un requirements.txt
con todas las versiones fijadas:
(.venv) $ cat requirements.txt
#
# This file is autogenerated by pip-compile
# To update, run:
#
# pip-compile
#
django==2.2.19
# via -r requirements.in
pytz==2021.1
# via django
sqlparse==0.4.1
# via django
Perfecto para tener un entorno reproducible 💯
Una vez fijadas, ¡por defecto no se actualizan!
(.venv) $ echo "django" > requirements.in
(.venv) $ pip-compile
#
# This file is autogenerated by pip-compile
# To update, run:
#
# pip-compile
#
django==2.2.19
# via -r requirements.in
pytz==2021.1
# via django
sqlparse==0.4.1
# via django
Se acabaron los pipelines rotos por nuevas versiones ✨
Solo se actualizan si lo pedimos:
(.venv) $ pip-compile -P django
#
# This file is autogenerated by pip-compile
# To update, run:
#
# pip-compile
#
asgiref==3.3.1
# via django
django==3.1.7
# via -r requirements.in
pytz==2021.1
# via django
sqlparse==0.4.1
# via django
Y, para poner nuestro entorno al día:
(.venv) $ pip-sync
Collecting asgiref==3.3.1
Using cached asgiref-3.3.1-py3-none-any.whl (19 kB)
Collecting django==3.1.7
Using cached Django-3.1.7-py3-none-any.whl (7.8 MB)
Collecting pytz==2021.1
Using cached pytz-2021.1-py2.py3-none-any.whl (510 kB)
Collecting sqlparse==0.4.1
Using cached sqlparse-0.4.1-py3-none-any.whl (42 kB)
Installing collected packages: sqlparse, pytz, asgiref, django
Successfully installed asgiref-3.3.1 django-3.1.7 pytz-2021.1 sqlparse-0.4.1
...que es casi lo mismo que hacer:
(.venv) $ python -m pip install -r requirements.txt
Requirement already satisfied: asgiref==3.3.1 in ./.venv/lib/python3.8/site-packages (from -r requirements.txt (line 7)) (3.3.1)
Requirement already satisfied: django==3.1.7 in ./.venv/lib/python3.8/site-packages (from -r requirements.txt (line 9)) (3.1.7)
Requirement already satisfied: pytz==2021.1 in ./.venv/lib/python3.8/site-packages (from -r requirements.txt (line 11)) (2021.1)
Requirement already satisfied: sqlparse==0.4.1 in ./.venv/lib/python3.8/site-packages (from -r requirements.txt (line 13)) (0.4.1)
😍😍😍
Otras alternativas:
Pipenv
Poetry
setup.py
~ ¡Calma! No hemos llegado aún, y además no es exactamente para estoEn resumen:
pip-compile
+ pip-sync
(o simplemente pip
) = ❤️requirements.txt
, ¡usémoslo!✨✨✨ Distribuya su código en tres sencillos pasos: ✨✨✨
(.venv) $ cat saludo.py # Este no cuenta ;)
"""
Un saludo
"""
__version__ = "0.1.0"
print("¡Hola, mundo!")
(.venv) $ python -m pip install flit && flit init
(.venv) $ python -m pip install build && python -m build
(.venv) $ python -m pip install twine && twine upload dist/*
Pero, ¿qué es flit, y qué está pasando aquí?
(.venv) $ ls
dist LICENSE pyproject.toml saludo.py
¡No hay setup.py
! 😮 Rebobinemos por un momento...
setup.py
para especificar los metadatos del proyectofrom setuptools import setup
setup(
...
setuptools
tiene ejem algunos detractoressetuptools
) y el frontend (pip
)«¿No había otra cosa que se llamaba...?»
Elevemos una oración por su alma 😇
pyproject.toml
[build-system]
requires = ["setuptools", "wheel"] # PEP 518
build-backend = "setuptools.build_meta" # PEP 517
(Para más información, Brett Cannon lo explica muy bien)
Por tanto, en lugar de hacer el antiguo:
(.venv) $ python setup.py install # Buuuuuuuuuuuuuu 👎
¡Ahora podemos hacer esto!
(.venv) $ python -m pip install .
Y pip
ya sabe hacer el resto 🎉
De paso, ¡python -m pip uninstall
funciona! ✨
Insisto: dejad de ejecutar setup.py
. Haced caso a Paul, es muy majete.
Equivalencias:
python setup.py install
-> python -m pip install .
python setup.py develop
-> python -m pip install -e .
("instalaciones editables", ¡muy útiles!)python setup.py sdist bdist_wheel
-> python -m build
(en seguida lo explicamos)setup.py
son endiablados 😵 (por ejemplo el de SciPy) - por suerte, setuptools
permite todo esto y mássetuptools
, es recomendable usar metadatos estáticos en setup.cfg
:[metadata]
name = mypackage
version = 0.0.1
[options]
packages = mypackage
install_requires =
requests
importlib; python_version == "2.6"
Y para eso tenemos flit
:
(.venv) $ flit init
Module name [saludo]:
Author [Juan Luis Cano Rodríguez]:
Author email [hello@juanlu.space]:
Home page [https://github.com/astrojuanlu/python-saludo]:
Choose a license (see http://choosealicense.com/ for more info)
1. MIT - simple and permissive
2. Apache - explicitly grants patent rights
3. GPL - ensures that code based on this is shared with the same terms
4. Skip - choose a license later
Enter 1-4 [1]:
Written pyproject.toml; edit that file to add optional extra info.
(.venv) $ cat pyproject.toml
[build-system]
requires = ["flit_core >=2,<4"]
build-backend = "flit_core.buildapi"
[tool.flit.metadata]
module = "saludo"
author = "Juan Luis Cano Rodríguez"
author-email = "hello@juanlu.space"
home-page = "https://github.com/astrojuanlu/python-saludo"
classifiers = [ "License :: OSI Approved :: MIT License",]
¡Y nada más! ✨
Y ha llegado el momento de la verdad: generamos nuestras Source Distribution y Built Distribution usando build
:
(.venv) $ python -m build
(.venv) $ ls dist/
saludo-0.1.0-py2.py3-none-any.whl saludo-0.1.0.tar.gz
(¡o al PyPI interno de tu empresa!)
«¿Significa eso que, dependiendo de si elijo
setuptools
,flit
, opoetry
, tengo que especificar los metadatos de una manera totalmente distinta?»
Sí 😓
...¡pero no por mucho tiempo!
pyproject.toml
setuptools
, flit
, y poetry
en mente!flit
hace pocos días 😍Alternativas:
Poetry
!enscons
(poca actividad en GitHub)scikit-build
o mesonpep517
para código con extensiones en C, C++, FORTRAN u otrosEn resumen:
flit
+ build
+ twine
= ❤️setuptools
pyproject.toml
será parecido en todos✨ Resumen del resumen: https://github.com/astrojuanlu/cookiecutter-pylib ✨
pip
! 🎉 Y nunca jamás nombrar a easy_install
🚫venv
! 🎉 Y nunca jamás hacer sudo pip install
🚫flit
! 🎉 Y, si lo necesitas, puedes seguir usando setuptools
✔️<hello@juanlu.space>