Pipenv — это упаковочный инструмент для Python, который решает некоторые общие проблемы, связанные с типичным рабочим процессом с использованием pip, virtualenv и старого доброго requirements.txt.

Помимо решения некоторых распространенных проблем, он объединяет и упрощает процесс разработки в едином инструменте командной строки. В этом уроке вы узнаете, какие проблемы решает Pipenv и как управлять зависимостями Python с помощью Pipenv. Кроме того, будет рассказано, как Pipenv сочетается с предыдущими методами распространения пакетов.

Содержание

Проблемы, которые решает Pipenv

Чтобы понять преимущества Pipenv, важно пройтись по текущим методам упаковки и управления зависимостями в Python.

Начнем с типичной ситуации со сторонними пакетами. Затем мы построим свой путь к развертыванию полного приложения Python.

Управление зависимостями с помощью файла requirements.txt

Представьте, что вы работаете над проектом Python, в котором используется сторонний пакет, например flask. Вам необходимо указать это требование, чтобы другие разработчики и автоматизированные системы могли запускать ваше приложение.

Итак, вы решили включить зависимость колбы в файл requirements.txt:

flask

Отлично, все локально работает нормально, и после некоторой отладки вы решаете перенести его в рабочую среду. Здесь становится немного страшно …

В приведенном выше файле requirements.txt не указано, какую версию flask использовать. В этом случае pip install -r requirements.txt по умолчанию установит последнюю версию. Это нормально, если в новейшей версии нет изменений интерфейса или поведения, которые нарушают работу нашего приложения.

В качестве примера предположим, что вышла новая версия flask. Однако он не имеет обратной совместимости с версией, которую вы использовали во время разработки.

Сейчас же,допустим, вы развертываете свое приложение в производственной среде и выполняете команду pip install -r requirements.txt. Pip получает последнюю, несовместимую с предыдущими версиями версию flask, и вот так ваше приложение ломается … в производстве.

«Эй, у меня,на моей машине, всё работало!» — не самое приятное открытие.

В этот момент, вы знаете, что версия flask, которую вы использовали во время разработки, работала нормально. Итак, чтобы что-то исправить, вы стараетесь быть более конкретным в файле requirements.txt. Вы добавляете спецификатор версии в зависимость flask, что называется закреплением зависимости:

flask==0.12.1

Закрепление зависимости flask к конкретной версии гарантирует, что pip install -r requirements.txt устанавливает точную версию flask, которую вы использовали во время разработки. Но так ли это на самом деле?

Имейте в виду, что сам flask также имеет зависимости (которые устанавливаются автоматически). Однако сам Flask не указывает точные версии для своих зависимостей. Например, он допускает любую версию Werkzeug> = 0.14.

Опять же, для этого примера, предположим, что была выпущена новая версия Werkzeug, но она привносит в ваше приложение ошибку, препятствующую нормальной работе.

Когда вы на этот раз выполните pip install -r requirements.txt в производственной среде, вы получите flask == 0.12.1, поскольку вы закрепили это требование. Однако, к сожалению, вы получите последнюю версию Werkzeug с ошибками. И снова продукт не работает в производстве.

Настоящая проблема здесь в том, что сборка не является детерминированной. Я имею в виду, что при одном и том же вводе (файл requirements.txt) pip не всегда создает одинаковую среду. В данный момент, вы не можете легко воспроизвести точную среду, имеющуюся на вашем компьютере для разработки в производственной среде.

Типичное решение этой проблемы — замораживание pip. Эта команда позволяет получить точные версии для всех установленных на данный момент сторонних библиотек, включая автоматически установленный пакет зависимостей. Таким образом, вы можете заморозить все в разработке, чтобы обеспечить одинаковую среду в производственной среде.

Выполнение замораживания pip приводит к закреплению зависимостей, которые вы можете добавить в файл requirements.txt:

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

