Зачем нужен __init__.py знает, наверное, любой питонист, но что насчёт __main__.py? Я видел немало проектов либо рабочих, либо на Github, которые не используют это магический файл, хотя могли бы сделать свою жизнь проще. На мой взгляд, __main__.py это лучший способ для взаимодействия с питоновскими модулями, состоящими из нескольких файлов.
Но давайте сначала разберёмся: как большинство людей запускают свои скрипты на Python?
Однажды вы напишете программу, которую захотите использовать и как импортируемый модуль, и как инструмент запускаемый из командной строки. Вы скорей всего в курсе, как обычно поступают в этом случае:
if __name__ == '__main__':
main(sys.argv)
Когда вы скармливаете скрипт интерпретатору, магическая глобальная переменная __name__ получает значение __main__. Таким образом мы узнаём, что это не импорт, а именно запуск. Например:
python myapp.py
И это прекрасно работает для одиночного файла.
Проблема
Но если вы похожи на меня, вы не захотите, чтобы всё ваше приложение теснилось в единственном файле. Разбиение логики по разным файлам упрощает редактирование и поддержку. Например:
.
+-- README.me
+-- requirements.txt
+-- setup.py
L-- src
+-- __init__.py
+-- client.py
+-- logic.py
+-- models.py
L-- run.py
Но пользователю, который склонировал проект из репозитория будет непонятно — какой из этих файлов главный? Неужели run.py? А может client.py? Где же искать знакомую строку if __name__ == '__main__'? Вот здесь-то __main__.py и способен проявить себя.
__main__.py
Файл __main__.py вызывается при запуске проекта с флагом модуля — -m. И это весьма удобно, если код предназначен и для использования в качестве модуля, и для запуска из консоли. Думайте об этом файле, как о месте куда можно класть всё, что вы обычно кладёте внутрь if __name__ == '__main__'. Давайте изменим проект из примера выше соответственно:
.
+-- README.me
+-- requirements.txt
+-- setup.py
L-- myapp
+-- __init__.py
+-- __main__.py
+-- client.py
+-- logic.py
+-- models.py
И, вуаля! Теперь можно просто запускать проект как обычный модуль.
python -m myapp
__main__.py будет выполняться автоматически. Это идеальное место для размещения интерфейса командной строки и обработки входных аргументов!
Комментарии (58)
masai
16.06.2019 02:03+1Гораздо удобнее прописать в
setup.py
все точки входа. И тогда не нужно запускать интерпретатор с ключом-m
, так как все точки входа будут обычными консольными командами.if __name__ == '__main__'
в этом случае тоже не нужен.gecube
17.06.2019 00:44+1Ага, больше логики в setup.py.
Я лично считаю упомянутую Вами практику порочной. Не вижу бенефитов от точек входа в setup.py, кроме путаницы, если нужно установить один модуль в два и более интерпретатора.masai
17.06.2019 14:44+1О какой логике речь? Мне кажется, вы не совсем поняли, что я имею в виду. Нужно лишь дописать в
setup.py
что-то вроде:
setuptools.setup( ... entry_points={ 'console_scripts': [ 'my_script=my_module:main', ], }, ...
И после установки через
pip
у вас появится общесистемная командаmy_script
. И точка входа main может находиться где угодно в соответствии с логикой кода, не обязательно в__main__.py
.
Если нужно установить один модуль в несколько интерпретаторов, то это значит, что у вас несколько виртуальных окружений. Соответственно, какое окружение активно, такой скрипт и будет вызван.
И это не порочная практика, а повсеместная и рекомендованная Python Packaging Authority. Посмотрите в
bin/
своего виртуального окружения. И взгляните на официальный пример проекта от PyPA.gecube
17.06.2019 14:59Я про эту штуку выше и писал — про врапперы. По ходу они pip'ом сами формируются и устанавливаются. ОК. Не самый плохой вариант. Уж всяко лучше, чем свои скрипты закидывать в /usr/local/bin...
eirnym
16.06.2019 10:18Этот файл придётся исключать из coverage при тестировании, ведь импорт модуля
__main__.py
приведёт к запуску программы. Это приведёт к тому, что в__main__.py
будет очень простой код, например код ниже, чтобы этот код не требовал покрытия тестами. В итоге, мы вернёмся кif __name__ == '__main__'
import run run.main()
MechanicZelenyy
16.06.2019 12:42+1Да, это так, вы же в __init__.py бизнес логику не прописываете?
eirnym
16.06.2019 12:45+1Видел очень много проектов, в которых в
__init__.py
написано очень много логики.MechanicZelenyy
16.06.2019 13:19+2Смотря какая логика, если бизнес то это плохо, не ориентируйтесь на них. Если именно логика импорта, то это нормально, например, если модуль содержит кроме питоновского кода, ещё и плюсовый/сишный, то логика импорта может быть сложной и платформеннозависимой.
gecube
17.06.2019 00:43+1И что? Миллион мух не может ошибаться? То что все говнячат — это, во-первых, не означает, что это нормально, а, во-вторых, отвечайте сами за свой продукт.
В третьих, наличие всякой хитрой логики, что в setup.py, что в init.py — за это надо руки отрывать
VovanZ
16.06.2019 14:15импорт модуля main.py приведёт к запуску программы
Нет, если в
__main__.py
точно так же запускать программу внутриif __name__ == '__main__'
eirnym
16.06.2019 14:32+1если называние модуля
__main__.py
, то ваша проверка лишена смысла: модуль и так будет всегда называться__main__
(по названию файла), как бы вы его не запускали или импортировали.Norrius
17.06.2019 05:14Проверка пройдёт только при запуске напрямую (
python my_package/__main__.py
) или через -m (python -m my_package
). Если__main__.py
импортируется из другого модуля, его__name__
будетmy_package.__main__
.
AMDmi3
16.06.2019 18:55Считаю что оформлять приложения в виде модулей это плохая идея, вместо этого использую
*.py
в корне проекта для приложений и подкаталоги для библиотек. Так сразу видно что можно запускать напрямую без поиска__main__.py
, не нужно никакихpython -m
для запуска, не создаётся искусственных связей между приложениями и библиотеками, которые потом мешают расширять проект (т.е. добавлять новые приложения, разбивать и объединять библиотеки, разбивать весь репозиторий). Примерная структура одного из текущих проектов:
+-- mycoolservice-updater.py # backend демон обновляющий данные +-- mycoolservice-*.py # какие-то ещё вспомогательные backend утилиты +-- mycoolservice-webapp.py # веб-приложение +-- mycoolservice # основная логика, используется всеми ¦ +-- __init__.py ¦ L-- *.py L-- mycoolserviceweb # логика специфичная для веб приложения, вьюхи +-- __init__.py L-- *.py
А
__main__.py
я бы оставил только для особой функциональности модулей типаpython -m json.tool
andreymal
17.06.2019 00:34И потом получать геморрой с публикацией всех этих скриптов на PyPI?
AMDmi3
17.06.2019 17:07Никакого геморроя. Много кто так и делает, см., например, ansible и black.
andreymal
17.06.2019 17:10Ок, я забыл про эту фичу (но всё равно не вижу смысла так делать)
setup( # ... scripts=[ 'bin/ansible', 'bin/ansible-playbook', 'bin/ansible-pull', 'bin/ansible-doc', 'bin/ansible-galaxy', 'bin/ansible-console', 'bin/ansible-connection', 'bin/ansible-vault', 'bin/ansible-config', 'bin/ansible-inventory', ], # ... )
gecube
17.06.2019 00:41Оформлять приложения в виде модулей это наиболее правильная идея, которая может возникнуть в рамках инфраструктуры и экосистемы питона. Типичный пример. Мне часто приходится работать с несколькими версиями питона на одной машине. И речь не про 2 vs 3, а про версии даже внутри одной линейки. Если скрипт запускать как исполняемый файл, то велика вероятность, что он запуститься не тем интерпретатором. В случае вызова python3.6 -m имя_модуля такой двусмысленности нет. И более того — если интерпретатор вырубается на отсутствующий модуль, то будет понятно, что делать.
AMDmi3
17.06.2019 17:09Скрипт можно запускать точно так же, причём на 1 аргумент короче.
gecube
17.06.2019 19:08Сомнительное преимущество. Здесь скорее дело в том, как "скрипт" установлен — как отдельно стоящий скрипт в /usr/bin и аналогах или как полноценный питон модуль.
В любом случае вопрос ещё в том, как грамотно обернуть питон-модуль — или в pypi, или в нативный пакет для операционной системы. Меня, честно говоря, очень расстраивает, когда setup.py приносит в систему то, что не ожидается изначально. Примеров масса.masai
17.06.2019 19:49Если ставить пакеты в виртуальные окружения, то всё, что пакеты приносят, остаётся в директории виртуального окружения и проблем нет. Всегда можно снести и поставить пакеты заново, если что-то пошло не так.
Ставить как нативные для системы особого смысла нет. Лишняя возня со сборкой, а премуществ никаких особо и нет.
gecube
17.06.2019 20:06Ну, чего все такие категоричные? Я вообще ума приложить не могу.
Часть вещей — действительно имеет смысл ставить в venv. Даже тот же хваленый airflow. Ansible — тоже скорее да, чем нет. Пользоваться, конечно, становится неудобно, но для сервисов это не проблема — обернул в сервис и понеслась. Часть вещей — не имеет смысла ставить в venv. Вообще — это какие-то общесистемные вещи, например, пакет для управления docker. Или еще что-то подобное. Ну, и опять же — мой тезис, что setup.py в теории может любую дрянь притащить в систему. Мне кажется, что не нужно пытаться противопоставлять средства нативной доставки приложений (deb/rpm в первую очередь) и pip, а все-таки пытаться их дружить. Благо в первых все-таки есть средства рекурсивной валидации зависимостей — для Питон-экосистемы это прям больное место.masai
18.06.2019 02:14Да, может, с категоричностью я палку перегнул, наверное. Просто сужу с точки зрения задач, которые именно я решал. Спорить не буду, я уже ниже написал, что каждый инструмент хорош, когда его используют по назначению.
Насчёт дряни — это к мейнтейнеру. Скрипты deb-пакетов тоже могут дел натворить. Или вы плавно ведёте к экзотике вроде Nix/Guix?
AMDmi3
17.06.2019 23:11Ставить как нативные для системы особого смысла нет.
Не будь смысла, опакечивание Python модулей не было бы распространено повсеместно: https://repology.org/projects/?search=python%3A
Лишняя возня со сборкой, а премуществ никаких особо и нет.
Возня со сборкой получается когда пакеты собираются вне системного репозитория, и зачастую не могут найти нужные нативные библиотеки и компилятор. А потом получается ещё больше возни, потому что системные библиотеки обновляются или удаляются, не зная ничего о том что от них зависят питоновские модули, в итоге ломая последние. Нет, система управления пакетами должна быть единой для всего.
masai
18.06.2019 02:11Не будь смысла, опакечивание Python модулей не было бы распространено повсеместно:
Я, возможно, потерял нить разговора, но речь шла о собственных проектах. Не берусь судить о всех, но лично мне проще устанавливать пакеты через
pip
прямо из репозиторияgit
, чем поддерживать пакеты и держать репозитории для разных операционных систем. Чтоб не компилировать каждый раз бинарные модули, можно держать один репозиторий с wheel-файлами. Это удобнее, если, например, разработка ведётся под MacOS, на серверах Ubuntu, а в докере вообще какой-нибудь Alpine.
Возня со сборкой получается когда пакеты собираются вне системного репозитория, и зачастую не могут найти нужные нативные библиотеки и компилятор.
Допускаю, что такое возможно, хотя я с таким не сталкивался. Ещё раз замечу, что дискуссия ведётся в контексте организации своего проекта, так как статья была об этом. А не о преимуществах и недостатках разных способов дистрибуции вообще.
У меня же сложилось такое мнение (не обязательно верное). Если пакет зависит от каких-то нативных библиотек, то ничто не мешает проверять наличие этих библиотек при компиляции и установке. Или вовсе помещать их в wheel-файл. Пакеты под линукс следует собирать не на коленке, а в официальном докере manylinux1, что даст какую-то уверенность в работоспособности. Мне кажется, что перечисленные проблемы — это не проблема способа дистрибуции, а проблема мейнтейнера. Вот, тот же numpy. Отлично ставится через
pip
, хотя насквозь бинарный.
С другой стороны, если пользоваться установленными в систему пакетами, то как работать с виртуальными окружениями? Да, на сервере это нечасто нужно, чтоб стояло несколько окружений. Но на машине разработчика — очень даже.
В общем, всё средства хороши на своём месте.
gecube
18.06.2019 08:42Мы тоже pip'ом ставим из гита. Это действительно удобно и способствует быстрой разработке.
Но нормальные пакеты (в первую очередь deb/rpm, во вторую pypi/wheel) становятся жизненно необходимы, если этими модулями будут пользоваться другие люди. Условно — решил проблему — поделись с сообществом. Ничего там сверхсекретного в твоей разработке скорее всего нет. Тот же ML — там ноу-хау в моделях (т.е. весовых коэффициентах), а не во фреймворках обучения.
Касательно дружить — да, все верно, что setup py ничего не знает о системе, куда будет установлен. Скажем, Вам нужна библиотека для работы с БД Postgres. В разных дистрибутивах она может быть разной версии, лежать по разным путям и устанавливаться из пакетов с разными именами. Унификации здесь нет и в ближайшее время не предвидится. В результате ставя питон-пакет из официального репозитория ОС — эти зависимости подтягиваются, т.к. мейнтейнеры их описали и гарантируют их. А ставя через pip — либо как повезет, либо там будет дичь в setup.py.
Насчёт дичи в deb/rpm — да, она тоже возможна, но мейнтейнеры официальных репозиториев такое не пропустят, т.е. проблема возможна только со сторонними репами
AMDmi3
17.06.2019 22:58или в pypi, или в нативный пакет для операционной системы
Я ни разу не встречал проблем с опакечиванием питоновых приложений хоть с PyPI, хоть напрямую с GitHub —
setup.py
делает всё что нужно и создаёт исполняемые файлы вbin
— это работает и c__main.py__
, и со скриптами.
Проблема, повторюсь, возникает при работе с репозиторием, потому что с
__main__.py
скриптов в нём не будет и чтобы пользоваться приложением из чекаута репозитория вам придётся, по сути, сначала руками распарситьentry_points
изsetup.py
, или искать__main__.py
.
ckpunT
16.06.2019 19:33И это прекрасно работает для одного проекта или как запуск некоторых модулей вполне себе хорошее решение:
`python -m venv`
`json.tool` — выше написали :)
А вот если это действительно приложение, да еще и не одно вращается на сервере или более того в контейнерах, то использование __main__.py или просто run.py даже вредит. Когда приходит OOM Killer, мы не видим какое конкретно приложение было «убито», а только python. Стараемся использовать шебанг в service_name.py или иное указываем в README.md.gecube
17.06.2019 00:38Очень спорный аргумент
В случае оом — лучше настроить эти питоновские скрипты как юниты системди и указать им политику рестарта...ckpunT
17.06.2019 07:01Способ реализации запуска/перезапуска приложения это инфраструктурный вопрос и легко решаем. Самостоятельно перезапущенное приложение показатель ненормальной работы сервиса и с этим надо разбираться. Представим ситуацию, что количество пользователей возросло, а мы используем по-прежнему сервер с 2ГБ ОЗУ. Мы получим ООМ периодически и systemd будет это отрабатывать. И в логах периодически будем видеть
[1395278.160214] python invoked oom-killer: gfp_mask=0xd0, order=0, oom_score_adj=996
Для одного-двух проектов это легко отыскать и исправить проблему. Если же больше, то иди-разберись какое из 30+ приложений упало. А если это еще приложение в kubernetes/swarm со скейлом 3 (мой вариант), то совсем не радостно становится искать логи упавшего сервиса. А еще надо как то в тикете описать что нужно править :)gecube
17.06.2019 07:17Вы несомненно правы, что искать по логам ООМкиллера не очень удобно. Но это очень похоже на борьбу не с причиной проблемы, а со следствием. Смотрите.
- В случае системд юнитов, почему я вообще о них заговорил, часть головной боли по задаче "понять, что отвалилось" перекладывается на системд. Ес-но, это не полное решение. Рестарт — это такая же затычка и Вам решать нужно ли его ставить (как будто в кубе приложения автоматически не перезапускаются в случае сбоя).
- Обвесить все мониторингом. Вообще все. Любое долгоживущие приложение должно быть под мониторингом. Упало? Получили Алерт и это увидели. С пакетными заданиями сложнее, но тоже реально. В этом кейсе становится сложнее понять, если отвалился внутренний компонент приложения, а не его основная часть, но тоже нужно смотреть по ситуации — наверняка есть решение.
- В идеальном мире — оом быть не должно. Разработчики должны ставить сайзинг на все свои решения. С небольшим, но запасом. И в лучшем случае ООМ никогда не будет, но ценой наличия незадействованных ОЗУ.
ckpunT
17.06.2019 08:37Надо было начать с того, что я не разработчик. Я устраняю следствие, а после пишу тикет с описанием проблемы и ссылкой на логи. Далее устраняют причины.
2. Не получится. На некоторых приложениях настроен kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale.
3. Запас есть, но он тоже ограничен.
Кубер при скейлинге так же может упереться в ограничение по количеству подов на рабочей ноде (110 штук, такое у нас пока не возможно) или в лимит ОЗУ прописанный в деплоях. Период опроса мониторинга 15 секунд и когда словишь «эффект домино», то несколько сервисов могут остановится в этом промежутке и на дашборде не будет видно в какой последовательности это произошло. Единственное место это логи ОС, но там раньше было по 2-4 записи подряд на подобии указанной мной в предыдущем комменте. Т.е. какой процесс был прибит ООМ, а какой был остановлен автоскейлером приходилось искать по тоннам логов потому, что stack trace в syslog указывал на python, а не конкретное приложение.gecube
17.06.2019 09:11С одной стороны, понимаю Вашу боль, с другой — повторюсь, что в одиночку эту проблему не решить, нужно кооперироваться с разработчиками, повышать культура кодирования.
- Не получится. На некоторых приложениях настроен kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale.
В условиях ограниченных ресурсов. Ну, а что — молодцы! К сожалению, не вижу Вашей инфры, поэтому не могу дать аргументированное мнение, но выглядит прям странно.
Период опроса мониторинга 15 секунд и когда словишь «эффект домино», то несколько сервисов могут остановится в этом промежутке и на дашборде не будет видно в какой последовательности это произошло
последовательность нужна для пост-мортема, а не для починить "прямо сейчас". Очень странная у Вас конфигурация, прям скажу, потому что кубер ведь должен мочь перезапускать поды. А если даже это не так — у Вас же мониторинг на прометеусе наверняка есть? Эластик есть, куда приложения логи отправляют?
Т.е. какой процесс был прибит ООМ, а какой был остановлен автоскейлером приходилось искать по тоннам логов потому, что stack trace в syslog указывал на python, а не конкретное приложение.
ну, и чем лог syslog'а поможет в случае, если отвалился не python процесс с pid=1, а какой-то дочерний? Вы просто сделали несколько слоев абстракций (k8s -> docker -> linux) и пытаетесь как будто использовать не тот инструмент исследования не для той задачи.
ckpunT
17.06.2019 11:37последовательность нужна для пост-мортема, а не для починить «прямо сейчас». Очень странная у Вас конфигурация, прям скажу, потому что кубер ведь должен мочь перезапускать поды.
пост-мортем используется для локализации проблемы со стороны разработчиков то же. Вроде я не писал про невозможность перезапуска подов? Кубер с этим очень хорошо справляется.
Я писал про цепочку падения микросервисов. Они восстанавливаются сразу же, но это не есть хорошо. По этому пишу пост-мортем, делаю тикет, туда всю инфу пишу и уже разработка разрабатывает :) Так что в одиночку я ничего не устраняю. У меня на это гораздо больше времени уйдет, чем у человека который писал этот микросервис.
ну, и чем лог syslog'а поможет в случае, если отвалился не python процесс с pid=1, а какой-то дочерний?
Jun 17 09:33:06 slave-1 kernel: [ pid ] uid tgid total_vm rss nr_ptes swapents oom_score_adj name
Jun 17 09:33:06 slave-1 kernel: [25793] 0 25793 394 62 4 0 0 http_server
Jun 17 09:33:06 slave-1 kernel: [25851] 0 25851 1911 1488 7 0 0 python
Jun 17 09:33:06 slave-1 kernel: Memory cgroup out of memory: Kill process 25851 (python) score 1459 or sacrifice child
видно какой сервис отвалился. Не просто python, а http_server
masai
17.06.2019 14:48Стараемся использовать шебанг в service_name.py или иное указываем в README.md.
Просто зарегистрируйте точку входа в
setup.py
и скрипт запуска с шебангом будет создан автоматически. А для Windows и вовсе exe-файл, если вдруг вам Windows нужен.
Mem0
17.06.2019 05:16Мне, как начинающему разрабочику на python, тяжело от таких статей. 7 разных мнений в комментариях, до конца непонятно — как и когда использовать __main__. Причём в официальной документации инфы об этом настолько мало, что просто страшно.
masai
17.06.2019 14:57+1Официальная документация рекомендует использовать
setuptools
для модулей, а те — регистрировать точки входа вsetup.py
. Инфы не мало, просто она неочевидным образом распределена. Если только начинаете разбираться, то посмотрите на официальный пример модуля. Там много комментариев, и охвачены разные аспекты: от пакетирования до тестирования.
__main__.py
имеет смысл использовать, если мы не делаем полноценный модуль, который будем устанавливать, а просто сделали модуль в виде папочки или zip-архива и так и запускаем.
metajiji
17.06.2019 05:16Простите, но разве это не сработает?
chmod +x ./myapp/__main__.py ./myapp/__main__.py
masai
17.06.2019 15:00+1Это плохой подход. Вот причины:
- нужно знать путь к модулю (а мало ли в какое окружение он будет установлен),
- нужно прописать shebang,
- это не работает в Windows, например.
Регистрация точек входа в
setup.py
и__main__.py
лишены этих недостатков.
kalininmr
что-то смысл немного ускользает.
зачем такое при использовании как пакета?
опять же есть __init__
за уши удалось примерную стратегию применения притянуть, но несколько сомнительна
germn Автор
Смотрите, вот есть у вас папка image_utils, в ней куча питоновских файлов, связанных с обработкой файлов. __init__.py делает папку модулем, который можно импортировать для других проектов.
Допустим, вы хотите этот модуль не только импортировать, но и при случае использовать из командной строки как-нибудь так:
Так команда не сработает, даже если положить if __name__ == '__main__' внутрь __init__.py. Напишет:
Как быть? Кладёте внутрь папки __main__.py, куда добавляете обработку командной строки и всё начинает работать. Бонусом, что не надо больше постоянно писать if __name__ == '__main__'
masai
Начиная с Python 3.3
__init__.py
для этого не нужен.Нужно каждый раз писать
python -m
, неудобно же. Удобнее и проще регистрировать скрипты.Я думаю, так мало людей использует
__main__.py
как раз из-за того, чтоsetup.py
представляет более удобный функционал.sergey-gornostaev
Никогда не использовали
python -m http.server
иpython -m compileall .
?vonabarak
python -m venv /path/to/venv
туда же. Очень удобно, имхо.gecube
Использовал, удобно.
masai
Использовал, конечно. А вы никогда не использовали
jupyter notebook
? По-моему, удобнее, чемpython -m jupyter notebook
.kalininmr
но тогда получается теперь нельзя запускать по человечески просто нужный файл.
без всякого этого -m и python
gecube
Ответьте на вопрос — а нужно ли это ???
Если очень хочется, ну, или сделайте симлинк на этот main.py из /usr/local/bin, или положите туда тупой двухстрочный шелл-скрипт, который за Вас сделает python -m module-name @$
kalininmr
симлинк на main при таком варианте ничем не поможет.
а шелл-скрипт — замечательное в своей оптимальности решение.
gecube
Ага, больше врапперов богу врапперов.
А Вы не подумали, что просто положив скрипт на питоне куда-либо — Вы убиваете всю возможность его версионировать. И доставлять через родное для питона окружение (pip install). Все равно по уму тогда нужно либо начать писать свои пакеты нативные для операционной системы, т.к. упаковывать python скрипты в deb, rpm — это вот все. И правильно прописывать зависимости от других пакетов (поверьте, это сложно). Либо использовать какие-то внешние способы доставки (а-ля ansible).
Касательно примеров вызова python -m против сразу вызов скрипта — смотрите ansible, сам pip, j2 из j2cli и пр. консольные утилиты, написанные на Питоне. Можете даже запилить сравнение, что они делают, чтобы быть доступными для пользователя по "простой" команде. И, да, в половине случаев, если не больше — Вы увидите враппер (может не на Шелл, но на питоне).
kalininmr
pip и пакетные менеджеры с удовольствием будут работать с нормальными питоновоскими скриптами.
а зачем тут вобще врапперы?
просто нужный файлик делаем исполняемым.
инерпретатор указываем env python (env python3)
и все замечательно запускается, версионифецируется
gecube
Нет. Неверно. Тот же pip — он устанавливается в систему по определенному пути (/usr/bin/pip), что не оставляет возможности иметь ДВА pip'а в системе. В случае с python3 выкрутились попросту устанавливая его в /usr/bin/pip3, но все равно при определенных обстоятельствах основной указывает не туда, куда надо. Более того — то, что по этому пути это не полноценный pip, а враппер:
о чем выше я и сообщил.
При использовании его через python3 -m pip — есть гарантия, что он всегда вызывается в нужном окружении.
masai
Вы ведь в курсе, что никто врапперы для питоновских модулей вручную не пишет, а они генерируются автоматически по записи в
entry_points
?andreymal
Прикол в том, что эти врапперы могут отнимать целых полсекунды на запуск из-за импорта модуля pkg_resources, поэтому я иногда пишу врапперы вручную на шелле, потому что они тупо быстрее)
andreymal
А не, я слегка облажался: долгий импорт pkg_resources появляется, если устанавливать модуль с опцией --editable, а без неё всё нормально. К несчастью, --editable мне очень часто нужен)
kalininmr
и по прежнему не понимаю, как мне просто проимпортировать пакет теперь.