В Python 3.8 предлагается добавить альтернативу виртуальным окружениям — локальную директорию с пакетами PEP 582 Python local packages directory.


Данный PEP предлагает добавить в Python механизм автоматического обнаружения директории __pypackages__ и использовать её при импорте в качестве источника установленных пакетов. Директория __pypackages__ будет иметь больший приоритет при импорте, чем глобальные или пользовательские директории с пакетами. Это позволит исключить создание, активацию или деактивацию виртуальных окружений.


Вот так будет выглядеть в Python 3.8 структура пакета с использованием __pypackages__ :


foo
    __pypackages__
        3.8
            lib
                bottle
    myscript.py

В статье я расскажу как использовать локальную директорию с пакетами не дожидаясь выхода Python 3.8.


В статье описан базовый пример, проверенный в Linux, Python 3.5. Для других платформ, возможно, понадобится внести изменения.


Установка пакетов в локальную директорию


Установка почти не отличается от обычной установки пакетов с помощью pip, за исключением дополнительной опции --target. В ней указываем полный или относительный путь до директории с локальными пакетами.


pip3 install --target="$PWD/__pypackages__/3.5/lib/" bar

$PWD — переменная с текущей рабочей директорией.


Будет создано следующее дерево директорий:


foo
    __pypackages__
        3.5
            lib
                bar
    myscript.py

Версию Python и вложенные директории приходится указывать вручную.


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


Еще один вариант установки

Есть еще один способ установки пакетов в конкретную директорию :


pip3 install --ignore-installed --install-option="--prefix=$PWD/__pypackages__" --install-option="--no-compile" bar

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


foo
    __pypackages__
        lib
            python3.5
                site-packages
                    bar
    myscript.py

Использование локальной директории с пакетами


После установки пакетов осталось указать интерпретатору где искать зависимости.


Для этого нужно внести в список sys.path путь до локальной директории с пакетами. Добавить путь достаточно в главный (загружаемый первым) модуль, в остальные добавлять не обязательно. После этого можно импортировать установленные локально пакеты.


import os
import sys

_PATH = '/__pypackages__/3.5/lib/'

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)) + _PATH)

import bar

Единственное условие — главный модуль должен быть на том же уровне вложенности, что и директория __pypackages__ .


Еще один способ указать Python, где искать пакеты — это выставить переменную окружения перед запуском скрипта.


PYTHONPATH="$PWD/__pypackages__/3.5/lib/:$PYTHONPATH" python3 ./myscript.py

Таким нехитрым способом можно добиться схожего с PEP 582 функционала уже сейчас.

