Pipenv: guide du nouvel outil de packaging Python

Pipenv: guide du nouvel outil de packaging Python

Pipenv est un outil de packaging pour Python qui résout certains problèmes courants associés au flux de travail typique utilisantpip,virtualenv et le bon vieuxrequirements.txt.

En plus de résoudre certains problèmes courants, il consolide et simplifie le processus de développement en un seul outil en ligne de commande.

Ce guide passe en revue les problèmes que Pipenv résout et comment gérer vos dépendances Python avec Pipenv. De plus, il couvrira comment Pipenv s'intègre aux méthodes précédentes pour la distribution depackage.

Free Bonus:Click here to get access to a free 5-day class qui vous montre comment éviter les problèmes courants de gestion des dépendances avec des outils tels que Pip, PyPI, Virtualenv et les fichiers d'exigences.

Problèmes résolus par Pipenv

Pour comprendre les avantages de Pipenv, il est important de parcourir les méthodes actuelles de packaging et de gestion des dépendances en Python.

Commençons par une situation typique de gestion de packages tiers. Nous allons ensuite construire notre chemin vers le déploiement d'une application Python complète.

Gestion des dépendances avecrequirements.txt

Imaginez que vous travaillez sur un projet Python qui utilise un package tiers commeflask. Vous devrez spécifier cette exigence afin que d'autres développeurs et systèmes automatisés puissent exécuter votre application.

Vous décidez donc d'inclure la dépendanceflask dans un fichierrequirements.txt:

flask

Très bien, tout fonctionne bien localement, et après avoir piraté votre application pendant un certain temps, vous décidez de la déplacer en production. Voici où les choses deviennent un peu effrayantes…

Le fichierrequirements.txt ci-dessus ne spécifie pas la version deflask à utiliser. Dans ce cas,pip install -r requirements.txt installera la dernière version par défaut. C'est correct, sauf s'il y a des changements d'interface ou de comportement dans la dernière version qui cassent notre application.

Pour cet exemple, disons qu’une nouvelle version deflask a été publiée. Cependant, il n'est pas rétrocompatible avec la version que vous avez utilisée pendant le développement.

Maintenant, disons que vous déployez votre application en production et faites unpip install -r requirements.txt. Pip obtient la dernière version non rétrocompatible deflask, et juste comme ça, votre application s'arrête… en production.

“But hey, it worked on my machine!” - J'y suis moi-même et ce n'est pas une sensation formidable.

À ce stade, vous savez que la version deflask que vous avez utilisée pendant le développement a bien fonctionné. Donc, pour réparer les choses, vous essayez d'être un peu plus précis dans vosrequirements.txt. Vous ajoutez unversion specifier à la dépendanceflask. Ceci est également appelépinning une dépendance:

flask==0.12.1

L'épinglage de la dépendanceflask à une version spécifique garantit qu'unpip install -r requirements.txt configure la version exacte deflask que vous avez utilisée pendant le développement. Mais est-ce vraiment le cas?

Gardez à l'esprit queflask lui-même a également des dépendances (quepip installe automatiquement). Cependant,flask lui-même ne spécifie pas les versions exactes de ses dépendances. Par exemple, il autorise n'importe quelle version deWerkzeug>=0.14.

Encore une fois, pour les besoins de cet exemple, disons qu’une nouvelle version deWerkzeug a été publiée, mais elle introduit un bogue stoppeur dans votre application.

Lorsque vous effectuezpip install -r requirements.txt en production cette fois, vous obtiendrezflask==0.12.1 puisque vous avez épinglé cette exigence. Cependant, malheureusement, vous obtiendrez la dernière version boguée deWerkzeug. Encore une fois, le produit se casse dans la production.

Le vrai problème ici est quethe build isn’t deterministic. Ce que je veux dire par là, c'est que, étant donné la même entrée (le fichierrequirements.txt), pip ne produit pas toujours le même environnement. Pour le moment, vous ne pouvez pas facilement reproduire l'environnement exact que vous avez sur votre machine de développement en production.

