Зачем нужен __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)


  1. kalininmr
    16.06.2019 01:37
    +1

    что-то смысл немного ускользает.
    зачем такое при использовании как пакета?
    опять же есть __init__
    за уши удалось примерную стратегию применения притянуть, но несколько сомнительна


    1. germn Автор
      16.06.2019 01:52

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

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

      python -m image_utils -convert=png someimage.jpg


      Так команда не сработает, даже если положить if __name__ == '__main__' внутрь __init__.py. Напишет:

      No module named image_utils.__main__; 'image_utils' is a package and cannot be directly executed

      Как быть? Кладёте внутрь папки __main__.py, куда добавляете обработку командной строки и всё начинает работать. Бонусом, что не надо больше постоянно писать if __name__ == '__main__'


      1. masai
        16.06.2019 02:09
        +2

        init.py делает папку модулем, который можно импортировать для других проектов.

        Начиная с Python 3.3 __init__.py для этого не нужен.


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

        Нужно каждый раз писать python -m, неудобно же. Удобнее и проще регистрировать скрипты.


        Я думаю, так мало людей использует __main__.py как раз из-за того, что setup.py представляет более удобный функционал.


        1. sergey-gornostaev
          16.06.2019 09:55

          Никогда не использовали python -m http.server и python -m compileall .?


          1. vonabarak
            16.06.2019 20:18
            +1

            python -m venv /path/to/venv туда же. Очень удобно, имхо.


          1. gecube
            17.06.2019 00:44

            Использовал, удобно.


          1. masai
            17.06.2019 15:09

            Никогда не использовали python -m http.server и python -m compileall .?

            Использовал, конечно. А вы никогда не использовали jupyter notebook? По-моему, удобнее, чем python -m jupyter notebook.


      1. kalininmr
        16.06.2019 11:33

        но тогда получается теперь нельзя запускать по человечески просто нужный файл.
        без всякого этого -m и python


        1. gecube
          17.06.2019 07:18

          Ответьте на вопрос — а нужно ли это ???
          Если очень хочется, ну, или сделайте симлинк на этот main.py из /usr/local/bin, или положите туда тупой двухстрочный шелл-скрипт, который за Вас сделает python -m module-name @$


          1. kalininmr
            17.06.2019 07:40

            симлинк на main при таком варианте ничем не поможет.
            а шелл-скрипт — замечательное в своей оптимальности решение.


            1. gecube
              17.06.2019 07:56

              Ага, больше врапперов богу врапперов.
              А Вы не подумали, что просто положив скрипт на питоне куда-либо — Вы убиваете всю возможность его версионировать. И доставлять через родное для питона окружение (pip install). Все равно по уму тогда нужно либо начать писать свои пакеты нативные для операционной системы, т.к. упаковывать python скрипты в deb, rpm — это вот все. И правильно прописывать зависимости от других пакетов (поверьте, это сложно). Либо использовать какие-то внешние способы доставки (а-ля ansible).


              Касательно примеров вызова python -m против сразу вызов скрипта — смотрите ansible, сам pip, j2 из j2cli и пр. консольные утилиты, написанные на Питоне. Можете даже запилить сравнение, что они делают, чтобы быть доступными для пользователя по "простой" команде. И, да, в половине случаев, если не больше — Вы увидите враппер (может не на Шелл, но на питоне).


              1. kalininmr
                17.06.2019 09:14

                pip и пакетные менеджеры с удовольствием будут работать с нормальными питоновоскими скриптами.
                а зачем тут вобще врапперы?
                просто нужный файлик делаем исполняемым.
                инерпретатор указываем env python (env python3)
                и все замечательно запускается, версионифецируется


                1. gecube
                  17.06.2019 09:21

                  Нет. Неверно. Тот же pip — он устанавливается в систему по определенному пути (/usr/bin/pip), что не оставляет возможности иметь ДВА pip'а в системе. В случае с python3 выкрутились попросту устанавливая его в /usr/bin/pip3, но все равно при определенных обстоятельствах основной указывает не туда, куда надо. Более того — то, что по этому пути это не полноценный pip, а враппер:


                  #!/usr/bin/python3
                  # EASY-INSTALL-ENTRY-SCRIPT: 'pip==19.1.1','console_scripts','pip3.7'
                  __requires__ = 'pip==19.1.1'
                  import re
                  import sys
                  from pkg_resources import load_entry_point
                  
                  if __name__ == '__main__':
                      sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
                      sys.exit(
                          load_entry_point('pip==19.1.1', 'console_scripts', 'pip3.7')()
                      )

                  о чем выше я и сообщил.


                  При использовании его через python3 -m pip — есть гарантия, что он всегда вызывается в нужном окружении.


              1. masai
                17.06.2019 15:12

                И, да, в половине случаев, если не больше — Вы увидите враппер (может не на Шелл, но на питоне).

                Вы ведь в курсе, что никто врапперы для питоновских модулей вручную не пишет, а они генерируются автоматически по записи в entry_points?


                1. andreymal
                  17.06.2019 15:20

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


                  1. andreymal
                    17.06.2019 15:35

                    А не, я слегка облажался: долгий импорт pkg_resources появляется, если устанавливать модуль с опцией --editable, а без неё всё нормально. К несчастью, --editable мне очень часто нужен)


      1. kalininmr
        16.06.2019 15:33

        и по прежнему не понимаю, как мне просто проимпортировать пакет теперь.


  1. Neqit
    16.06.2019 01:52
    +1

    Очень просто, а главное полезно, спасибо


  1. masai
    16.06.2019 02:03
    +1

    Гораздо удобнее прописать в setup.py все точки входа. И тогда не нужно запускать интерпретатор с ключом -m, так как все точки входа будут обычными консольными командами. if __name__ == '__main__' в этом случае тоже не нужен.


    1. gecube
      17.06.2019 00:44
      +1

      Ага, больше логики в setup.py.
      Я лично считаю упомянутую Вами практику порочной. Не вижу бенефитов от точек входа в setup.py, кроме путаницы, если нужно установить один модуль в два и более интерпретатора.


      1. 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.


        1. gecube
          17.06.2019 14:59

          Я про эту штуку выше и писал — про врапперы. По ходу они pip'ом сами формируются и устанавливаются. ОК. Не самый плохой вариант. Уж всяко лучше, чем свои скрипты закидывать в /usr/local/bin...


  1. eirnym
    16.06.2019 10:18

    Этот файл придётся исключать из coverage при тестировании, ведь импорт модуля __main__.py приведёт к запуску программы. Это приведёт к тому, что в __main__.py будет очень простой код, например код ниже, чтобы этот код не требовал покрытия тестами. В итоге, мы вернёмся к if __name__ == '__main__'


    import run
    run.main()


    1. MechanicZelenyy
      16.06.2019 12:42
      +1

      Да, это так, вы же в __init__.py бизнес логику не прописываете?


      1. eirnym
        16.06.2019 12:45
        +1

        Видел очень много проектов, в которых в __init__.py написано очень много логики.


        1. MechanicZelenyy
          16.06.2019 13:19
          +2

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


        1. gecube
          17.06.2019 00:43
          +1

          И что? Миллион мух не может ошибаться? То что все говнячат — это, во-первых, не означает, что это нормально, а, во-вторых, отвечайте сами за свой продукт.
          В третьих, наличие всякой хитрой логики, что в setup.py, что в init.py — за это надо руки отрывать


    1. VovanZ
      16.06.2019 14:15

      импорт модуля main.py приведёт к запуску программы

      Нет, если в __main__.py точно так же запускать программу внутри if __name__ == '__main__'


      1. eirnym
        16.06.2019 14:32
        +1

        если называние модуля __main__.py, то ваша проверка лишена смысла: модуль и так будет всегда называться __main__ (по названию файла), как бы вы его не запускали или импортировали.


        1. avalak
          16.06.2019 20:03
          +1

          Всё там нормально будет.
          Попробуйте


          1. eirnym
            18.06.2019 00:47

            Спасибо, век живи — век учись


        1. Norrius
          17.06.2019 05:14

          Проверка пройдёт только при запуске напрямую (python my_package/__main__.py) или через -m (python -m my_package). Если __main__.py импортируется из другого модуля, его __name__ будет my_package.__main__.


  1. 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


    1. andreymal
      17.06.2019 00:34

      И потом получать геморрой с публикацией всех этих скриптов на PyPI?


      1. AMDmi3
        17.06.2019 17:07

        Никакого геморроя. Много кто так и делает, см., например, ansible и black.


        1. 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',
              ],
              # ...
          )


    1. gecube
      17.06.2019 00:41

      Оформлять приложения в виде модулей это наиболее правильная идея, которая может возникнуть в рамках инфраструктуры и экосистемы питона. Типичный пример. Мне часто приходится работать с несколькими версиями питона на одной машине. И речь не про 2 vs 3, а про версии даже внутри одной линейки. Если скрипт запускать как исполняемый файл, то велика вероятность, что он запуститься не тем интерпретатором. В случае вызова python3.6 -m имя_модуля такой двусмысленности нет. И более того — если интерпретатор вырубается на отсутствующий модуль, то будет понятно, что делать.


      1. AMDmi3
        17.06.2019 17:09

        Скрипт можно запускать точно так же, причём на 1 аргумент короче.


        1. gecube
          17.06.2019 19:08

          Сомнительное преимущество. Здесь скорее дело в том, как "скрипт" установлен — как отдельно стоящий скрипт в /usr/bin и аналогах или как полноценный питон модуль.
          В любом случае вопрос ещё в том, как грамотно обернуть питон-модуль — или в pypi, или в нативный пакет для операционной системы. Меня, честно говоря, очень расстраивает, когда setup.py приносит в систему то, что не ожидается изначально. Примеров масса.


          1. masai
            17.06.2019 19:49

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


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


            1. gecube
              17.06.2019 20:06

              Ну, чего все такие категоричные? Я вообще ума приложить не могу.
              Часть вещей — действительно имеет смысл ставить в venv. Даже тот же хваленый airflow. Ansible — тоже скорее да, чем нет. Пользоваться, конечно, становится неудобно, но для сервисов это не проблема — обернул в сервис и понеслась. Часть вещей — не имеет смысла ставить в venv. Вообще — это какие-то общесистемные вещи, например, пакет для управления docker. Или еще что-то подобное. Ну, и опять же — мой тезис, что setup.py в теории может любую дрянь притащить в систему. Мне кажется, что не нужно пытаться противопоставлять средства нативной доставки приложений (deb/rpm в первую очередь) и pip, а все-таки пытаться их дружить. Благо в первых все-таки есть средства рекурсивной валидации зависимостей — для Питон-экосистемы это прям больное место.


              1. masai
                18.06.2019 02:14

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


                Насчёт дряни — это к мейнтейнеру. Скрипты deb-пакетов тоже могут дел натворить. Или вы плавно ведёте к экзотике вроде Nix/Guix?


            1. AMDmi3
              17.06.2019 23:11

              Ставить как нативные для системы особого смысла нет.

              Не будь смысла, опакечивание Python модулей не было бы распространено повсеместно: https://repology.org/projects/?search=python%3A


              Лишняя возня со сборкой, а премуществ никаких особо и нет.

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


              1. masai
                18.06.2019 02:11

                Не будь смысла, опакечивание Python модулей не было бы распространено повсеместно:

                Я, возможно, потерял нить разговора, но речь шла о собственных проектах. Не берусь судить о всех, но лично мне проще устанавливать пакеты через pip прямо из репозитория git, чем поддерживать пакеты и держать репозитории для разных операционных систем. Чтоб не компилировать каждый раз бинарные модули, можно держать один репозиторий с wheel-файлами. Это удобнее, если, например, разработка ведётся под MacOS, на серверах Ubuntu, а в докере вообще какой-нибудь Alpine.


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

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


                У меня же сложилось такое мнение (не обязательно верное). Если пакет зависит от каких-то нативных библиотек, то ничто не мешает проверять наличие этих библиотек при компиляции и установке. Или вовсе помещать их в wheel-файл. Пакеты под линукс следует собирать не на коленке, а в официальном докере manylinux1, что даст какую-то уверенность в работоспособности. Мне кажется, что перечисленные проблемы — это не проблема способа дистрибуции, а проблема мейнтейнера. Вот, тот же numpy. Отлично ставится через pip, хотя насквозь бинарный.


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


                В общем, всё средства хороши на своём месте.


                1. gecube
                  18.06.2019 08:42

                  Мы тоже pip'ом ставим из гита. Это действительно удобно и способствует быстрой разработке.
                  Но нормальные пакеты (в первую очередь deb/rpm, во вторую pypi/wheel) становятся жизненно необходимы, если этими модулями будут пользоваться другие люди. Условно — решил проблему — поделись с сообществом. Ничего там сверхсекретного в твоей разработке скорее всего нет. Тот же ML — там ноу-хау в моделях (т.е. весовых коэффициентах), а не во фреймворках обучения.


                  Касательно дружить — да, все верно, что setup py ничего не знает о системе, куда будет установлен. Скажем, Вам нужна библиотека для работы с БД Postgres. В разных дистрибутивах она может быть разной версии, лежать по разным путям и устанавливаться из пакетов с разными именами. Унификации здесь нет и в ближайшее время не предвидится. В результате ставя питон-пакет из официального репозитория ОС — эти зависимости подтягиваются, т.к. мейнтейнеры их описали и гарантируют их. А ставя через pip — либо как повезет, либо там будет дичь в setup.py.


                  Насчёт дичи в deb/rpm — да, она тоже возможна, но мейнтейнеры официальных репозиториев такое не пропустят, т.е. проблема возможна только со сторонними репами


          1. AMDmi3
            17.06.2019 22:58

            или в pypi, или в нативный пакет для операционной системы

            Я ни разу не встречал проблем с опакечиванием питоновых приложений хоть с PyPI, хоть напрямую с GitHub — setup.py делает всё что нужно и создаёт исполняемые файлы в bin — это работает и c __main.py__, и со скриптами.


            Проблема, повторюсь, возникает при работе с репозиторием, потому что с __main__.py скриптов в нём не будет и чтобы пользоваться приложением из чекаута репозитория вам придётся, по сути, сначала руками распарсить entry_points из setup.py, или искать __main__.py.


  1. ckpunT
    16.06.2019 19:33

    И это прекрасно работает для одного проекта или как запуск некоторых модулей вполне себе хорошее решение:
    `python -m venv`
    `json.tool` — выше написали :)
    А вот если это действительно приложение, да еще и не одно вращается на сервере или более того в контейнерах, то использование __main__.py или просто run.py даже вредит. Когда приходит OOM Killer, мы не видим какое конкретно приложение было «убито», а только python. Стараемся использовать шебанг в service_name.py или иное указываем в README.md.


    1. gecube
      17.06.2019 00:38

      Очень спорный аргумент
      В случае оом — лучше настроить эти питоновские скрипты как юниты системди и указать им политику рестарта...


      1. 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 (мой вариант), то совсем не радостно становится искать логи упавшего сервиса. А еще надо как то в тикете описать что нужно править :)


        1. gecube
          17.06.2019 07:17

          Вы несомненно правы, что искать по логам ООМкиллера не очень удобно. Но это очень похоже на борьбу не с причиной проблемы, а со следствием. Смотрите.


          1. В случае системд юнитов, почему я вообще о них заговорил, часть головной боли по задаче "понять, что отвалилось" перекладывается на системд. Ес-но, это не полное решение. Рестарт — это такая же затычка и Вам решать нужно ли его ставить (как будто в кубе приложения автоматически не перезапускаются в случае сбоя).
          2. Обвесить все мониторингом. Вообще все. Любое долгоживущие приложение должно быть под мониторингом. Упало? Получили Алерт и это увидели. С пакетными заданиями сложнее, но тоже реально. В этом кейсе становится сложнее понять, если отвалился внутренний компонент приложения, а не его основная часть, но тоже нужно смотреть по ситуации — наверняка есть решение.
          3. В идеальном мире — оом быть не должно. Разработчики должны ставить сайзинг на все свои решения. С небольшим, но запасом. И в лучшем случае ООМ никогда не будет, но ценой наличия незадействованных ОЗУ.


          1. 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, а не конкретное приложение.


            1. gecube
              17.06.2019 09:11

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


              1. Не получится. На некоторых приложениях настроен kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale.

              В условиях ограниченных ресурсов. Ну, а что — молодцы! К сожалению, не вижу Вашей инфры, поэтому не могу дать аргументированное мнение, но выглядит прям странно.


              Период опроса мониторинга 15 секунд и когда словишь «эффект домино», то несколько сервисов могут остановится в этом промежутке и на дашборде не будет видно в какой последовательности это произошло

              последовательность нужна для пост-мортема, а не для починить "прямо сейчас". Очень странная у Вас конфигурация, прям скажу, потому что кубер ведь должен мочь перезапускать поды. А если даже это не так — у Вас же мониторинг на прометеусе наверняка есть? Эластик есть, куда приложения логи отправляют?


              Т.е. какой процесс был прибит ООМ, а какой был остановлен автоскейлером приходилось искать по тоннам логов потому, что stack trace в syslog указывал на python, а не конкретное приложение.

              ну, и чем лог syslog'а поможет в случае, если отвалился не python процесс с pid=1, а какой-то дочерний? Вы просто сделали несколько слоев абстракций (k8s -> docker -> linux) и пытаетесь как будто использовать не тот инструмент исследования не для той задачи.


              1. 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


    1. masai
      17.06.2019 14:48

      Стараемся использовать шебанг в service_name.py или иное указываем в README.md.

      Просто зарегистрируйте точку входа в setup.pyи скрипт запуска с шебангом будет создан автоматически. А для Windows и вовсе exe-файл, если вдруг вам Windows нужен.


  1. Mem0
    17.06.2019 05:16

    Мне, как начинающему разрабочику на python, тяжело от таких статей. 7 разных мнений в комментариях, до конца непонятно — как и когда использовать __main__. Причём в официальной документации инфы об этом настолько мало, что просто страшно.


    1. masai
      17.06.2019 14:57
      +1

      Официальная документация рекомендует использовать setuptools для модулей, а те — регистрировать точки входа в setup.py. Инфы не мало, просто она неочевидным образом распределена. Если только начинаете разбираться, то посмотрите на официальный пример модуля. Там много комментариев, и охвачены разные аспекты: от пакетирования до тестирования.


      __main__.py имеет смысл использовать, если мы не делаем полноценный модуль, который будем устанавливать, а просто сделали модуль в виде папочки или zip-архива и так и запускаем.


  1. metajiji
    17.06.2019 05:16

    Простите, но разве это не сработает?

    chmod +x ./myapp/__main__.py
    ./myapp/__main__.py
    


    1. masai
      17.06.2019 15:00
      +1

      Это плохой подход. Вот причины:


      • нужно знать путь к модулю (а мало ли в какое окружение он будет установлен),
      • нужно прописать shebang,
      • это не работает в Windows, например.

      Регистрация точек входа в setup.py и __main__.py лишены этих недостатков.