Как структурировать проект Python, чтобы разрешить импорт именованных модулей из подкаталогов
это моя структура каталогов:
Projects
+ Project_1
+ Project_2
- Project_3
- Lib1
__init__.py # empty
moduleA.py
- Tests
__init__.py # empty
foo_tests.py
bar_tests.py
setpath.py
__init__.py # empty
foo.py
bar.py
цели:
- иметь организованную структуру проекта
- иметь возможность самостоятельно запускать каждый .py файл при необходимости
- иметь возможность ссылаться / импортировать как брата, так и Кузина модули
- сохранить все инструкции import / from в начале каждого файла.
Я достиг #1, используя вышеуказанную структуру
Я в основном достигнуто 2, 3 и 4, выполнив следующие действия (как рекомендовано это отличное руководство)
в любом пакете, который должен получить доступ к родительским или двоюродным модулям (например, каталог тестов выше), я включаю файл с именем setpath.py который имеет следующий код:
import os
import sys
sys.path.insert(0, os.path.abspath('..'))
sys.path.insert(0, os.path.abspath('.'))
sys.path.insert(0, os.path.abspath('...'))
затем в каждом модуле, который нуждается в Родительском / двоюродном доступе, например foo_tests.py, я могу написать хороший чистый список импорта, например:
import setpath # Annoyingly, PyCharm warns me that this is an unused import statement
import foo.py
внутри setpath.py, второй и третий вставок не являются строго необходимыми для этого примера, но включены для устранения неполадок.
моя проблема в том, что это работает только для импорта, который ссылается на имя модуля напрямую, а не для импорта, который ссылается на пакет. Например, внутри bar_tests.py, ни один из двух операторов ниже не работает при запуске bar_tests.py напрямую.
import setpath
import Project_3.foo.py # Error
from Project_3 import foo # Error
Я получаю сообщение об ошибке "ImportError: нет модуля с именем "Project_3"".
странно то, что я могу запустить файл непосредственно из PyCharm, и он работает нормально. Я знаю, что PyCharm делает некоторые закулисные магии с Python Path
переменная, чтобы все работало, но я не могу понять, что это такое. Поскольку PyCharm просто запускает python.exe и устанавливает некоторые переменные среды, должно быть возможно клонировать это поведение из самого скрипта Python.
по причинам, которые на самом деле не относятся к этому вопросу, я должен ссылаться bar
С помощью Project_3
квалификатор.
Я открыт для любого решения, которое выполняет вышеуказанное, все еще выполняя мои предыдущие цели. Я также открыт для альтернативной структуры каталогов, если она работает лучше. Я читал Python doc по импорту и пакеты, но я все еще в недоумении. Я думаю, что один из возможных путей может вручную установить __path__
переменная, но я не уверен, какой из них нужно изменить или что его установить.
2 ответов
эти типы вопросов квалифицируются как" в первую очередь основанные на мнении", поэтому позвольте мне поделиться своим мнением о том, как я это сделаю.
первый "сможет самостоятельно выполнить каждый .пы файл в случае необходимости": файл представляет собой модуль, поэтому он не должен быть вызван непосредственно, или это исполняемый файл, то он должен импортировать зависимостей, начиная от верхнего уровня (вы можете избежать этого в коде, точнее перенести ее в общие места, с помощью setup.py entry_points, но тогда ваш бывший исполняемый эффективно преобразуется в модуль). И да, это одна из слабых сторон модели модулей Python, которая вызывает недоразумения.
во-вторых, используйте virtualenv (или venv в Python3) и поместите каждый из ваших Project_x в отдельный. Таким образом, имя проекта не будет частью пути модуля Python.
В-третьих, ссылка, которую вы предоставили упоминания setup.py -вы можете использовать его. Поместите свой пользовательский код в Project_x/src / mylib1, создайте src/mylib1/setup.py и, наконец, ваши модули в src/mylib1/mylib1/module.py - ... Затем вы можете установить свой код pip как любой другой пакет (или pip-e, чтобы вы могли работать с кодом напрямую, не переустанавливая его, Хотя, к сожалению, у него есть некоторые ограничения).
и, наконец, как вы уже подтвердили в комментарии ;). Проблема с вашей текущей моделью заключалась в том, что в sys.path.insert(0, os.path.abspath('...'))
вы ошибочно использовали обозначение модуля Python, которое неверно для системных путей и должно быть заменено на '../..'
работать, как ожидалось.
я думаю, что ваши цели не являются разумными. В частности, цель номер 2 проблемы:
- смогите независимо побежать каждое .py файл при необходимости
это не работает хорошо для модулей в упаковке. По крайней мере, если вы используете .py
файлы наивно (например,python foo_tests.py
в командной строке). Когда вы запускаете файлы таким образом, Python не может сказать, где должна начинаться иерархия пакетов.
есть две альтернативы это может сработать. Первый вариант-запустить скрипты из папки верхнего уровня (например,projects
) С помощью -m
флаг интерпретатору, чтобы дать ему пунктирный путь к основному модулю и использовать явный относительный импорт для получения модулей sibling и cousin. Так что вместо бега python foo_tests.py
непосредственно, run python -m project_3.tests.foo_tests
С (или python -m tests.foo_tests
внутри project_3
возможно), а у нас есть foo_tests.py
использовать from .. import foo
.
другой (менее хороший) вариант-Добавить папку верхнего уровня к пути поиска модуля вашей установки Python на общесистемной основе (например, добавьте до PYTHON_PATH
переменная окружения), а затем использовать абсолютный импорт для всех ваших модулей (например,import project3.foo
). Это эффективно то, что ваш setpath
модуль делает, но делает его общесистемным как часть конфигурации вашей системы, а не во время выполнения, он намного чище. Он также избегает нескольких имен, которые setpath
позволит вам использовать для импорта модуля (например, try import foo_tests, tests.foo_tests
и вы получите два отдельные копии того же модуля).