Альтернативы imp.find_module? - PullRequest
5 голосов
/ 21 апреля 2011

Фон

Я устал от проблемы с 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, которая выдаст ALL возможных совпадений или взять список исключений?Я также открыт для совершенно разных решений.Желательно не вносить исправления в python вручную, но это было бы невозможно - по крайней мере, для локального решения.

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

Ответы [ 3 ]

4 голосов
/ 12 февраля 2013

Начиная с 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; подробности см. в документации, указанной выше.

2 голосов
/ 26 апреля 2012

Я тоже устал от этого ограничения в 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__

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

0 голосов
/ 30 марта 2015

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

перед:

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.
                    # /2017552/dinamicheskaya-zagruzka-klassov-runtimewarning-plaginy-roditelskogo-modulya-naideny-obrabotke-absolytnogo-importa
                    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
...