Пакет / модуль Python лениво загружает подмодули - PullRequest
0 голосов
/ 16 сентября 2010

Интересный пример использования сегодня: мне нужно перенести модуль в нашу кодовую базу после изменений кода.Старый mynamespace.Document исчезнет, ​​и я хочу обеспечить плавную миграцию, заменив этот пакет на объект кода, который будет динамически импортировать правильный путь и мигрировать соответствующие объекты.

Короче:

# instanciate a dynamic package, but do not load
# statically submodules
mynamespace.Document = SomeObject()
assert 'submodule' not in mynamespace.Document.__dict__

# and later on, when importing it, the submodule
# is built if not already available in __dict__
from namespace.Document.submodule import klass
c = klass()

Несколько замечаний:

  • Я говорю не только о переносе кода .Простого огромного sed в некотором смысле было бы достаточно, чтобы изменить код, чтобы перенести некоторые операции импорта, и мне не понадобился бы динамический модуль.Я говорю об объектах.Веб-сайт, содержащий некоторые живые / хранимые объекты, нуждается в миграции.Эти объекты будут загружены при условии, что mynamespace.Document.submodule.klass существует, и это является причиной динамического модуля.Мне нужно предоставить сайту что-то для загрузки.
  • Мы не можем или не хотим изменять способ, которым объекты не выбираются / загружаются.Для простоты, давайте просто скажем, что мы хотим убедиться, что идиома from mynamespace.Document.submodule import klass должна работать.Я не могу использовать вместо этого from mynamespace import Document as container; klass = getattr(getattr(container, 'submodule'), 'klass')

То, что я пробовал:

import sys
from types import ModuleType

class VerboseModule(ModuleType):
    def __init__(self, name, doc=None):
        super(VerboseModule, self).__init__(name, doc)
        sys.modules[name] = self
    def __repr__(self):
        return "<%s %s>" % (self.__class__.__name__, self.__name__)
    def __getattribute__(self, name):
        if name not in ('__name__', '__repr__', '__class__'):
            print "fetching attribute %s for %s" % (name, self)
        return super(VerboseModule, self).__getattribute__(name)

class DynamicModule(VerboseModule):
    """
    This module generates a dummy class when asked for a component
    """
    def __getattr__(self, name):
        class Dummy(object):
            pass
        Dummy.__name__ = name
        Dummy.__module__ = self
        setattr(self, name, Dummy)
        return Dummy
class DynamicPackage(VerboseModule):
    """
    This package should generate dummy modules
    """
    def __getattr__(self, name):
        mod = DynamicModule("%s.%s" % (self.__name__, name))
        setattr(self, name, mod)
        return mod

DynamicModule("foobar")
# (the import prints:)
# fetching attribute __path__ for <DynamicModule foobar>
# fetching attribute DynamicModuleWorks for <DynamicModule foobar>
# fetching attribute DynamicModuleWorks for <DynamicModule foobar>
from foobar import DynamicModuleWorks
print DynamicModuleWorks

DynamicPackage('document')
# fetching attribute __path__ for <DynamicPackage document>
from document.submodule import ButDynamicPackageDoesNotWork
# Traceback (most recent call last):
# File "dynamicmodule.py", line 40, in <module>
#   from document.submodule import ButDynamicPackageDoesNotWork
#ImportError: No module named submodule

Как видите, динамический пакет не работает.Я не понимаю, что происходит, потому что document даже не запрашивают атрибут ButDynamicPackageDoesNotWork.

Может кто-нибудь уточнить, что происходит;и если / как я могу это исправить?

1 Ответ

4 голосов
/ 16 сентября 2010

Проблема в том, что python пропустит запись для document в sys.modules и загрузит файл для submodule напрямую.Конечно, этого не существует.

демонстрация:

>>> import multiprocessing
>>> multiprocessing.heap = None
>>> import multiprocessing.heap
>>> multiprocessing.heap
<module 'multiprocessing.heap' from '/usr/lib/python2.6/multiprocessing/heap.pyc'>

Мы ожидаем, что heap по-прежнему None, потому что python может просто извлечь его из sys.modules, ноне бываетПунктирная запись по существу отображается непосредственно на {something on python path}/document/submodule.py, и делается попытка загрузить ее напрямую.

Обновление

Хитрость заключается в переопределении системы импорта питонов.Следующий код требует вашего DynamicModule класса.

import sys

class DynamicImporter(object):
    """this class works as both a finder and a loader."""
    def __init__(self, lazy_packages):
        self.packages = lazy_packages

    def load_module(self, fullname):
        """this makes the class a loader. It is given name of a module and expected
           to return the module object"""
        print "loading {0}".format(fullname)
        components = fullname.split('.')
        components = ['.'.join(components[:i+1])
                      for i in range(len(components))]
        for component in components:
            if component not in sys.modules:
                DynamicModule(component)
                print "{0} created".format(component)
        return sys.modules[fullname]


    def find_module(self, fullname, path=None):
        """This makes the class a finder. It is given the name of a module as well as
           the package that contains it (if applicable). It is expected to return a 
           loader for that module if it knows of one or None in which case other methods
           will be tried"""
        if fullname.split('.')[0] in self.packages:
            print "found {0}".format(fullname)
            return self
        else:
            return None


# This is a list of finder objects which is empty by defaule
# It is tried before anything else when a request to import a module is encountered.
sys.meta_path=[DynamicImporter('foo')]

from foo.bar import ThisShouldWork
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...