С помощью подобных закрепленных зависимостей вы можете гарантировать, что пакеты, установленные в вашей производственной среде, точно соответствуют пакетам в вашей среде разработки, чтобы ваш продукт не сломался неожиданно. Это «решение», к сожалению, приводит к совершенно новому набору проблем.

Теперь, когда вы указали точные версии каждого стороннего пакета, вы несете ответственность за поддержание этих версий в актуальном состоянии, даже если они являются подчиненными зависимостями flask. Что, если в Werkzeug == 0.14.1 обнаружена дыра в безопасности, которую разработчики пакета немедленно исправили в Werkzeug == 0.14.2? Вам действительно необходимо выполнить обновление до Werkzeug == 0.14.2, чтобы избежать проблем с безопасностью, связанных с предыдущим, непропатченная версия Werkzeug.

Во-первых, вы должны знать, что есть проблема с вашей версией. Затем вам нужно получить новую версию в производственной среде, прежде чем кто-то воспользуется брешей в безопасности. Итак, вам нужно вручную изменить свой файл requirements.txt, чтобы указать новую версию Werkzeug == 0.14.2. Как вы можете видеть в этой ситуации, ответственность за своевременное получение необходимых обновлений ложится на вас.

Правда в том, что вам действительно все равно, какая версия Werkzeug будет установлена, если она не нарушает ваш код. На самом деле, вы, вероятно, захотите установить последнюю версию, чтобы получать исправления ошибок, исправления безопасности и новые функции дополнительной оптимизации и т.д.

Реальный вопрос: «Как вы разрешаете детерминированные сборки для вашего проекта Python, не беря на себя ответственность за обновление версий субзависимостей?»

Предупреждение о спойлере: простой ответ — использовать Pipenv.

Разработка проектов с различными зависимостями

Давайте немного переключимся, чтобы поговорить о другой распространенной проблеме, которая возникает, когда вы работаете над несколькими проектами. Представьте, что ProjectA нужен django == 1.9, а ProjectBdjango == 1.10.

По умолчанию Python пытается хранить все ваши сторонние пакеты в общесистемном месте. Это означает, что каждый раз, когда вы хотите переключаться между ProjectA и ProjectB, вы должны убедиться, что установлена ​​правильная версия django. Это затрудняет переключение между проектами, потому что вам придется удалять и переустанавливать пакеты, чтобы соответствовать требованиям для каждого проекта. Стандартное решение — использовать виртуальную среду с собственным исполняемым файлом Python и сторонним хранилищем пакетов. Таким образом, ProjectA и ProjectB должным образом разделены. Теперь вы можете легко переключаться между проектами, поскольку они не используют одно и то же место для хранения пакетов. PackageA может иметь любую версию django в своей собственной среде, а PackageB может иметь то, что ему нужно, полностью отдельно. Очень распространенный инструмент для этого — virtualenv (или venv в Python 3).

Pipenv имеет встроенное управление виртуальной средой, так что у вас есть единый инструмент для управления пакетами.

Разрешение зависимостей

Что я имею в виду под разрешением зависимостей? Допустим, у вас есть файл requirements.txt, который выглядит примерно так:

package_a
package_b

Допустим, package_a имеет подчиненную зависимость package_c, а package_a требует определенной версии этого пакета: package_c> = 1.0. В свою очередь, package_b имеет ту же подзависимость, но требует package_c <= 2.0. В идеале, когда вы пытаетесь установить package_a и package_b, инструмент установки будет смотреть на требования для package_c (> = 1.0 и <= 2.0) и выберите версию, отвечающую этим требованиям. Вы бы надеялись, что инструмент разрешит зависимости, и ваша программа в конечном итоге заработает. Вот что я имею в виду под «разрешением зависимостей». К сожалению, сам по себе pip на данный момент не имеет реального разрешения зависимостей, но есть открытая проблема для его поддержки. То, как pip будет обрабатывать вышеуказанный сценарий, выглядит следующим образом:

  1. Он устанавливает package_a и ищет версию package_c, которая удовлетворяет первому требованию (package_c> = 1.0).
  2. Затем Pip устанавливает последнюю версию package_c для выполнения этого требования. Допустим, последняя версия package_c — 3.1.

