Вопрос
Стандартная библиотека явно документирует как напрямую импортировать исходные файлы (учитывая абсолютный путь к исходному файлу), но этот подход делает не работают, если этот исходный файл использует неявный импорт брата, как описано в примере ниже.
Как этот пример можно адаптировать для работы при наличии имплицитных импортных товаров?
Я уже проверил этот и этот другой Вопрос о Stackoverflow по этой теме, но они не адресуют неявный импорт брака в файл, импортируемый вручную.
Настройка/Пример
Здесь иллюстративный пример
Структура каталогов:
root/
- directory/
- app.py
- folder/
- implicit_sibling_import.py
- lib.py
app.py
:
import os
import importlib.util
# construct absolute paths
root = os.path.abspath(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
isi_path = os.path.join(root, 'folder', 'implicit_sibling_import.py')
def path_import(absolute_path):
'''implementation taken from https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly'''
spec = importlib.util.spec_from_file_location(absolute_path, absolute_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
isi = path_import(isi_path)
print(isi.hello_wrapper())
lib.py
:
def hello():
return 'world'
implicit_sibling_import.py
:
import lib # this is the implicit sibling import. grabs root/folder/lib.py
def hello_wrapper():
return "ISI says: " + lib.hello()
#if __name__ == '__main__':
# print(hello_wrapper())
Запуск python folder/implicit_sibling_import.py
с блоком if __name__ == '__main__':
закомментировал выходы ISI says: world
в Python 3.6.
Но запуск python directory/app.py
дает:
Traceback (most recent call last):
File "directory/app.py", line 10, in <module>
spec.loader.exec_module(module)
File "<frozen importlib._bootstrap_external>", line 678, in exec_module
File "<frozen importlib._bootstrap>", line 205, in _call_with_frames_removed
File "/Users/pedro/test/folder/implicit_sibling_import.py", line 1, in <module>
import lib
ModuleNotFoundError: No module named 'lib'
Обход
Если я добавлю import sys; sys.path.insert(0, os.path.dirname(isi_path))
в app.py
, python app.py
дает world
, как и предполагалось, но я хотел бы избежать по возможности перетащить sys.path
.
Требования к ответам
Мне бы хотелось python app.py
распечатать ISI says: world
, и я хотел бы сделать это, изменив функцию path_import
.
Я не уверен в последствиях mangling sys.path
. Например. если был directory/requests.py
, и я добавил путь к directory
в sys.path
, я бы не хотел, чтобы import requests
начал импортировать directory/requests.py
вместо импорта запрашивает библиотеку, которую я установил с помощью pip install requests
.
Решение ДОЛЖНО реализовано как функция python, которая принимает абсолютный путь к нужному модулю и возвращает объект модуля .
В идеале решение не должно вводить побочные эффекты (например, если он изменяет sys.path
, он должен вернуть sys.path
в исходное состояние). Если решение действительно вводит побочные эффекты, оно должно объяснить, почему решение не может быть достигнуто без введения побочных эффектов.
PYTHONPATH
Если у меня есть несколько проектов, которые делают это, я не хочу забывать устанавливать PYTHONPATH
каждый раз, когда я переключаюсь между ними. Пользователь должен просто иметь возможность pip install
моего проекта и запускать его без каких-либо дополнительных настроек.
-m
-m
флаг является рекомендуемым/питоновым подходом, но стандартная библиотека также явно документирует Как напрямую импортировать исходные файлы. Я хотел бы знать, как я могу адаптировать этот подход, чтобы справиться с неявным относительным импортом. Очевидно, что внутренние компоненты Python должны это делать, поэтому как внутренние компоненты отличаются от документации "импортировать исходные файлы напрямую"?