Комментарии (57)


  1. Tanner
    20.03.2019 12:00

    Очень странный PEP. Выглядит как ползучий фичеризм и неосиляторство. В случаях, описанных в мотвационной части, пользователю либо достаточно pip install --user, либо всё равно придётся учить virtualenv. Если же хочется чего-то странного, то можно и поиграть с sys.path, как предлагает ОП. Зачем нужен ещё один глобальный механизм, непонятно.


    1. arghhh Автор
      20.03.2019 12:49

      Я данный PEP рассматриваю больше с точки зрения поставки зависимостей со своим пакетом «все своё ношу с собой», чем полноценную замену virtual env.


    1. Juralis
      20.03.2019 14:57

      Очевидно, для снижения барьера сложности. Python вынужден конкурировать сегодня с молодыми языками, вроде go, rust и node.js, в которых разрешение зависимостей реализовано значительно проще и удобнее. Развитие пакетного менеджера остро необходимо для роста популярности языка.


      1. Tanner
        20.03.2019 15:50

        Если существующие способы работы с зависимостями в Python слишком сложны (в чём я вообще-то сомневаюсь, но готов допустить), то как добавление ещё одного способа сделает жизнь проще?


        1. Juralis
          20.03.2019 16:06

          Если они хотят сделать нечто похожее на то, что сделано в npm или cargo, то там объективно порог вхождения ниже. Всё, что требуется для развертывания приложения с нуля — это запустить npm install в директории проекта. Есть там и свои подводные камни, но в 90% случаев этого достаточно.
          Дело не только в том, что управлять зависимостями в python сильно уж сложнее. А в том, возможно, что есть несколько вариантов и они не так активно насаждаются.
          Новичкам сложно порой с наскоку понять, как именно перенести своё приложение на сервер, что делать с версиями интерпретатора, библиотек и так далее. всё это создаёт избыточную сложность. Да, сделать можно всё, но наиболее востребованные сценарии всё же должны быть очевидны и просты, желательно настолько, чтобы не задумываясь можно было сделать правильно.


          1. Tanner
            20.03.2019 16:29
            +1

            Иначе говоря, ждём появления в Python новой системы управления зависимостями, альтернативной pip/virtualenv/pyenv? Ну, ок тогда, пусть расцветают сто цветов.


          1. onlinehead
            21.03.2019 01:25

            Это пожалуй похоже больше на механизм vendoring в Golang, вот только там тоже проблем достаточно. Все его реализации так или иначе experimental как раз по причинам отсутствия толкового механизма противоречащих зависимостей.
            Еще один путь импорта не особо помогает в ситуациях, когда модуль A зависит от модуля B версии 1, а модуль C от модуля B версии 2, а это мне видится больше проблемой, чем необходимость создания virtualenv.


    1. hardex
      20.03.2019 15:00

      virtualenv — костыль, который по сравнению с __pypackages__ привносит только сложности.


      1. Tanner
        20.03.2019 16:19

        по сравнению

        Мне кажется, вы не очень представляете, что сравниваете. Если virtualenv ? костыль, то только по сравнению с системами, стоящими на соответствующем уровне абстракции по отношению к CPython и пользовательскому коду. LXC, например. Это было бы понятно: Docker стартовал только в 2013, а virtualenv ? в 2007, когда дешёвого способа создать среду исполнения ещё не было. Вы же сравниваете одну фичу интерпретатора языка со сложным компонентом, управляющим средой, в которой этот интерпретатор запускается.


      1. Stas911
        20.03.2019 23:13

        del


      1. JTG
        21.03.2019 13:40

        Этот PEP решает только часть проблем (точнее, одну), из-за которых существует virtualenv. Если он не будет допилен до состояния, в котором сможет virtualenv заменить, и они продолжат существовать параллельно — то это лишь всё усложнит (there should be one — and preferably only one — obvious way to do it, ага). Если же будет допилен — естественным образом станет таким же костылём.

        Вот, навскидку:

        • Virtualenv позволяет создать несколько окружений с одной и той же версией python под разные архитектуры.
        • В virtualenv Python может быть собран с разными флагами (да, CPython часто собирают под специфичные задачи, в отличие от языка, из которого автор притащил этот PEP). В одном окружении может быть собран Python 3.6 с отладкой, во втором — без, а в третьем — какая-нибудь сборка прямо из HEAD, в четвёртом вообще MinGW.
        • Пакеты могут тащить с собой бинарники, которые сейчас попадают в venv/bin. Что предлагают авторы? Правильно, скопировать подход, используемый там — npmx (или что щас вместо него), и использовать pipx. Ух, как всё упростилось-то!


    1. microcoder
      21.03.2019 10:33

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


      import foo.bar <= 0.5

      каталог в пользовательском пространстве:


      python_lib
          +-- 3.5
          L-- 3.7
              +-- foo.0.4
              ¦   L-- bar
              L-- foo.1.0
                  L-- bar

      Вы скажите — Ну это же будет помойка, +100500 версий одного модуля, да и как быть с


      $ pip install --upgrade <PACKAGE>

      ?
      А не нужно апгрейдить существующий пакет! Только инсталлировать каждый раз новый пакет с новой версией. Но тогда же встанет проблема с версиями пакетов которые не используются в проектах и занимают место? Верно, такая проблема будет. Для этого инструмент pip можно снабдить чистильщиком, который просканирует проекты, найдет команду import, сопоставит с текущим каталогом пакетов в пользовательском пространстве и удалит лишние.


      Что в этой схеме не так? Почему, например, такое решение не принято до сих пор, которое решает проблему заодно и с зависимостями от тех или иных версий пакетов/библиотек?


      1. Tanner
        21.03.2019 11:54

        Параметризовать import ? это плохое решение, оно смешивает разные уровни. Импорт пакета ? это код, а версия пакета ? это инфраструктура. Когда, чтобы поменять что-то в инфраструктуре, приходится лезть в код ? это называется «прибили гвоздями».


        1. microcoder
          21.03.2019 16:38

          Я не понял Вашего аргумента.


          Когда, чтобы поменять что-то в инфраструктуре, приходится лезть в код ? это называется «прибили гвоздями»

          А почему бы нет? Если автор пакета решил, что его пакет зависит от какой-то версии другого пакета, например версии 1.x, и не работает, скажем с версией 2.x, то как вы будете менять инфраструктуру не залазия в код?


          1. Tanner
            21.03.2019 19:35

            Автор пакета или автор кода? Это разные роли. Автор пакета может вообще не знать Python, он может быть девопсом, например, и решать пакетированием чисто задачи доставки кода на стейдж/прод. А import должен писать джун, у которого вообще нет полномочий, чтобы решать, какую версию пакета использует проект.


            1. microcoder
              21.03.2019 20:48

              автор кода?

              Импорты кто пишет? Автор кода? Значит автор кода.


              он может быть девопсом, например, и решать пакетированием чисто задачи доставки кода на стейдж/прод.

              Так… и в чём сложности?
              Он же может выполнить pip requirements.txt который напишет автор кода или какой-нибудь авто-tools сгенерирует его для пакета. Зачем девопсу лезть в код? Не нужно.


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

              А это я не совсем понял. Речь о том, чтобы "физически" был запрет выбора иной версии пакета в проекте кроме той, что установил devops? Ну так и не будет у него такой возможности.


              1. Tanner
                21.03.2019 20:58

                То есть некий супер-pip при доставке должен пройтись по проекту и поредактировать код? А в вызовы importlib, наверное, чего-нибудь добавить эвристически, или посендбоксить их?


                1. microcoder
                  21.03.2019 22:53

                  Не понял, для чего "супер-pip"'у проходить по проекту, когда об этом может позаботится автор пакета и создать requirements.txt?


                  Даже если это пупер супер pip, то причем здесь редактирование? Максимум что нужно — это чтение.


                  Еще раз — параметризация import ИСКЛЮЧИТЕЛЬНО для автора проекта. Никаким девопсам никакого до этого дела не должно быть. Для них автор кода готовит requirements.txt.


  1. JTG
    20.03.2019 13:23

    Да это же получается node_modules. Надеюсь, не взлетит (тем более venv уже 7 лет как доступен «из коробки»).


    1. Juralis
      20.03.2019 14:50

      Отмечу, что штатная фича в виде
      npm install
      Которая автоматом подтягивает локально все зависимости проекта — сильно упрощают жизнь. Аналоги в python несколько переусложнены. Описание зависимостей и их установка в python не такое уж и приятное дело.


    1. wtpltd
      20.03.2019 15:01

      А что плохого в node_modules?


      1. Pydeg
        20.03.2019 16:28

        Ну самое очевидное — это полное дублирование одних и тех же зависимостей в пакетных директориях разных проектов.
        Думаю, не стоит говорить насколько это всё медленно и объемно, и чтобы хоть как-то исправить положение сейчас создают различные костыли, вроде складывания зависимостей в глобальную директорию пакетного менеджера, а потом создание симлинков в директории пакетов проекта. Работает это, конечно, гораздо быстрее, но для чего тогда, спрашивается, директория пакетов внутри проекта?


        1. wtpltd
          20.03.2019 17:44

          Да и пусть себе дублируется. Диск дешев. Так что этот аргумент такой, спорный.
          Медленно — ну и venv так работает да и любой, наверное, пакетный менеджер. Но это же разовая операция. В пределах датацентра или офиса можно держать проксирующий реестр, чтобы побыстрее устанавливалось. Возможно критично, если надо автоматом инстансы запускать, но тут проще собрать образ с уже установленными зависимостями.
          В целом, подход npm мне нравится — удалил каталог проекта и в системе никакого лишнего мусора не осталось.
          Но это мое субъективное, не претендующее на вселенскую истину.


          1. Pydeg
            20.03.2019 18:28
            +1

            Да и пусть себе дублируется. Диск дешев. Так что этот аргумент такой, спорный.
            Ну как сказать, средний фронтенд проект сейчас может весить несколько сотен мегабайт. CRA hello-world весит около 200 мб. По-моему диск ещё не настолько дешев, чтобы не обращать внимание на гигабайты.
            Медленно — ну и venv так работает да и любой, наверное, пакетный менеджер. Но это же разовая операция. В пределах датацентра или офиса можно держать проксирующий реестр, чтобы побыстрее устанавливалось.
            Это никак не решает проблему того, что файлы зависимостей нужно переместить в директорию с проектом. Это очень дорогая операция, которая замедляет установку зависимостей в разы.


            1. arghhh Автор
              20.03.2019 18:50

              Так в python и сейчас зависимости можно поставить глобально, в виртуальное окружение или как в pep предложено. Каждый может выбрать подходящий ему способ.


            1. kmansoft
              21.03.2019 00:27
              +1

              Да и вообще раздражает (дублирование в каждом проекте) своей простотой. И усугубляется тем что модулей зависимостей буквально тысячи.


              Я вот хочу одну папку node_modules где-то в домашней папке (то есть "в систему" от рута ставить не хочу) и пусть все проекты на неё ссылаются.


              Если разные версии зависимостей то это должно тоже работать (скажем номер версии в названии папки с модулем где-то там внутри).


              Короче, хочу чтобы node.js умел как Go.


            1. wtpltd
              21.03.2019 02:13

              Ну бросьте. Даже личный SSD уже достаточно дешев, чтобы не дрожать за гигабайт. А если говорить о корпоративе, то диск, это наименее затратная часть вашего сервиса/приложения/сайта. В части приложения — данные тут ни при чем.

              Что касается дорогой операции — собираете готовый образ и единожды его на машину скачиваете. После этого плодите инстансы сразу. Если речь о том, что вы на локальной машине запускаете npm install и оно у вас работает долго — это не про прод. И это не та проблема, на которую надо грудью как на амбразуру. Это разовая операция. Можно пойти кофейку тяпнуть и Вареньке из бухгалтерии в декольту попялиться.


      1. JTG
        21.03.2019 13:44

        В Python (точнее, CPython) модули часто прибиты к конкретному питону (не к версии, а именно к кокретной сборке) гораздо сильнее, чем пакеты в ноде. Аналог node_modules местами просто неприменим, см. #comment_19919970


        1. wtpltd
          21.03.2019 16:00

          Мы тут от питона в сторону ноды заобсуждались.
          Конкретно для питона, я вообще не понимаю потребности в PEP 582. Если это машина разработчика, то там в любом случае virtualenv. А если прод, то это упаковывается в контейнер с нужной версией питона и без virtualenv.


  1. barker
    20.03.2019 15:52

    Вы же просто сделали папку, куда сложили пакеты и вставили её руками в PYTHONPATH. Вы думаете что это похоже на virtualenv? А интерпретатор конкретный виртуалить как, бинарники итд куда складываться будут?


    1. kmansoft
      20.03.2019 19:26

      Ну вот видимо в этом и разница по сравнению с venv в котором можно задать свою версию самого питона.


      Вот с бинарными файлами и правда не ясно.


      Вообще то лично мне venv не кажется сложным. Всего то одна команда создать (один раз) и одна команда "войти".


      Но видимо есть мнение что должен быть совсем простой механизм и вот лоне предложен.


      1. lair
        21.03.2019 00:10

        Ну вот видимо в этом и разница по сравнению с venv в котором можно задать свою версию самого питона.

        Мне вот, кстати, интересно, как это сделать, чтобы разработчик мог забрать репозиторий, ввести команду — и у него создался venv именно с конкретной фиксированной версией питона. А то как фиксировать версии пакета — понятно, а как фиксировать версии питона — не очень.


        1. kmansoft
          21.03.2019 00:18

          Вроде вот так


          virtualenv -p /usr/bin/python2.7 venv


          1. lair
            21.03.2019 00:19
            +1

            Мы с вами точно про один и тот же venv?


            1. kmansoft
              21.03.2019 00:36

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


              1. lair
                21.03.2019 00:44

                … что означает, что задача "разработчик забрал, ввел команду и получил повторяемое окружение" силами одного venv не решаема. Печально, придется оставить самопальный зоопарк из conda и venv.


                1. evgenyk
                  21.03.2019 18:42

                  Такая задача так просто не решается. Если в системе нет нужного питона, его нужно компилить, что достаточноо хлопотно.
                  Для таких штук лучше использовать докер. Но, внутри докера, лично я все равно испольую virtualenv это позволяет лугко упрвалять зависимостями.


                  1. lair
                    21.03.2019 18:56

                    Если в системе нет нужного питона, его нужно компилить, что достаточноо хлопотно.

                    Да меня бы устроило "бросить ошибку, если вызвали с неправильным питоном".


                    Для таких штук лучше использовать докер.

                    … докерфайл от которого хранить в репозитории и при каждой операции поднимать контейнер? Оверхед же.


                    1. evgenyk
                      21.03.2019 22:40

                      — Бросать ошибку если уж критично, должен ИМХО, разработчик.
                      — У меня многие проекты в докере. Виртуалэнв в докере, а исходники подмонтируются. Редактируешь на хосте, а запускаешь в контейнере. Перестраивать нужно редко, только если добавляешь внешние пакеты.


                      1. lair
                        21.03.2019 22:41

                        Бросать ошибку если уж критично, должен ИМХО, разработчик.

                        Тогда почему бы разработчику не проверять и версии пакетов?


                        У меня многие проекты в докере.

                        Оверхед же.


                        1. evgenyk
                          21.03.2019 22:44

                          А как еще, я работаю на рабочей станции, а целевая система — другая. А в чем оверхэд?


                          1. lair
                            21.03.2019 22:45

                            А в чем оверхэд?

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


                            Ну и да, при таком подходе virtualenv, очевидно, нафиг не сдался.


                            1. evgenyk
                              21.03.2019 22:52

                              — Скорость внесения изменений во что? У меня не влияет.
                              — virtualenv нужен, чтобы на целевой системе держать нужные для проекта версии библиотек и пакетов на уровне проекта.


                              1. lair
                                21.03.2019 22:53

                                Скорость внесения изменений во что? У меня не влияет.

                                В код.


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

                                … то есть на целевой системе у вас не докер?


                                1. evgenyk
                                  21.03.2019 22:59

                                  2) Целевая Есть без докера, есть с докером.
                                  1) На машине разработчика исходники подмонтированы с локального диска. ДОкер испольуется в одном случае как легковесная виртуальная машина. в другом как имидж для целевой системыю.


                                  1. lair
                                    21.03.2019 23:00

                                    И как вы гарантируете, что на целевой системе без докера нужная вам версия питона?


                                    1. evgenyk
                                      21.03.2019 23:02

                                      Никак, я просто знаю, какая на целевой системе. Если нужна будет особая, установим особую. Тогда и в докере на машине разработчика установлю такую же.


                                      1. lair
                                        21.03.2019 23:04

                                        Никак, я просто знаю, какая на целевой системе.

                                        Прекрасный подход.


                                        1. evgenyk
                                          21.03.2019 23:11

                                          ??? Это не подход, это специфика. Мы сами устанавливаем целевые системы. И во главе угла совсем другое — версии других софтов. Приходится танцевать от этого. Нужен будет питон 2.7.875-beta, будем испольовать его. Целевые системы мы устанавливаем сами.


                                          1. lair
                                            21.03.2019 23:12

                                            Специфика "я просто знаю, какая версия софта на целевой машине" — это все равно прекрасная специфика.


                                            1. evgenyk
                                              21.03.2019 23:15

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


  1. Zanak
    20.03.2019 20:29
    +1

    Я против.
    1. Разными версиями пакетов проблема не исчерпывается. Virtualenv умеет предоставлять и разные версии самого питона. А значит, Virtualenv все равно придется знать.
    2. Virtualenv на столько неудобен, что аналогичные механизмы реализованы для perl, php, ruby? Терзают смутные сомнения, что подобным образом работают gvm для golang и rustup для rust, но не проверял, наверняка не уверен. Вас ни чего не смущает в этом факте?
    3. Придется заново выпустить дофига пакетов, которые были собраны с использованием старого инструментария. Не уверен, что мантейнеры всех из них захотят этим заниматься.
    4. Даже на установке Virtualenv сэкономить, скорее всего, не получится. Есть такой инструмент для тестирования, tox, который, по сути, надстраивается над virtualenv и гоняет тесты на разных версиях пакетов.

    Да и конце -то концов, ни кто не обвиняет плюсы или раст за черезмерную сложность, хотя последний мне дается с заметным скрипом, почему нужно ломать проверенные годами инструменты в угоду непонятно кому и с сомнительной выгодой в перспективе?


    1. evgenyk
      21.03.2019 18:46

      ни кто не обвиняет плюсы или раст за черезмерную сложность

      Как никто!? Я обвиняю! Плюсы это ужасный монстр. Причем это не ужас, а ужас-ужас.


      1. Zanak
        22.03.2019 09:30

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


  1. 007913
    21.03.2019 07:20

    Вот из-за подобных PEP-ов Гвидо и ушел :(


  1. evgenyk
    21.03.2019 13:19
    -1

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


  1. Stas911
    22.03.2019 09:40
    +1

    В теме наблюдается острая нехватка этой картинки
    image