Вот где (потенциально) начинаются проблемы. Если версия package_c, выбранная pip, не соответствует будущим требованиям (например, package_b требует package_c <= 2.0), установка завершится ошибкой.

«Решение» этой проблемы — указать диапазон, необходимый для подчиненной зависимости (package_c) в файле requirements.txt. Тогда, pip может разрешить этот конфликт и установить пакет, отвечающий этим требованиям:

package_c>=1.0,<=2.0
package_a
package_b

Однако, как и раньше, теперь вы занимаетесь непосредственно подчиненными зависимостями (package_c). Проблема в том, что если package_a изменяет свои требования без вашего ведома, указанные вами требования (package_c> = 1.0, <= 2.0) могут больше не быть приемлемыми, и установка может завершиться неудачно… снова. Настоящая проблема в том, что снова вы несете ответственность за своевременное соблюдение требований подчиненных зависимостей.

В идеале ваш инструмент установки должен быть достаточно умным, чтобы устанавливать пакеты, отвечающие всем требованиям, без явного указания версий подзависимостей.

Введение в Pipenv

Теперь, когда мы рассмотрели проблемы, давайте посмотрим, как Pipenv их решает.

Для начала установим его:

$ pip install pipenv

Как только вы это сделаете, вы можете забыть о pip, поскольку Pipenv, по сути, выступает в качестве замены. Он также представляет два новых файла: Pipfile (который предназначен для замены файла requirements.txt) и Pipfile.lock (который позволяет выполнять детерминированные сборки). Pipenv использует pip и virtualenv под капотом, но упрощает их использование с помощью единого интерфейса командной строки.

Пример использования

Давайте начнем с создания потрясающего приложения Python. Сначала создайте оболочку в виртуальной среде, чтобы изолировать разработку этого приложения:

$ pipenv shell

Это создаст виртуальную среду, если она еще не существует. Pipenv создает все ваши виртуальные среды в местоположении по умолчанию. Если вы хотите изменить поведение Pipenv по умолчанию, есть несколько переменных среды для настройки.

Вы можете принудительно создать среду Python 2 или 3 с аргументами --two и -three соответственно. В противном случае Pipenv будет использовать все, что найдет virtualenv по умолчанию.

Примечание: если вам требуется более конкретная версия Python, вы можете указать аргумент --python с нужной версией. Например: --python 3.6

Теперь вы можете установить необходимый сторонний пакет flask. О, но вы знаете, что вам нужна версия 0.12.1, а не последняя версия, так что будьте конкретны:

$ pipenv install flask==0.12.1

Вы должны увидеть в своем терминале что-то вроде этого:

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

Вы заметите, что создаются два файла: Pipfile и Pipfile.lock. Мы рассмотрим их подробнее через секунду. Давайте установим еще один сторонний пакет, numpy, для некоторой обработки чисел. Вам не нужна конкретная версия, поэтому не указывайте ее:

$ pipenv install numpy

Если вы хотите установить что-то непосредственно из системы контроля версий (VCS), вы можете! Вы указываете местоположения аналогично тому, как вы это делаете с pip. Например, чтобы установить библиотеку запросов из системы контроля версий, выполните следующие действия:

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

Обратите внимание на аргумент -e выше, чтобы сделать установку доступной для редактирования. В настоящее время это требуется, чтобы Pipenv выполнял разрешение подчиненных зависимостей.

Допустим, у вас также есть несколько модульных тестов для этого замечательного приложения, и вы хотите использовать pytest для их запуска. Вам не нужен pytest в производственной среде, поэтому вы можете указать, что эта зависимость предназначена только для разработки, с помощью аргумента --dev:

$ pipenv install pytest --dev

Указание аргумента --dev поместит зависимость в специальное место [dev-packages] в Pipfile. Эти пакеты разработки устанавливаются только в том случае, если вы укажете аргумент --dev с помощью pipenv install.

В разных разделах зависимости, необходимые только для разработки, отделяются от зависимостей, необходимых для реальной работы базового кода. Обычно это можно сделать с помощью дополнительных файлов требований, таких как dev-requirements.txt или test-requirements.txt. Теперь все объединено в один файл Pipfile в разных разделах.

Хорошо, допустим, у вас есть все, что работает в вашей локальной среде разработки, и вы готовы запустить это в производство. Для этого нужно заблокировать свою среду, чтобы вы могли убедиться, что у вас есть такая же в производстве:

$ pipenv lock

Этим создадим/обновим ваш Pipfile.lock, который вам никогда не понадобится (и никогда не предполагается) редактировать вручную. Вы всегда должны использовать сгенерированный файл.

Теперь, когда вы получите свой код и Pipfile.lock в производственной среде, вы должны установить последнюю записанную успешную среду:

$ pipenv install --ignore-pipfile

Это говорит Pipenv игнорировать Pipfile для установки и использовать то, что находится в Pipfile.lock. Учитывая этот Pipfile.lock, Pipenv создаст ту же среду, что и у вас, когда вы запускали блокировку pipenv, подзависимости и все такое. Файл блокировки позволяет выполнять детерминированные сборки, делая снимки всех версий пакетов в среде (аналогично результату зависания пакетов).

Теперь допустим, что другой разработчик хочет внести некоторые дополнения в ваш код. В этой ситуации они получат код, включая Pipfile, и воспользуются этой командой:

$ pipenv install --dev

При этом будут установлены все зависимости, необходимые для разработки, включая как обычные зависимости, так и те, которые вы указали с помощью аргумента --dev во время установки.

Если точная версия не указана в Pipfile, команда install дает возможность зависимостям (и подчиненным зависимостям) обновить свои версии.

Это важное замечание, поскольку оно решает некоторые из ранее обсуждавшихся проблем. Для демонстрации допустим, что стала доступна новая версия одной из ваших зависимостей. Поскольку вам не нужна конкретная версия этой зависимости, вы не указываете точную версию в Pipfile. Когда вы устанавливаете pipenv, новая версия зависимости будет установлена ​​в вашей среде разработки.

Теперь вы вносите изменения в код и запускаете несколько тестов, чтобы убедиться, что все по-прежнему работает должным образом. (У вас ведь есть модульные тесты, верно?) Теперь, как и раньше, вы блокируете свою среду с помощью pipenv lock и обновленного Pipfile.lock будет сгенерирован с новой версией зависимости. Как и раньше, вы можете реплицировать эту новую среду в производственной среде с помощью файла блокировки.

Как видно из этого сценария, вам больше не нужно форсировать точные версии, которые вам действительно не нужны, чтобы гарантировать, что ваша среда разработки и производственная среда совпадают. Вам также не нужно постоянно обновлять подчиненные зависимости, которые вам «не нужны». Этот рабочий процесс с Pipenv в сочетании с вашим отличным тестированием устраняет проблемы, связанные с ручным выполнением всего вашего управления зависимостями.

Подход Pipenv к разрешению зависимостей

Pipenv попытается установить подчиненные зависимости, которые удовлетворяют всем требованиям ваших основных зависимостей. Однако, если есть конфликтующие зависимости (package_a требуется package_c> = 1.0, а package_b требуется package_c <1.0), Pipenv не сможет создать файл блокировки и выдаст ошибку, подобную следующей:

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
Что в переводе на чисто русский выглядит так:
Предупреждение: ваши зависимости не могут быть разрешены. Вероятно, у вас есть несоответствие в ваших зависимостях.
Вы можете использовать $ pipenv install --skip-lock, чтобы обойти этот механизм, а затем запустить $ pipenv graph, чтобы проверить ситуацию.
Не удалось найти версию, соответствующую package_c> = 1.0, package_c <1.0