La solution typique à ce problème consiste à utiliserpip freeze. Cette commande vous permet d'obtenir les versions exactes de toutes les bibliothèques tierces actuellement installées, y compris les sous-dépendances pip installées automatiquement. Vous pouvez donc tout geler en cours de développement pour vous assurer d'avoir le même environnement en production.

L'exécution depip freeze entraîne des dépendances épinglées que vous pouvez ajouter à unrequirements.txt:

click==6.7
Flask==0.12.1
itsdangerous==0.24
Jinja2==2.10
MarkupSafe==1.0
Werkzeug==0.14.1

Avec ces dépendances épinglées, vous pouvez vous assurer que les packages installés dans votre environnement de production correspondent exactement à ceux de votre environnement de développement, afin que votre produit ne se casse pas de manière inattendue. Cette «solution», malheureusement, conduit à une toute nouvelle série de problèmes.

Maintenant que vous avez spécifié les versions exactes de chaque package tiers, vous êtes responsable de la mise à jour de ces versions, même si ce sont des sous-dépendances deflask. Que faire si une faille de sécurité est découverte dansWerkzeug==0.14.1 que les responsables du package ont immédiatement corrigée dansWerkzeug==0.14.2? Vous devez vraiment mettre à jour versWerkzeug==0.14.2 pour éviter tout problème de sécurité découlant de la version antérieure non corrigée deWerkzeug.

Tout d'abord, vous devez savoir qu'il y a un problème avec la version que vous avez. Ensuite, vous devez obtenir la nouvelle version dans votre environnement de production avant que quelqu'un exploite le trou de sécurité. Vous devez donc modifier manuellement vosrequirements.txt pour spécifier la nouvelle versionWerkzeug==0.14.2. Comme vous pouvez le voir dans cette situation, la responsabilité de rester à jour avec les mises à jour nécessaires incombe à vous.

La vérité est que vous ne vous souciez vraiment pas de la version deWerkzeug installée tant qu'elle ne rompt pas votre code. En fait, vous voulez probablement la dernière version pour vous assurer d'obtenir des corrections de bogues, des correctifs de sécurité, de nouvelles fonctionnalités, plus d'optimisation, etc.

La vraie question est:“How do you allow for deterministic builds for your Python project without gaining the responsibility of updating versions of sub-dependencies?”

Alerte spoiler: La réponse simple est d'utiliser Pipenv.

Développement de projets avec différentes dépendances

Commençons un peu pour parler d'un autre problème courant qui survient lorsque vous travaillez sur plusieurs projets. Imaginez queProjectA a besoin dedjango==1.9, mais queProjectB a besoin dedjango==1.10.

Par défaut, Python essaie de stocker tous vos packages tiers dans un emplacement à l'échelle du système. Cela signifie que chaque fois que vous souhaitez basculer entreProjectA etProjectB, vous devez vous assurer que la bonne version dedjango est installée. Cela rend la transition entre les projets pénible car vous devez désinstaller et réinstaller les packages pour répondre aux exigences de chaque projet.

La solution standard consiste à utiliser unvirtual environment qui possède son propre exécutable Python et un stockage de packages tiers. De cette façon,ProjectA etProjectB sont correctement séparés. Vous pouvez désormais facilement basculer entre les projets, car ils ne partagent pas le même emplacement de stockage de package. PackageA peut avoir n'importe quelle version dedjango dont il a besoin dans son propre environnement, etPackageB peut avoir ce dont il a besoin totalement séparé. Un outil très courant pour cela estvirtualenv (ouvenv en Python 3).

Pipenv a intégré la gestion de l'environnement virtuel de sorte que vous disposez d'un seul outil pour la gestion de vos packages.

Résolution des dépendances

Qu'est-ce que j'entends par résolution de dépendance? Supposons que vous ayez un fichierrequirements.txt qui ressemble à ceci:

package_a
package_b

Disons quepackage_a a une sous-dépendancepackage_c, etpackage_a nécessite une version spécifique de ce package:package_c>=1.0. À son tour,package_b a la même sous-dépendance mais a besoin depackage_c<=2.0.

