Как настроить 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__.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'
Примечания:
я прочитала
- https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/
- http://setuptools.readthedocs.io/en/latest/setuptools.html
- многие вопросы здесь, которые выглядят как но не ответили на мой вопрос выше.
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",]),
...)