Как говорится в предупреждении, вы также можете показать график зависимостей, чтобы понять свои зависимости верхнего уровня и их подчиненные зависимости:

$ pipenv graph

Эта команда распечатает древовидную структуру, показывающую ваши зависимости. Вот пример:

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]

Из выходных данных графика pipenv вы можете увидеть зависимости верхнего уровня, которые мы установили ранее (Flask, numpy, pytest и запросы), а под ними вы можете увидеть пакеты, от которых они зависят.

Кроме того, вы можете перевернуть дерево, чтобы показать подчиненные зависимости с родительским элементом, который этого требует:

$ pipenv graph --reverse

Это перевернутое дерево может быть более полезным, когда вы пытаетесь выяснить конфликтующие подзависимости.

Pipfile

Pipfile намеревается заменить файл requirements.txt. Pipenv в настоящее время является эталонной реализацией для использования Pipfile. Похоже, что сам pip сможет обрабатывать эти файлы. Также стоит отметить, что Pipenv даже является официальным инструментом управления пакетами, рекомендованным самим Python.

Синтаксис Pipfile - TOML, и файл разделен на разделы. [dev-packages] для пакетов только для разработки, [packages] для минимально необходимых пакетов и [requires] для других требований, таких как конкретная версия Python. См. Пример файла ниже:

[ [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"

В идеале у вас не должно быть никаких зависимостей в вашем Pipfile. Я имею в виду, что вы должны включать только те пакеты, которые вы фактически импортируете и используете. Нет необходимости хранить chardet в вашем Pipfile только потому, что это подчиненная зависимость запросов. (Pipenv установит его автоматически.) Pipfile должен передать зависимости уровня, которые требуются вашему пакету.

Pipfile.lock

Этот файл позволяет выполнять детерминированные сборки, указывая точные требования для воспроизведения среды. Он содержит точные версии пакетов и хэшей для поддержки более безопасной проверки, которую теперь поддерживает и сам pip. Пример файла может выглядеть следующим образом. Обратите внимание, что синтаксис этого файла - JSON, и что я исключил части файла с помощью ...:

{
    "_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"
        },
        ...
    }
}

Обратите внимание на точную версию, указанную для каждой зависимости. Даже суб-зависимости, такие как werkzeug, которых нет в нашем Pipfile, появляются в этом Pipfile.lock. Хэши используются, чтобы гарантировать, что вы получаете тот же пакет, что и при разработке.

Стоит еще раз отметить, что вы никогда не должны изменять этот файл вручную. Он предназначен для генерации с помощью блокировки pipenv.

Дополнительные функции Pipenv

Откройте сторонний пакет в редакторе по умолчанию с помощью следующей команды:

$ pipenv open flask

Это откроет пакет flask в редакторе по умолчанию, или вы можете указать программу с переменной окружения EDITOR. Например, я использую Sublime Text, поэтому просто устанавливаю EDITOR = subl. Это упрощает копание во внутреннем устройстве пакета, который вы используете.

Вы можете запустить команду в виртуальной среде без запуска оболочки:

$ pipenv run 

Проверьте наличие уязвимостей безопасности (и требований PEP 508) в вашей среде:

$ pipenv check

Допустим, вам больше не нужен пакет. Вы можете удалить его:

$ pipenv uninstall numpy

Кроме того, допустим, вы хотите полностью стереть все установленные пакеты из виртуальной среды:

$ pipenv uninstall --all

Вы можете заменить --all на --all-dev, чтобы просто удалить пакеты разработчика.