Idéalement, lorsque vous essayez d'installerpackage_a etpackage_b, l'outil d'installation examine les exigences pourpackage_c (étant>=1.0 et<=2.0) et sélectionne un version qui remplit ces conditions. Vous espérez que l'outil résout les dépendances pour que votre programme fonctionne à la fin. C'est ce que j'entends par «résolution de dépendance».

Malheureusement, pip lui-même n’a pas de résolution de dépendance réelle pour le moment, mais il existe unopen issue pour le prendre en charge.

La façon dont pip gérerait le scénario ci-dessus est la suivante:

  1. Il installepackage_a et recherche une version depackage_c qui remplit la première condition (package_c>=1.0).

  2. Pip installe ensuite la dernière version depackage_c pour répondre à cette exigence. Supposons que la dernière version depackage_c soit 3.1.

C'est là que le problème commence (potentiellement).

Si la version depackage_c sélectionnée par pip ne répond pas aux exigences futures (telles quepackage_b nécessitantpackage_c<=2.0), l'installation échouera.

La «solution» à ce problème est de spécifier la plage requise pour la sous-dépendance (package_c) dans le fichierrequirements.txt. De cette façon, pip peut résoudre ce conflit et installer un package qui répond à ces exigences:

package_c>=1.0,<=2.0
package_a
package_b

Comme avant, vous vous préoccupez maintenant directement des sous-dépendances (package_c). Le problème avec ceci est que sipackage_a modifie son exigence sans que vous le sachiez, les exigences que vous avez spécifiées (package_c>=1.0,<=2.0) peuvent ne plus être acceptables et l'installation peut échouer… à nouveau. Le vrai problème est qu'une fois de plus, vous êtes responsable de rester à jour avec les exigences des sous-dépendances.

Idéalement, votre outil d'installation serait suffisamment intelligent pour installer des packages répondant à toutes les exigences sans que vous spécifiiez explicitement les versions de sous-dépendance.

Présentation de Pipenv

Maintenant que nous avons résolu les problèmes, voyons comment Pipenv les résout.

Commençons par l'installer:

$ pip install pipenv

Une fois que vous avez fait cela, vous pouvez effectivement oublierpip puisque Pipenv agit essentiellement comme un remplaçant. Il introduit également deux nouveaux fichiers, lePipfile (qui est censé remplacerrequirements.txt) et lePipfile.lock (qui permet des constructions déterministes).

Pipenv utilise lespip etvirtualenv sous le capot mais simplifie leur utilisation avec une interface de ligne de commande unique.

Exemple d'utilisation

Commençons par créer votre superbe application Python. Tout d'abord, créez un shell dans un environnement virtuel pour isoler le développement de cette application:

$ pipenv shell

Cela créera un environnement virtuel s'il n'en existe pas déjà. Pipenv crée tous vos environnements virtuels dans un emplacement par défaut. Si vous souhaitez modifier le comportement par défaut de Pipenv, il existe desenvironmental variables for configuration.

Vous pouvez forcer la création d'un environnement Python 2 ou 3 avec les arguments--two et--three respectivement. Sinon, Pipenv utilisera tout ce quevirtualenv trouve par défaut.

Remarque: si vous avez besoin d'une version plus spécifique de Python, vous pouvez fournir un argument--python avec la version dont vous avez besoin. Par exemple:--python 3.6

Vous pouvez maintenant installer le package tiers dont vous avez besoin,flask. Oh, mais vous savez que vous avez besoin de la version0.12.1 et non de la dernière version, alors allez-y et soyez précis:

$ pipenv install flask==0.12.1

Vous devriez voir quelque chose comme ceci dans votre terminal:

Adding flask==0.12.1 to Pipfile's [packages]...
Pipfile.lock not found, creating...

Vous remarquerez que deux fichiers sont créés, unPipfile etPipfile.lock. Nous allons les examiner de plus près dans une seconde. Installons un autre package tiers,numpy, pour quelques calculs. Vous n'avez pas besoin d'une version spécifique, alors n'en spécifiez pas:

$ pipenv install numpy

Si vous souhaitez installer quelque chose directement à partir d'un système de contrôle de version (VCS), vous pouvez! Vous spécifiez les emplacements de la même manière que vous le feriez avecpip. Par exemple, pour installer la bibliothèquerequests à partir du contrôle de version, procédez comme suit:

$ pipenv install -e git+https://github.com/requests/requests.git#egg=requests

Notez l'argument-e ci-dessus pour rendre l'installation modifiable. Actuellement,this is required pour que Pipenv effectue la résolution des sous-dépendances.

Supposons que vous ayez également des tests unitaires pour cette application géniale et que vous souhaitiez utiliserpytest pour les exécuter. Vous n’avez pas besoin depytest en production, vous pouvez donc spécifier que cette dépendance est uniquement destinée au développement avec l’argument--dev:

$ pipenv install pytest --dev

Fournir l'argument--dev placera la dépendance dans un emplacement spécial[dev-packages] dans lesPipfile. Ces packages de développement ne sont installés que si vous spécifiez l'argument--dev avecpipenv install.

Les différentes sections séparent les dépendances nécessaires uniquement au développement de celles nécessaires au fonctionnement réel du code de base. En règle générale, cela serait accompli avec des fichiers d'exigences supplémentaires tels quedev-requirements.txt outest-requirements.txt. Maintenant, tout est consolidé dans un seulPipfile sous différentes sections.

D'accord, alors supposons que tout fonctionne dans votre environnement de développement local et que vous êtes prêt à le mettre en production. Pour ce faire, vous devez verrouiller votre environnement afin de vous assurer d'avoir le même en production:

$ pipenv lock

Cela créera / mettra à jour vosPipfile.lock, que vous n’aurez jamais besoin de modifier manuellement. Vous devez toujours utiliser le fichier généré.

Maintenant, une fois que vous avez obtenu votre code etPipfile.lock dans votre environnement de production, vous devez installer le dernier environnement réussi enregistré:

$ pipenv install --ignore-pipfile

Cela indique à Pipenv d'ignorer lesPipfile pour l'installation et d'utiliser ce qui se trouve dans lesPipfile.lock. Compte tenu de cesPipfile.lock, Pipenv créera exactement le même environnement que celui que vous aviez lorsque vous avez exécutépipenv lock, les sous-dépendances et tout.

