Альтернативы imp.find_module?

Задний план

Я устал от проблемы с тем, что pylint не может импортировать файлы, когда вы используете пакеты пространства имен и делите свою кодовую базу на отдельные папки. Поэтому я начал копаться в исходном коде astNG, который был идентифицирован как источник проблемы (см. отчет об ошибке 8796 на astng). В основе проблемы, по-видимому, лежит использование собственных питонов imp.find_module в процессе поиска импорта.

Что происходит, так это то, что первый (под)пакет импорта - a в import a.b.c - передается в find_module с путем None. Какой бы путь ни возвращался, он затем передается в find_module на следующем проходе в цикле поиска, где вы пытаетесь найти b в предыдущем примере.

Псевдокод из logilab.common.modutils:

path = None
while import_as_list:
      try:
           _, found_path, etc = find_module(import_as_list[0], path)
      #exception handling and checking for a better version in the .egg files
      path = [found_path]
      import_as_list.pop(0)

Эта проблема

Вот что сломано: вы получаете только первый лучший результат от find_module, в котором могут быть или не быть ваши подпакеты. Если вы НЕ найдете подпакеты, у вас не будет возможности отступить и попробовать следующий.

Я попытался явно использовать sys.path вместо None, чтобы результат можно было удалить из списка путей и сделать вторую попытку, но средство поиска модулей python достаточно умно, чтобы не было точного совпадения в путях. , что делает этот подход непригодным для использования - во всяком случае, насколько мне известно.

Со слезами на глазах

Есть ли альтернатива find_modules, которая возвращает ВСЕ возможные совпадения или принимает список исключений? Я также открыт для совершенно разных решений. Желательно не исправлять python вручную, но это не невозможно — по крайней мере, для локального решения.

(Предостережение: я использую python 2.6 и по причинам текущей политики компании не могу обновиться, предложения для p3k и т. Д. Не будут помечены как принятые, если это не единственный ответ.)


person Per Fagrell    schedule 21.04.2011    source источник


Ответы (3)


Я тоже устал от этого ограничения в PyLint.

Я не знаю замены для imp.find_modules(), но я думаю, что нашел другой способ работы с пакетами пространства имен в PyLint. См. мой комментарий к отчету об ошибке, на который вы ссылаетесь (http://www.logilab.org/ticket/8796).

Идея состоит в том, чтобы использовать pkg_resources для поиска пакетов пространства имен. Вот мое дополнение к logilab.common.modutils._module_file() сразу после while modpath:

  while modpath:
      if modpath[0] in pkg_resources._namespace_packages and len(modpath) > 1:
          module = sys.modules[modpath.pop(0)]
          path = module.__path__

Это не очень изысканно и обрабатывает только пакеты пространства имен верхнего уровня.

person jd.    schedule 26.04.2012
comment
Пакетов пространства имен верхнего уровня достаточно для моих нужд, я очень надеюсь, что это (или что-то более общее) будет быстро добавлено в исходный код! Я очень рад, что вам удалось найти исправление, где я нарисовал пробел :) - person Per Fagrell; 23.05.2012

Начиная с Python 2.5, правильный способ сделать это — использовать pkgutil.iter_modules(). (для плоского списка) или pkgutil.walk_packages() (для дерева подпакетов). Оба полностью совместимы с пакетами пространства имен.

Например, если бы я хотел найти только подпакеты/подмодули «jmb», я бы сделал:

import jmb, pkgutil
for (module_loader, name, ispkg) in pkgutil.iter_modules(jmb.__path__, 'jmb.'):
    # 'name' will be 'jmb.foo', 'jmb.bar', etc.
    # 'ispkg' will be true if 'jmb.foo' is a package, false if it's a module

Вы также можете использовать iter_modules или walk_packages для обхода всех модулей в sys.path; подробности см. в документах, указанных выше.

person PJ Eby    schedule 11.02.2013

предупреждение + отказ от ответственности: еще не проверено!

до:

for part in parts:
    modpath.append(part)
    curname = '.'.join(modpath)
    # ...
    if module is None:
        mp_file, mp_filename, mp_desc = imp.find_module(part, path)
        module = imp.load_module(curname, mp_file, mp_filename, mp_desc)

после: - спасибо pjeby за упоминание pkgutil!

for part in parts:
    modpath.append(part)
    curname = '.'.join(modpath)
    # ...
    if module is None:
        # + https://stackoverflow.com/a/14820895/611007
        # # mp_file, mp_filename, mp_desc = imp.find_module(part, path)
        # # module = imp.load_module(curname, mp_file, mp_filename, mp_desc)
        import pkgutil
        mp_file = None
        for loadr,name,ispkg in pkgutil.iter_modules(path=path,prefix='.'.join(modpath[:-1])+'.'):
            if name.split('.')[-1] == part:
                if not hasattr(loadr,'path') and hasattr(loadr,'archive'):
                    # with zips `name` was like '.somemodule'
                    # it gives `RuntimeWarning: Parent module '' not found while handling absolute import`
                    # I expect the name I need to be 'somemodule'
                    # TODO: I don't know why python does this or what the correct usage is.
                    # https://stackoverflow.com/questions/2267984/
                    if name and name[0] == '.':
                        name = name[1:]
                    ldr= loadr.find_module(name,loadr.archive)
                    module = ldr.load_module(name)
                    break
                imploader= loadr.find_module(name,loadr.path)
                mp_file,mp_filename,mp_desc= imploader.file,imploader.filename,imploader.etc
                module = imploader.load_module(imploader.fullname)
                break
        if module is None:
            raise ImportError
person n611x007    schedule 30.03.2015