Pipenv поддерживает автоматическую загрузку переменных среды, когда файл .env существует в каталоге верхнего уровня. Таким образом, когда вы запускаете оболочку pipenv для открытия виртуальной среды, она загружает ваши переменные среды из файла. Файл .env просто содержит пары ключ-значение:

SOME_ENV_CONFIG=some_value
SOME_OTHER_ENV_CONFIG=some_other_value

Наконец, вот несколько быстрых команд, чтобы узнать, где что находится. Как узнать, где находится ваша виртуальная среда:

$ pipenv --venv

Как узнать, где находится дом вашего проекта:

$ pipenv --where

Распространение пакетов

Вы можете спросить, как все это работает, если вы собираетесь распространять свой код в виде пакета.

Да, мне нужно распространять свой код в виде пакета

Как Pipenv работает с файлами setup.py?

В этом вопросе много нюансов. Во-первых, файл setup.py необходим, когда вы используете setuptools в качестве системы сборки/распространения. Некоторое время это было стандартом де-факто, но недавние изменения сделали использование инструментов настройки необязательным. Это значит, что такие проекты, как flit, могут использовать новый pyproject.toml для указания другой системы сборки, для которой не требуется setup.py.

При этом в ближайшем будущем setuptools и сопровождающий файл setup.py по-прежнему будут выбором по умолчанию для многих людей.

Вот рекомендуемый рабочий процесс, когда вы используете setup.py как способ распространения вашего пакета:

  • setup.py
  • Ключевое слово install_requires должно включать все, что пакету «минимально необходимо для правильной работы».
  • Pipfile
  • Представляет конкретные требования к вашему пакету
  • Получите минимально необходимые зависимости из setup.py, установив пакет с помощью Pipenv:
    • Используйте pipenv install '-e'.
    • В результате в вашем Pip-файле появится строка, которая выглядит примерно так: "e1839a8" = {path = ".", Editable = true}.
  • Pipfile.lock
  • Подробная информация о воспроизводимой среде, созданной из блокировки pipenv

Чтобы уточнить, укажите минимальные требования в setup.py, а не непосредственно с помощью pipenv install. Затем используйте команду pipenv install '-e.', чтобы установить ваш пакет как редактируемый. Таким образом, вы получите все требования из setup.py в вашу среду. Затем вы можете использовать блокировку pipenv для получения воспроизводимой среды.

Мне не нужно распространять свой код в виде пакета

Отлично! Если вы разрабатываете приложение, которое не предназначено для распространения или установки (личный веб-сайт, настольное приложение, игра или подобное), вам действительно не нужен setup.py.

В этой ситуации вы можете использовать Pipfile/Pipfile.lock combo для управления зависимостями с помощью описанного ранее потока для развертывания воспроизводимой среды в производственной среде.

У меня уже есть файл requirements.txt. Как мне преобразовать в Pipfile?

Если вы запустите pipenv install, он должен автоматически обнаружить файл requirements.txt и преобразовать его в файл Pipfile, выводя что-то вроде следующего:

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.

Что в переводе на русский звучит такЖ

Найден файл requirements.txt вместо файла Pipfile! Преобразование…
Предупреждение: ваш Pipfile теперь содержит закрепленные версии, если ваш файл requirements.txt содержит.
Мы рекомендуем обновить ваш Pipfile, указав вместо этого версию "*".

Обратите внимание на это предупреждение.

Если вы закрепили точные версии в файле requirements.txt, возможно, вы захотите изменить свой Pipfile, указав только точные версии, которые вам действительно нужны. Это позволит вам получить реальные преимущества перехода. Например, предположим, что у вас есть следующее, но эта точная версия numpy вам не нужна:

[packages]
numpy = "==1.14.1"

Если у вас нет конкретных требований к версии для ваших зависимостей, вы можете использовать подстановочный знак *, чтобы сообщить Pipenv, что можно установить любую версию:

[packages]
numpy = "*"

