Как настроить main.py, init.py, и setup.py для базовой настройки пакета?

Справочная информация:

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

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

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

  1. pip install Package а то python а то from Package import *

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

  3. 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__.py:

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

__main__.py:

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'

Примечания:

я прочитала

3 ответов


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

code.py:

foo = 1

__init__.py:

from .code import foo

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

__main__.py:

from Package import foo

print('foo = ', foo)

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

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

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

$ tree Package/
Package/
├── setup.py
└── Package   <-- Renamed "src" to "Package" because that's the package's 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; то есть бег!--36--> выполнит соответствующий код.

резюме

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


редактировать

Run __main__.py без pip install

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

изменить PYTHONPATH

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

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

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

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

конечно есть разные способы для разных операционных систем.

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

вы (или, скорее, ваш коллега) можете добавить следующие строки в верхнюю часть скрипта (перед 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')

создайте отдельный, верхний уровень main.py скрипт

таким образом, у вас будет следующий макет:

$ tree Package/
Package/
├── main.py   <-- Add this file.
├── setup.py
└── src
    ├── code.py
    ├── __init__.py
    └── __main__.py

здесь main.py содержит

import src.__main__

теперь __main__.py рассматривается как часть the src пакет и относительный импорт будут работать. Вместо python src/__main__.py вы будет работать python main.py сейчас.


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

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

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

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

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


Я часто использую эту установку, потому что она лучше работает с 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",]),
...)