Расширение системы импорта для модуля Dynami c - PullRequest
2 голосов
/ 28 мая 2020

Я работаю над проектом, который динамически собирает классы из другого модуля и создает новые классы на основе найденных. Например, если исходный модуль имеет класс «AdaptiveFilter», создается новый класс с именем «AdaptiveFilterNode», содержащий экземпляр «AdaptiveFilter». Я добавлял эти сгенерированные классы в динамически созданный модуль (module_from_spec()) и создал MetaPathFinder и Loader для расширения системы import. Это отлично работает при импорте модуля Dynami c, например, import nodes.auto_gen или from nodes.auto_gen import AdaptiveFilterNode, но не работает при попытке напрямую импортировать класс, например import nodes.auto_gen.AdaptiveFilterNode. Мне нужно иметь возможность напрямую импортировать класс, потому что сгенерированные классы должны иметь возможность выбора, что требует прямого импорта. Я считаю, что проблема в Finder, который я сделал:

class MyNodeFinder(MetaPathFinder):

    def find_spec(self, fullname, path=None, target=None):

        if fullname == 'nodes.auto_gen':
            auto_gen_spec = = ModuleSpec('nodes.auto_gen',
                                           MyLoader())

            return auto_gen_spec
        return None

Из моего ограниченного понимания системы импорта, в Finder или модуле spe c нет ничего, что сообщает системе импорта, что классы содержатся в модуле nodes.auto_gen, пока модуль уже не будет загружен. Как я могу создать поисковик / загрузчик, который копирует стандартную систему импорта для модуля Dynami c с классами Dynami c?

EDIT : Полный пример, модуль dynamic_creator:

dynamic_creator / __ init __. Py

from importlib.machinery import ModuleSpec
from importlib.util import module_from_spec
import sys
from importlib.abc import MetaPathFinder
from importlib.abc import Loader


__version__ = '0.0.0'


class _MyWrapperClass():

    wrapped_func = None

    def __init__(self):
        super().__init__()

    def __call__(self, *args, **kwargs):
        return self.wrapped_func(*args, **kwargs)


class MyFinder(MetaPathFinder):

    def find_spec(self, fullname, path=None, target=None):

        if fullname == 'dynamic_creator.auto_gen':
            auto_node_spec = ModuleSpec('dynamic_creator.auto_gen',
                                        MyLoader())

            return auto_node_spec
        return None


class MyLoader(Loader):

    def create_module(self, spec):
        return module_from_spec(ModuleSpec('dynamic_creator.auto_gen', None))

    def exec_module(self, module):
        def create_type(func):
            t = type(func.__name__, (_MyWrapperClass, ), {})
            t.wrapped_func = func

            # Wait, you can do this?
            t.__module__ = 'dynamic_creator.auto_gen'
            return t

        funcs_to_wrap = [
            sum,
            abs,
            pow
        ]

        for func in funcs_to_wrap:
            setattr(module, func.__name__, create_type(func))


my_finder = MyFinder()

sys.meta_path.append(my_finder)

Тестовый сценарий:

import dynamic_creator.auto_gen  # Works
from dynamic_creator.auto_gen import abs  # Works
import dynamic_creator.auto_gen.pow as my_pow  # Fails

EDIT : Сообщение об ошибке выглядит следующим образом:

No module named 'dynamic_creator.auto_gen.pow'; 'dynamic_creator.auto_gen' is not a package

...