Если вы нервничаете из-за того, что разрешаете какую-либо версию с *, обычно лучше указать версию, которая больше или равна той, что у вас уже есть, чтобы вы по-прежнему могли использовать преимущества новых версий:

[packages]
numpy = ">=1.14.1"

Конечно, оставаться в курсе новых выпусков также означает, что вы несете ответственность за то, чтобы ваш код по-прежнему функционировал должным образом при изменении пакетов. Это означает, что набор тестов необходим для всего этого потока Pipenv, если вы хотите гарантировать работоспособность выпусков своего кода.

Вы разрешаете пакетам обновляться, запускаете тесты, убедитесь, что все они проходят, заблокируйте свою среду, и тогда вы можете расслабиться, зная, что вы не внесли критических изменений. Если что-то сломается из-за зависимости, вам нужно написать несколько регрессионных тестов и, возможно, еще несколько ограничений на версии зависимостей.

Например, если numpy == 1.15 устанавливается после запуска pipenv install и ломает ваш код, что вы, надеюсь, заметите либо во время разработки, либо во время тестов, у вас есть несколько вариантов:

  1. Обновите свой код, чтобы он работал с новой версией зависимости.

    Если обратная совместимость с предыдущими версиями зависимости невозможна, вам также необходимо добавить нужную версию в свой Pipfile:

    [packages]
    numpy = ">=1.15"
    
  2. Ограничьте версию зависимости в Pipfile <той версии, которая только что нарушила ваш код:
    [packages]
    numpy = ">=1.14.1,<1.15"
    

Вариант 1 является предпочтительным, поскольку он гарантирует, что ваш код использует самые последние зависимости. Однако вариант 2 занимает меньше времени и не требует изменения кода, только ограничения на зависимости.

Вы также можете установить из файлов требований с тем же аргументом -r, который принимает pip:

$ pipenv install -r requirements.txt

Если у вас есть dev-requirements.txt или что-то подобное, вы также можете добавить их в Pipfile. Просто добавьте аргумент --dev, чтобы он попал в правильный раздел:

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

Кроме того, вы можете пойти другим путем и сгенерировать файлы требований из Pipfile:

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

Что дальше?

Мне кажется, что естественным развитием экосистемы Python будет система сборки, которая использует Pipfile для установки минимально необходимых зависимостей при извлечении и сборке пакета из индекса пакета (например, PyPI). Еще раз важно отметить, что спецификация проекта Pipfile все еще находится в разработке, а Pipenv - всего лишь эталонная реализация.

При этом я могу предвидеть будущее, в котором раздела install_requires в setup.py не будет, а вместо этого будут указаны минимальные требования к Pipfile. Или файл setup.py полностью удален, и вы получаете метаданные и другую информацию другим способом, все еще использую Pipfile для получения необходимых зависимостей.

Стоит ли проверять Pipenv?

Определенно. Даже если это просто способ объединить инструменты, которые вы уже используете (pip и virtualenv) в едином интерфейсе. Однако это гораздо больше. С добавлением Pipfile вы указываете только те зависимости, которые вам действительно нужны. Вам больше не нужно самостоятельно управлять версиями всего, чтобы убедиться, что вы можете воспроизвести свою среду разработки. С Pipfile.lock вы можете спокойно заниматься разработкой, зная, что вы можете точно воспроизвести свою среду где угодно.

Вдобавок ко всему, весьма вероятно, что формат Pipfile будет принят и поддержан официальными инструментами Python, такими как pip, поэтому было бы полезно быть впереди всех. Да, и убедитесь, что вы также обновляете весь свой код до Python 3: 2020 год уже прошёл.

Ссылки, дополнительные материалы для чтения, интересные обсуждения и т.д.

Фундамент Pipenv: A Guide to the New Python Packaging Tool

Опубликовано Вадим В. Костерин

ст. преп. кафедры ЦЭиИТ. Автор более 130 научных и учебно-методических работ. Лауреат ВДНХ (серебряная медаль).

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *