Подтвердить что ты не робот

Как настроить __main__.py, __init__.py и __setup__.py для базовой установки пакета?

История:

У меня такая структура каталогов:

Package/
    setup.py
    src/
        __init__.py
        __main__.py 
        code.py

Я хочу иметь возможность запускать код по-разному.

  • pip install Package, а затем python, а затем from Package import *

  • python -m Package, который должен сделать вещь в __main__.py

  • python __main__.py, который также должен делать вещь в __main__.py, но на этот раз мы предположим, что вы загрузили источник, а не pip installing.

Теперь я получил первые два, но с грязной настройкой:

setup.py:

setup(
    name='Package',
    packages=['Package'],
    package_dir={'Package': 'src'},
    ...
    entry_points={ 'console_scripts': ['Package = src.__main__:main' ] }

__ __ INIT ру:.

from Package.code import .......

__ __ Основной р:.

from . import .......

Что для меня больше смысла было бы в обоих случаях писать

from code import ........

но это дает мне импортные ошибки.

Вопрос:

Есть ли у меня это единственный способ?

И самое главное, как я могу поддержать третий вариант использования? Прямо сейчас python __main__.py бросает

File "__main__.py", line 10, in <module>
    from . import code
ImportError: cannot import name 'class defined in code.py'

Примечания:

Я прочитал

4b9b3361

Ответ 1

У вас есть почти все, что вам нужно (даже немного больше)! Я бы пошел со следующей настройкой:

code.py

foo = 1

__ __ INIT ру:.

from .code import foo

Выполнение относительного импорта здесь, потому что __init__.py будет использоваться при импорте всего пакета. Обратите внимание, что мы явно отмечаем импорт как относительный с помощью . -syntax, потому что это требуется для Python 3 (и в Python 2, если вы сделали from __future__ import absolute_import).

__ __ Основной р:.

from Package import foo

print('foo = ', foo)

Это основной пакет script, поэтому мы используем абсолютный оператор import. Поступая таким образом, мы предполагаем, что пакет был установлен (или, по крайней мере, поставлен на путь); и именно так должны быть обработаны пакеты! Вы можете подумать, что это противоречит вашему третьему варианту использования, но на самом деле нет причин не pip install при работе с пакетом. И это действительно не очень важно (особенно при использовании virtualenv)!

Если вы беспокоитесь о том, чтобы возиться с исходными файлами и легко наблюдать за изменениями, запустив файл __main__.py, вы можете просто установить пакет с помощью переключателя -e ( "редактируемый" ): pip install -e . (предполагая, что вы находятся в каталоге Package). Однако с вашей текущей структурой каталогов это не будет работать, потому что переключатель -e поместит egg-link в каталог, содержащий файл setup.py; этот каталог не содержит пакет с именем Package, а src вместо этого (у меня есть вопрос об этом).

Вместо этого, если вы следуете за соглашением, чтобы назвать корневой каталог источника пакета после самого пакета (то есть Package для вашего примера), то установка с помощью -e не проблема: Python находит требуемый пакет Package в соответствующем каталоге:

$ tree Package/
Package/
├── setup.py
└── Package   <-- Renamed "src" to "Package" because that the package name.
    ├── code.py
    ├── __init__.py
    └── __main__.py

Это также позволяет опустить дополнительное определение package_dir={'Package': 'src'} в setup.py.

Заметка о setup.py. Для трех используемых вами случаев использования нет необходимости определять точку входа. То есть вы можете пропустить строку entry_points={ 'console_scripts': ['Package = src.__main__:main' ] }. При отправке модуля __main__.py python -m Package будет легко выполняться код в этом модуле. Вы также можете добавить дополнительное if-предложение:

def main():
    print('foo = ', foo)

if __name__ == '__main__':
    main()

Точка входа, с другой стороны, позволяет вам напрямую выполнить код в __main__.main из CLI; который работает $ Package, выполнит соответствующий код.

Резюме

Суть в том, что я всегда использовал pip install при работе с пакетами. И почему нет, особенно если вы уже создали файл setup.py? Если изменения в пакете должны применяться "в режиме реального времени", то вы можете установить с помощью коммутатора -e (для этого может потребоваться переименование папки src, см. Выше). Таким образом, ваш третий вариант использования будет читаться как "Загрузить исходный код и pip install (-e) Package (в пределах virtualenv), затем вы можете запустить python __main__.py".


Изменить

Запустите __main__.py без pip install

Если вы не хотите устанавливать пакет через pip, но все же можете запустить __main__.py script, я все равно поеду с указанной выше настройкой. Затем нам нужно убедиться, что оператор from Package import ... все еще выполняется, и это может быть достигнуто путем расширения пути импорта (обратите внимание, что для этого требуется, чтобы каталог src был переименован в имя пакета!).

Изменить PYTHONPATH

Для Linux bash вы можете установить Pythonpath следующим образом:

export PYTHONPATH=$PYTHONPATH:/path/to/Package

Или, если вы находитесь в том же каталоге, что и __main__.py:

export PYTHONPATH=$PYTHONPATH:`cd ..; pwd`

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

Расширить путь в __main__.py

Вы (или, вернее, ваш коллега) можете добавить следующие строки в начало script (перед операторами from Package import ...):

import sys
sys.path.append('/path/to/Package')

Расширьте путь в sitecustomize.py

Вы можете поместить модуль с именем sitecustomize.py в каталог lib/python3.5/site-packages/ вашей установки Python, который содержит следующие строки:

import sys
sys.path.append('/path/to/Package')

Ответ 2

from code import ......... терпит неудачу, потому что в вашей системе с именем code не установлен пакет Python. В вашей системе с именем code есть модуль Python, но в вашем заявлении на импорт вы не укажете пакет, в котором может быть найден ваш модуль code.

Цель файла __init__.py, который у вас есть в src/, указывает Python, что каталог src/ должен обрабатываться пакетом Python с его содержимым в качестве модулей внутри пакета. Поскольку code.py находится в src/ вместе с вашим файлом __init__.py, ваш модуль code находится в вашем пакете src.

Теперь, когда вы знаете, в каком пакете может быть найден ваш модуль code, вы можете импортировать его с помощью:

from src.code import .........

Кроме того, в качестве примечания: __init__.py выполняет свою работу, просто присутствуя в вашем каталоге src/, поэтому даже не нужно содержать какой-либо код. По этой причине обычно рекомендуется оставить файл __init__.py пустым.

Ответ 3

Я часто использую эту настройку, потому что она работает лучше с python setup.py develop

Package_root/
    setup.py
    src/
        Package/
            __init__.py
            __main__.py 
            code.py

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

setup( ...
    package_dir = {'': 'src'},
    entry_points = {'console_scripts': ['Package = Package.__main__:main'],},
    packages = find_packages(exclude=["Package.egg_info",]),
...)