Неявно использовать пространство имен в импортированных модулях в Python

Я пытаюсь создать библиотеку из проекта Python, которым я не владею. Проект имеет следующую структуру каталогов:

.
├── MANIFEST.in
├── pyproject.toml
└── src
    ├── all.py
    ├── the.py
    └── sources.py

В pyproject.toml у меня есть:

[tool.setuptools]
packages = ["mypkg"]

[tool.setuptools.package-dir]
mypkg = "src"

Проблема, с которой я столкнулся, заключается в том, что когда я собираю и устанавливаю этот пакет, я не могу его использовать, потому что автор импортирует вещи без префикса mypkg в различные исходные файлы.

F.ex. в all.py

from the import SomeThing

Поскольку у меня нет пакета, я не могу модифицировать все исходники, но я все же хочу создать из него библиотеку, просто добавив MANIFEST.in и pyproject.toml.

Можно ли как-то проинструктировать setuptools собрать пакет, который не будет засорять site-packages всеми исходниками, но при этом позволит их импортировать без префикса mypkg?

Краткий ответ: нет, они должны быть на уровне соответствующего каталога (т.е. site-packages). Более длинный ответ: обойти это ограничение, внедрив хук импорта с вашим пакетом и перехватив все соответствующие импорты, чтобы они разрешались в тот, который установлен в site-packages/mypkg месте. Обратите внимание, что использование хуков импорта не обязательно решит проблему глобального загрязнения пространства имен, если только вы не сможете найти способ сделать этот импорт доступным только для тех модулей из mypkg.

metatoaster 12.10.2022 11:01

Спасибо @metatoaster! Если вы дадите ответ как ответ, я могу отметить его как принятый.

evading 13.10.2022 16:40
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
2
69
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это невозможно без добавления пользовательского хука импорта с пакетом. Хук принимает форму модуля, который поставляется с пакетом, и перед использованием его необходимо импортировать из вашего модуля (например, в src/all.py).

src/mypkgimp.py

import sys
import importlib  

class MyPkgLoader(importlib.abc.Loader):
    def find_spec(self, name, path=None, target=None):
        # update the list with modules that should be treated special
        if name in ['sources', 'the']:
            return importlib.util.spec_from_loader(name, self)
        return None

    def create_module(self, spec):
        # Uncomment if "normal" imports should have precedence
        # try:
        #     sys.meta_path = [x for x in sys.meta_path[:] if x is not self]
        #     return importlib.import_module(spec.name)
        # except ImportError:
        #     pass
        # finally:
        #     sys.meta_path = [self] + sys.meta_path

        # Otherwise, this will unconditionally shadow normal imports
        module = importlib.import_module('.' + spec.name, 'mypkg')
        # Final step: inject the module to the "shortened" name
        sys.modules[spec.name] = module
        return module

    def exec_module(self, module):
        pass

if not hasattr(sys, 'frozen'):
    sys.meta_path = [MyPkgLoader()] + sys.meta_path

Да, вышеприведенное использует разные методы, описанные в потоке , на который я ссылался ранее, поскольку importlib устарела в Python 3.10, обратитесь к документации за подробностями.

В любом случае, для демонстрации поместите в модули несколько фиктивных классов:

src/the.py

class SomeThing: ...

src/sources.py

class Source: ...

Теперь измените src/all.py, чтобы иметь следующее:

import mypkg.mypkgimp
from the import SomeThing

Пример использования:

>>> from sources import Source
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'sources'
>>> from mypkg import all
>>> all.SomeThing
<class 'mypkg.the.SomeThing'>
>>> from sources import Source
>>> Source
<class 'mypkg.sources.Source'>
>>> from sources import Error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: cannot import name 'Error' from 'mypkg.sources' (/tmp/mypkg/src/sources.py)

Обратите внимание, что изначально импорт не работал, но после того, как mypkg.all был импортирован, импорт sources теперь работает глобально. Следовательно, может потребоваться осторожность, чтобы не скрывать «настоящий» импорт, и я предоставил пример для импорта с использованием механизма импорта «по умолчанию» [*].

Если вы хотите, чтобы имена модулей выглядели по-другому (то есть без префикса mypkg.), это будет отдельный вопрос, поскольку код обычно не проверяет собственное имя модуля на функциональность (и неважно, что это фактически показывает, как пространство имен неявно используется - изменение фактического имени больше похоже на перемещение модуля, да, это можно сделать, но немного сложнее, и этот ответ и так достаточно длинный).

[*] «по умолчанию», то есть не включая поведение, введенное этим пользовательским хуком импорта - другие хуки импорта могут делать свои собственные другие странные махинации.

Другие вопросы по теме