Le fichier de verrouillage permet des constructions déterministes en prenant un instantané de toutes les versions des packages dans un environnement (similaire au résultat d'unpip freeze).

Supposons maintenant qu'un autre développeur souhaite apporter quelques ajouts à votre code. Dans cette situation, ils obtiendraient le code, y compris lesPipfile, et utiliseraient cette commande:

$ pipenv install --dev

Cela installe toutes les dépendances nécessaires au développement, qui incluent à la fois les dépendances régulières et celles que vous avez spécifiées avec l'argument--dev pendantinstall.

Lorsqu'une version exacte n'est pas spécifiée dans le Pipfile, la commandeinstall donne la possibilité aux dépendances (et sous-dépendances) de mettre à jour leurs versions.

Ceci est une note importante car elle résout certains des problèmes précédents dont nous avons discuté. Pour le démontrer, supposons qu'une nouvelle version de l'une de vos dépendances devienne disponible. Comme vous n'avez pas besoin d'une version spécifique de cette dépendance, vous ne spécifiez pas de version exacte dans lesPipfile. Lorsque vouspipenv install, la nouvelle version de la dépendance sera installée dans votre environnement de développement.

Maintenant, vous apportez vos modifications au code et exécutez des tests pour vérifier que tout fonctionne toujours comme prévu. (Vous avez des tests unitaires, non?) Maintenant, comme avant, vous verrouillez votre environnement avecpipenv lock, et unPipfile.lock mis à jour sera généré avec la nouvelle version de la dépendance. Comme précédemment, vous pouvez répliquer ce nouvel environnement en production avec le fichier de verrouillage.

Comme vous pouvez le voir dans ce scénario, vous n'avez plus à forcer les versions exactes dont vous n'avez pas vraiment besoin pour vous assurer que vos environnements de développement et de production sont les mêmes. Vous n'avez pas non plus besoin de vous tenir au courant de la mise à jour des sous-dépendances dont vous "ne vous souciez pas". Ce flux de travail avec Pipenv, combiné à vos excellents tests, résout les problèmes de faire manuellement toute votre gestion des dépendances.

Approche de résolution des dépendances de Pipenv

Pipenv tentera d'installer des sous-dépendances qui satisfont à toutes les exigences de vos dépendances principales. Cependant, s'il existe des dépendances conflictuelles (package_a a besoin depackage_c>=1.0, maispackage_b a besoin depackage_c<1.0), Pipenv ne pourra pas créer un fichier de verrouillage et affichera une erreur comme ce qui suit:

Warning: Your dependencies could not be resolved. You likely have a mismatch in your sub-dependencies.
  You can use $ pipenv install --skip-lock to bypass this mechanism, then run $ pipenv graph to inspect the situation.
Could not find a version that matches package_c>=1.0,package_c<1.0

Comme le dit l'avertissement, vous pouvez également afficher un graphique de dépendance pour comprendre vos dépendances de niveau supérieur et leurs sous-dépendances:

$ pipenv graph

Cette commande imprimera une structure arborescente montrant vos dépendances. Voici un exemple:

Flask==0.12.1
  - click [required: >=2.0, installed: 6.7]
  - itsdangerous [required: >=0.21, installed: 0.24]
  - Jinja2 [required: >=2.4, installed: 2.10]
    - MarkupSafe [required: >=0.23, installed: 1.0]
  - Werkzeug [required: >=0.7, installed: 0.14.1]
numpy==1.14.1
pytest==3.4.1
  - attrs [required: >=17.2.0, installed: 17.4.0]
  - funcsigs [required: Any, installed: 1.0.2]
  - pluggy [required: <0.7,>=0.5, installed: 0.6.0]
  - py [required: >=1.5.0, installed: 1.5.2]
  - setuptools [required: Any, installed: 38.5.1]
  - six [required: >=1.10.0, installed: 1.11.0]
requests==2.18.4
  - certifi [required: >=2017.4.17, installed: 2018.1.18]
  - chardet [required: >=3.0.2,<3.1.0, installed: 3.0.4]
  - idna [required: >=2.5,<2.7, installed: 2.6]
  - urllib3 [required: <1.23,>=1.21.1, installed: 1.22]

À partir de la sortie depipenv graph, vous pouvez voir les dépendances de niveau supérieur que nous avons installées précédemment (Flask,numpy,pytest etrequests), et en dessous vous pouvez voir les packages dont ils dépendent.

De plus, vous pouvez inverser l'arborescence pour afficher les sous-dépendances avec le parent qui l'exige:

$ pipenv graph --reverse

Cet arbre inversé peut être plus utile lorsque vous essayez de comprendre des sous-dépendances conflictuelles.

Le Pipfile

Pipfile a l'intention de remplacerrequirements.txt. Pipenv est actuellement l'implémentation de référence pour l'utilisation dePipfile. Il semble très probable quepip itself will be able to handle these files. En outre, il convient de noter quePipenv is even the official package management tool recommended by Python itself.

La syntaxe desPipfile estTOML et le fichier est séparé en sections. [dev-packages] pour les packages de développement uniquement,[packages] pour les packages minimum requis, et[requires] pour d'autres exigences comme une version spécifique de Python. Voir un exemple de fichier ci-dessous:

[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"

[dev-packages]
pytest = "*"

[packages]
flask = "==0.12.1"
numpy = "*"
requests = {git = "https://github.com/requests/requests.git", editable = true}

[requires]
python_version = "3.6"

Idéalement, vous ne devriez pas avoir de sous-dépendances dans vosPipfile. Ce que je veux dire par là, c'est que vous ne devez inclure que les packages que vous importez et utilisez réellement. Inutile de conserverchardet dans votrePipfile simplement parce qu’il s’agit d’une sous-dépendance derequests. (Pipenv l'installera automatiquement.) LesPipfile devraient transmettre les dépendances de niveau supérieur requises par votre paquet.

Le Pipfile.lock

Ce fichier permet des constructions déterministes en spécifiant les exigences exactes pour reproduire un environnement. Il contient des versions exactes des packages et des hachages pour prendre en charge une vérification plus sécurisée, quipip itself now supports également. Un exemple de fichier pourrait ressembler à ce qui suit. Notez que la syntaxe de ce fichier est JSON et que j'ai exclu des parties du fichier avec...:

{
    "_meta": {
        ...
    },
    "default": {
        "flask": {
            "hashes": [
                "sha256:6c3130c8927109a08225993e4e503de4ac4f2678678ae211b33b519c622a7242",
                "sha256:9dce4b6bfbb5b062181d3f7da8f727ff70c1156cbb4024351eafd426deb5fb88"
            ],
            "version": "==0.12.1"
        },
        "requests": {
            "editable": true,
            "git": "https://github.com/requests/requests.git",
            "ref": "4ea09e49f7d518d365e7c6f7ff6ed9ca70d6ec2e"
        },
        "werkzeug": {
            "hashes": [
                "sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b",
                "sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c"
            ],
            "version": "==0.14.1"
        }
        ...
    },
    "develop": {
        "pytest": {
            "hashes": [
                "sha256:8970e25181e15ab14ae895599a0a0e0ade7d1f1c4c8ca1072ce16f25526a184d",
                "sha256:9ddcb879c8cc859d2540204b5399011f842e5e8823674bf429f70ada281b3cc6"
            ],
            "version": "==3.4.1"
        },
        ...
    }
}

Notez la version exacte spécifiée pour chaque dépendance. Même les sous-dépendances commewerkzeug qui ne sont pas dans nosPipfile apparaissent dans cePipfile.lock. Les hachages sont utilisés pour garantir que vous récupérez le même package que lors du développement.

Il convient de noter à nouveau que vous ne devez jamais modifier ce fichier à la main. Il est destiné à être généré avecpipenv lock.

Fonctionnalités supplémentaires de Pipenv

Ouvrez un package tiers dans votre éditeur par défaut avec la commande suivante:

$ pipenv open flask

Cela ouvrira le packageflask dans l'éditeur par défaut, ou vous pouvez spécifier un programme avec une variable d'environnementEDITOR. Par exemple, j'utiliseSublime Text, donc je viens de définirEDITOR=subl. Il est donc très simple de fouiller dans les éléments internes d'un package que vous utilisez.


Vous pouvez exécuter une commande dans l'environnement virtuel sans lancer de shell:

$ pipenv run 

Recherchez les vulnérabilités de sécurité (et les exigences dePEP 508) dans votre environnement:

$ pipenv check

Supposons maintenant que vous n'ayez plus besoin d'un package. Vous pouvez le désinstaller:

$ pipenv uninstall numpy

De plus, supposons que vous souhaitiez effacer complètement tous les packages installés de votre environnement virtuel:

$ pipenv uninstall --all

Vous pouvez remplacer--all par--all-dev pour simplement supprimer les packages de développement.


Pipenv prend en charge le chargement automatique des variables d'environnement lorsqu'un fichier.env existe dans le répertoire de niveau supérieur. De cette façon, lorsque vouspipenv shell pour ouvrir l'environnement virtuel, il charge vos variables d'environnement à partir du fichier. Le fichier.env ne contient que des paires clé-valeur:

SOME_ENV_CONFIG=some_value
SOME_OTHER_ENV_CONFIG=some_other_value

Enfin, voici quelques commandes rapides pour savoir où se trouvent les choses. Comment savoir où se trouve votre environnement virtuel:

$ pipenv --venv

Comment savoir où se trouve votre maison de projet:

$ pipenv --where

Distribution de colis

Vous vous demandez peut-être comment tout cela fonctionne si vous avez l'intention de distribuer votre code sous forme de package.

Oui, je dois distribuer mon code sous forme de package

Comment Pipenv fonctionne-t-il avec les fichierssetup.py?

Il y a beaucoup de nuances à cette question. Tout d'abord, un fichiersetup.py est nécessaire lorsque vous utilisezsetuptools comme système de construction / distribution. C'est la norme de facto depuis un certain temps maintenant, maisrecent changes a rendu l'utilisation desetuptools facultative.

Cela signifie que les projets commeflit peuvent utiliser les nouveauxpyproject.toml pour spécifier un système de construction différent qui ne nécessite pas desetup.py.

Cela étant dit, dans un proche avenir,setuptools et unsetup.py qui l'accompagne resteront le choix par défaut pour de nombreuses personnes.

Voici un flux de travail recommandé lorsque vous utilisez unsetup.py pour distribuer votre package:

  • setup.py

  • Le mot cléinstall_requires doit inclure le package“minimally needs to run correctly.”

  • Pipfile

  • Représente les exigences concrètes de votre colis

  • Extrayez les dépendances minimales requises desetup.py en installant votre package à l'aide de Pipenv:

    • Utilisezpipenv install '-e .'

    • Cela se traduira par une ligne dans vosPipfile qui ressemble à quelque chose comme"e1839a8" = {path = ".", editable = true}.

  • Pipfile.lock

  • Détails pour un environnement reproductible généré à partir depipenv lock

Pour clarifier, mettez vos exigences minimales ensetup.py au lieu de directement avecpipenv install. Utilisez ensuite la commandepipenv install '-e .' pour installer votre package comme modifiable. Cela obtient toutes les exigences desetup.py dans votre environnement. Ensuite, vous pouvez utiliserpipenv lock pour obtenir un environnement reproductible.

Je n'ai pas besoin de distribuer mon code sous forme de package

Génial! Si vous développez une application qui n’est pas destinée à être distribuée ou installée (un site Web personnel, une application de bureau, un jeu ou similaire), vous n’avez pas vraiment besoin d’unsetup.py.

Dans cette situation, vous pouvez utiliser la combinaisonPipfile /Pipfile.lock pour gérer vos dépendances avec le flux décrit précédemment pour déployer un environnement reproductible en production.

J'ai déjà unrequirements.txt. Comment puis-je convertir enPipfile?

Si vous exécutezpipenv install, il devrait détecter automatiquement lesrequirements.txt et les convertir enPipfile, en produisant quelque chose comme ce qui suit:

requirements.txt found, instead of Pipfile! Converting…
Warning: Your Pipfile now contains pinned versions, if your requirements.txt did.
We recommend updating your Pipfile to specify the "*" version, instead.

Prenez note de l'avertissement ci-dessus.

Si vous avez épinglé des versions exactes dans votre fichierrequirements.txt, vous souhaiterez probablement modifier vosPipfile pour ne spécifier que les versions exactes dont vous avez vraiment besoin. Cela vous permettra de bénéficier des avantages réels de la transition. Par exemple, supposons que vous disposez des éléments suivants, mais que vous n’avez pas vraiment besoin de la version exacte denumpy:

[packages]
numpy = "==1.14.1"

Si vous n'avez pas d'exigences de version spécifiques pour vos dépendances, vous pouvez utiliser le caractère générique* pour indiquer à Pipenv que n'importe quelle version peut être installée:

[packages]
numpy = "*"

Si vous vous sentez nerveux à l'idée d'autoriser une version avec les*, il est généralement prudent de spécifier une version supérieure ou égale à la version sur laquelle vous vous trouvez déjà afin de pouvoir toujours profiter des nouvelles versions:

[packages]
numpy = ">=1.14.1"

Bien sûr, rester à jour avec les nouvelles versions signifie également que vous êtes responsable de vous assurer que votre code fonctionne toujours comme prévu lorsque les packages changent. Cela signifie qu'une suite de tests est essentielle à l'ensemble de ce flux Pipenv si vous souhaitez garantir le bon fonctionnement des versions de votre code.

Vous autorisez les packages à se mettre à jour, exécutez vos tests, assurez-vous qu'ils réussissent tous, verrouillez votre environnement, puis vous pouvez rester tranquille en sachant que vous n'avez pas introduit de changements de rupture. Si les choses se cassent à cause d'une dépendance, vous avez des tests de régression à écrire et potentiellement plus de restrictions sur les versions des dépendances.

Par exemple, sinumpy==1.15 est installé après l'exécution depipenv install et qu'il casse votre code, ce que vous remarquerez, espérons-le, pendant le développement ou pendant vos tests, vous avez quelques options:

  1. Mettez à jour votre code pour qu'il fonctionne avec la nouvelle version de la dépendance.

    Si la rétrocompatibilité avec les versions précédentes de la dépendance n'est pas possible, vous devrez également remplacer la version requise dans vosPipfile:

    [packages]
    numpy = ">=1.15"
  2. Restreignez la version de la dépendance dans lesPipfile à< la version qui vient de casser votre code:

    [packages]
    numpy = ">=1.14.1,<1.15"

L'option 1 est préférée car elle garantit que votre code utilise les dépendances les plus à jour. Cependant, l'option 2 prend moins de temps et ne nécessite pas de modifications de code, juste des restrictions sur les dépendances.


Vous pouvez également installer à partir de fichiers d'exigences avec le même argument-r quepip prend:

$ pipenv install -r requirements.txt

Si vous avez undev-requirements.txt ou quelque chose de similaire, vous pouvez également les ajouter auxPipfile. Ajoutez simplement l'argument--dev pour qu'il soit placé dans la bonne section:

$ pipenv install -r dev-requirements.txt --dev

De plus, vous pouvez aller dans l'autre sens et générer des fichiers d'exigences à partir d'unPipfile:

$ pipenv lock -r > requirements.txt
$ pipenv lock -r -d > dev-requirements.txt

Et après?

Il me semble qu'une progression naturelle pour l'écosystème Python serait un système de construction qui utilise lesPipfile pour installer les dépendances minimales requises lors de la récupération et de la construction d'un package à partir d'un index de package (comme PyPI). Il est important de noter à nouveau que lePipfile design specification est toujours en développement et que Pipenv n'est qu'une implémentation de référence.

Cela étant dit, je pourrais voir un futur où la sectioninstall_requires desetup.py n’existe pas, et où lePipfile est référencé pour des exigences minimales à la place. Ou lesetup.py a complètement disparu, et vous obtenez des métadonnées et d'autres informations d'une manière différente, en utilisant toujours lesPipfile pour obtenir les dépendances nécessaires.

Est-ce que Pipenv vaut le détour?

Absolument. Même s'il s'agit simplement d'un moyen de consolider les outils que vous utilisez déjà (pip &virtualenv) dans une seule interface. Mais c’est beaucoup plus que cela. Avec l'ajout desPipfile, vous ne spécifiez que les dépendances dont vous avez vraiment besoin.

Vous n'avez plus le mal de tête de gérer vous-même les versions de tout pour vous assurer de pouvoir répliquer votre environnement de développement. Avec lesPipfile.lock, vous pouvez développer l'esprit tranquille en sachant que vous pouvez reproduire exactement votre environnement n'importe où.

En plus de tout cela, il semble très probable que le formatPipfile sera adopté et pris en charge par des outils Python officiels commepip, il serait donc avantageux d'être en avance sur le jeu. Oh, et assurez-vous également de mettre à jour tout votre code vers Python 3:2020 is coming up fast.

Références, lectures complémentaires, discussions intéressantes, etc.

Free Bonus:Click here to get access to a free 5-day class qui vous montre comment éviter les problèmes courants de gestion des dépendances avec des outils tels que Pip, PyPI, Virtualenv et les fichiers d'exigences.