У меня две версии одного и того же пакета Python. Мне нужно от модуля в субпакете в текущей версии, чтобы иметь возможность вызвать функцию внутри старой версии пакета (который копировал себя в прошлом)
Где я сейчас:
now/
package/
__init__.py
subpackage/
__init__.py
module.py -> "import package.subpackage.... <HERE>"
subpackage2/
...
...
Старая версия:
past/
package/
__init__.py
subpackage/
__init__.py
module.py -> "import package.subpackage; from . import module2; .... def f(x) ..."
module2.py
subpackage2/
...
...
Мне нужно импортировать в <HERE>
"старый" f
и запустить его.
в идеале
f
должна жить своей жизнью внутри старого пакета, не зная ничего о новой версии пакетаf
сделать ее вещь должна работать "новая "версия)Основная идея состоит в том, чтобы улучшить воспроизводимость, сохранив код, который я использовал для какой-либо задачи, вместе с выходными данными, а затем смог запустить ее части.
К сожалению, я понял, что это не простая задача с Python 3, поэтому я готов принять какой-то компромисс. Я готов принять, например, что после запуска старого f(x)
package
имен в "новом" коде будет привязан к старому.
РЕДАКТИРОВАТЬ
Я попытался двумя способами с помощью importlib
. Идея заключалась в создании mod
объекта, а затем выполнении f = getattr(mod, "f")
, но это не работает
sys.path
на ['.../past/package/subpackage']
а затем вызов importlib.import_module('package.subpackage.module')
. Проблема в том, что он будет загружать один из "сейчас" даже с измененным sys.path
, возможно, потому, что package
имен уже находится в sys.modules
spec = importlib.util.spec_from_file_location("module", "path..to..past..module.py")) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module)
В этом случае относительный импорт (from. import module2.py
) не будет работать, что приведет к ошибке "попытка относительного импорта без известного родительского пакета"Есть один способ, которым это может работать довольно просто, но вам придется внести несколько изменений в свой старый пакет.
Вы можете просто создать файл в now/package/old/__init__.py
содержащий:
__path__ = ['/absolute/path/to/old/package']
В новом пакете вы можете:
from package.old.package.subpackage.module import f as old_f
Улов здесь заключается в том, что старый пакет пытается импортировать свои собственные пакеты с использованием абсолютного импорта, вместо этого он загружает материал из новых пакетов. Таким образом, старый пакет должен будет использовать только относительный импорт при импорте продуктов из своего собственного пакета или вам придется добавлять пакет package.old ко всем абсолютным импортам, которые использовался старым пакетом.
Если вы в порядке с модификацией старых пакетов таким образом, то это должно быть хорошо. Если это ограничение не сработает для вас, тогда прочитайте.
Если вы действительно уверены, что по некоторым причинам не хотите изменять старые пакеты. Затем сделайте черную магию, вы хотите заменить builtins.__import__
своей собственной версией, которая возвращает разные модули в зависимости от того, кто выполняет импорт. Вы можете выяснить, кто выполняет импорт, проверив стек вызовов.
Например, так вы можете это сделать (тестируется на Python 3.6):
import builtins
import inspect
import package.old
old_package_path = package.old.__path__[0]
OUR_PACKAGE_NAME = 'package'
OUR_PACKAGE_NAME_WITH_DOT = OUR_PACKAGE_NAME + '.'
def import_module(name, globs=None, locs=None, fromlist=(), level=0):
# only intercept imports for our own package from our old module
if not name.startswith(OUR_PACKAGE_NAME_WITH_DOT) or \
not inspect.stack()[1].filename.startswith(old_package_path):
return real_import(name, globs, locs, fromlist, level)
new_name = OUR_PACKAGE_NAME + '.old.' + name[len(OUR_PACKAGE_NAME_WITH_DOT):]
mod = real_import(new_name, globs, locs, fromlist, level)
return mod.old
# save the original __import__ since we'll need it to do the actual import
real_import = builtins.__import__
builtins.__import__ = import_module
builtins.__import__
в любых builtins.__import__
импорта, встречающихся интерпретатором, и вызов не кэшируется, поэтому вы можете возвращать разные вещи каждый раз, когда он вызывается, даже если они используют одно и то же имя.
Ниже мой старый ответ, здесь только для исторических целей
Я не совсем понимаю, что вы пытаетесь сделать, но это, вероятно, можно сделать в Python 3 с помощью importlib.
Вы бы просто создали загрузчик модулей, который загружает ваш модуль из явного пути к файлу.
Также есть функция invalidate_caches()
и reload()
которая может быть полезна, хотя вам могут и не понадобиться.
virtualenv
, то unpicking и работаю в том , что