Я вызову . корневую папку, и в моем примере она находится по адресу C:\tmp\test_imports\.
api.py
В качестве тестового примера давайте воспользуемся следующим ./api/api.py
deffunction_from_api(): return'I am the return value from api.api!'
test_one.py
from api.api import function_from_api
deftest_function(): print(function_from_api())
if __name__ == '__main__': test_function()
Попробуйте запустить test_one:
PS C:\tmp\test_imports> python .\myproject\tests\test_one.py Traceback (most recent call last): File ".\myproject\tests\test_one.py", line 1, in <module> from api.api import function_from_api ModuleNotFoundError: No module named 'api'
Также попытка относительного импорта не сработает:
Использование from ..api.api import function_from_api приведет к
PS C:\tmp\test_imports> python .\myproject\tests\test_one.py Traceback (most recent call last): File ".\tests\test_one.py", line 1, in <module> from ..api.api import function_from_api ValueError: attempted relative import beyond top-level package
Шаги
1) Создайте файл pyproject.toml в каталог корневого уровня
(ранее люди использовали файл setup.py)
Содержимое для минимального pyproject.toml будет*
[project] name = "myproject" version = "0.1.0" description = "My small project"
Если вы знакомы с виртуальными средами, активируйте одну из них и переходите к следующему шагу. Использование виртуальных сред не абсолютно обязательно, но они действительно помогут вам в долгосрочной перспективе (когда у вас выполняется более 1 проекта ..). Самые простые шаги (запускаются в корневой папке)
Создание виртуального env
python -m venv venv
Активировать виртуальную среду env
source ./venv/bin/activate (Linux, macOS) или ./venv/Scripts/activate (Win)
Чтобы узнать больше об этом, просто найдите в Google "python virtual env tutorial" или аналогичный. Вероятно, вам никогда не понадобятся никакие другие команды, кроме создания, активации и деактивации.
После создания и активации виртуальной среды в вашей консоли должно быть указано имя виртуальной среды в круглых скобках
3) pip установит ваш проект в редактируемом состоянии
Установите свой пакет верхнего уровня myproject с помощью pip. Хитрость заключается в использовании -e флага при выполнении установки. Таким образом, он устанавливается в состоянии, доступном для редактирования, и все изменения, внесенные в файлы .py, будут автоматически включены в установленный пакет. Для использования pyproject.toml и флага -e требуется значение pip >= 21.3
В корневом каталоге запустите
pip install -e . (обратите внимание на точку, она обозначает "текущий каталог")
Вы также можете увидеть, что он установлен с помощью pip freeze
Obtaining file:///home/user/projects/myproject Installing build dependencies ... done Checking if build backend supports build_editable ... done Getting requirements to build editable ... done Preparing editable metadata (pyproject.toml) ... done Building wheels for collected packages: myproj Building editable for myproj (pyproject.toml) ... done Created wheel for myproj: filename=myproj-0.1.0-py2.py3-none-any.whl size=903 sha256=f19858b080d4e770c2a172b9a73afcad5f33f4c43c86e8eb9bdacbe50a627064 Stored in directory: /tmp/pip-ephem-wheel-cache-qohzx1u0/wheels/55/5f/e4/507fdeb40cdef333e3e0a8c50c740a430b8ce84cbe17ae5875 Successfully built myproject Installing collected packages: myproject Successfully installed myproject-0.1.0 (venv) PS C:\tmp\test_imports> pip freeze myproject==0.1.0
4) Добавьте myproject. в свой импорт
Обратите внимание, что вам придется добавлять myproject. только в импорт, который иначе не работал бы. Импорт, который работал без pyproject.toml & pip install, все равно будет работать нормально. Смотрите пример ниже.
Протестируйте решение
Теперь давайте протестируем решение, используя api.py определенное выше и test_one.py определенное ниже.
test_one.py
from myproject.api.api import function_from_api
deftest_function(): print(function_from_api())
if __name__ == '__main__': test_function()
запуск теста
(venv) PS C:\tmp\test_imports> python .\myproject\tests\test_one.py I am the return value from api.api!
* здесь в качестве серверной части сборки используется flit. Существуют и другие альтернативы.
** На самом деле вы можете разместить свою виртуальную среду в любом месте вашего жесткого диска.
Ответ 2
Семь лет спустя
С тех пор, как я написал ответ ниже, модификация sys.path по-прежнему остается быстрым и грязным приемом, который хорошо работает для частных скриптов, но было внесено несколько улучшений
Установка пакета (в virtualenv или нет) даст вам то, что вы хотите, хотя я бы предложил использовать для этого pip, а не использовать setuptools напрямую (и использовать setup.cfg для хранения метаданных)
Использование -m флага и запуск как пакета тоже работает (но будет немного неудобно, если вы захотите преобразовать свой рабочий каталог в устанавливаемый пакет).
Что касается тестов, то, в частности, pytest может найти пакет api в этой ситуации и позаботится о sys.path взломах за вас
Так что это действительно зависит от того, что вы хотите сделать. Однако в вашем случае, поскольку кажется, что ваша цель - в какой-то момент создать правильный пакет, установка через pip -e, вероятно, ваш лучший выбор, даже если он еще не идеален.
Старый ответ
Как уже говорилось в другом месте, ужасная правда заключается в том, что вам приходится выполнять уродливые взломы, чтобы разрешить импорт из родственных модулей или родительского пакета из __main__ модуля. Проблема подробно описана в PEP 366. PEP 3122 попытался обработать импорт более рациональным способом, но Guido отклонил его из-за
Похоже, единственным вариантом использования является запуск скриптов, которые находятся внутри каталога модуля, который я всегда рассматривал как антипаттерн.
# Ugly hack to allow absolute import from the root folder # whatever its name is. Please forgive the heresy. if __name__ == "__main__"and __package__ isNone: from sys import path from os.path import dirname asdir
Здесь path[0] находится родительская папка вашего запущенного скрипта и dir(path[0]) ваша папка верхнего уровня.
Я до сих пор не смог использовать относительный импорт с этим, но он допускает абсолютный импорт с верхнего уровня (в родительской папке вашего примера api).
Ответ 3
Вот еще одна альтернатива, которую я вставляю поверх файлов Python в tests папке:
# Path hack. import sys, os sys.path.insert(0, os.path.abspath('..'))
Ответ 4
Вам не нужно и не следует взламыватьsys.path, если только это не необходимо, а в данном случае это не так. Используйте:
import api.api_key # in tests, examples
Запуск из каталога проекта: python -m tests.test_one.
Вероятно, вам следует переместить tests (если это unittests api) внутрь api и запустить python -m api.test, чтобы выполнить все тесты (при условии, что они есть __main__.py) или python -m api.test.test_one запустить test_one вместо этого.
Вы также можете удалить __init__.py из examples (это не пакет Python) и запустить примеры в virtualenv, где api установлен, например, pip install -e . в virtualenv будет установлен пакет inplace api, если у вас есть соответствующий setup.py.