Как найти список модулей, которые зависят от конкретного модуля в Python - PullRequest
10 голосов
/ 01 декабря 2009

Чтобы сократить время разработки моего веб-приложения на Python, я пытаюсь использовать reload () для модулей, которые я недавно модифицировал. Функция reload () происходит через выделенную веб-страницу (часть версии для разработки веб-приложения), в которой перечислены модули, которые были недавно изменены (и измененная временная метка py-файла более поздняя, ​​чем соответствующий pyc-файл). Полный список модулей получен из sys.modules (и я фильтрую список, чтобы сосредоточиться только на тех модулях, которые являются частью моего пакета).

Перезагрузка отдельных файлов Python в некоторых случаях работает, а в других - нет. Я предполагаю, что все модули, которые зависят от модифицированного модуля, должны быть перезагружены, и перезагрузка должна происходить в правильном порядке.

Я ищу способ получения списка модулей, импортированных конкретным модулем. Есть ли способ сделать такой самоанализ в Python?

Я понимаю, что мой подход не может быть гарантирован на 100%, и самый безопасный способ - перезагрузить все, но если бы быстрый подход работал в большинстве случаев, он был бы достаточно хорош для целей разработки.

Ответ на комментарии относительно автозагрузчика DJango

@ Гленн Мейнард, Спасибо, я читал об автозагрузчике DJango. Мое веб-приложение основано на Zope 3 и с количеством пакетов и большим количеством инициализаций на основе ZCML, полный перезапуск занимает от 10 до 30 секунд или более, если размер базы данных больше. Я пытаюсь сократить количество времени, затрачиваемого на перезагрузку. Когда я чувствую, что внес много изменений, я обычно предпочитаю делать полный перезапуск, но чаще я меняю несколько строк здесь и там, на которые я не хочу тратить так много времени. Настройка разработки полностью независима от настройки производства, и, как правило, если при перезагрузке происходит что-то не так, это становится очевидным, поскольку на страницах приложения появляется нелогичная информация или создаются исключения. Я очень заинтересован в изучении того, будет ли выборочная перезагрузка работать или нет.

Ответы [ 3 ]

5 голосов
/ 04 декабря 2009

Итак - это отвечает «Найти список модулей, которые зависят от заданного» - вместо того, как был изначально сформулирован вопрос - на который я ответил выше.

Как оказалось, это немного сложнее: нужно найти дерево зависимостей для всех загруженных модулей и инвертировать его для каждого модуля, сохраняя при этом порядок загрузки, который не сломал бы вещи.

Я также разместил это в Python Wiki на Brazillian по адресу: http://www.python.org.br/wiki/RecarregarModulos

#! /usr/bin/env python
# coding: utf-8

# Author: João S. O. Bueno
# Copyright (c) 2009 - Fundação CPqD
# License: LGPL V3.0


from types import ModuleType, FunctionType, ClassType
import sys

def find_dependent_modules():
    """gets a one level inversed module dependence tree"""
    tree = {}
    for module in sys.modules.values():
        if module is None:
            continue
        tree[module] = set()
        for attr_name in dir(module):
            attr = getattr(module, attr_name)
            if isinstance(attr, ModuleType):
                tree[module].add(attr)
            elif type(attr) in (FunctionType, ClassType):        
                tree[module].add(attr.__module__)
    return tree


def get_reversed_first_level_tree(tree):
    """Creates a one level deep straight dependence tree"""
    new_tree = {}
    for module, dependencies in tree.items():
        for dep_module in dependencies:
            if dep_module is module:
                continue
            if not dep_module in new_tree:
                new_tree[dep_module] = set([module])
            else:
                new_tree[dep_module].add(module)
    return new_tree

def find_dependants_recurse(key, rev_tree, previous=None):
    """Given a one-level dependance tree dictionary,
       recursively builds a non-repeating list of all dependant
       modules
    """
    if previous is None:
        previous = set()
    if not key in rev_tree:
        return []
    this_level_dependants = set(rev_tree[key])
    next_level_dependants = set()
    for dependant in this_level_dependants:
        if dependant in previous:
            continue
        tmp_previous = previous.copy()
        tmp_previous.add(dependant)
        next_level_dependants.update(
             find_dependants_recurse(dependant, rev_tree,
                                     previous=tmp_previous,
                                    ))
    # ensures reloading order on the final list
    # by postponing the reload of modules in this level
    # that also appear later on the tree
    dependants = (list(this_level_dependants.difference(
                        next_level_dependants)) +
                  list(next_level_dependants))
    return dependants

def get_reversed_tree():
    """
        Yields a dictionary mapping all loaded modules to
        lists of the tree of modules that depend on it, in an order
        that can be used fore reloading
    """
    tree = find_dependent_modules()
    rev_tree = get_reversed_first_level_tree(tree)
    compl_tree = {}
    for module, dependant_modules in rev_tree.items():
        compl_tree[module] = find_dependants_recurse(module, rev_tree)
    return compl_tree

def reload_dependences(module):
    """
        reloads given module and all modules that
        depend on it, directly and otherwise.
    """
    tree = get_reversed_tree()
    reload(module)
    for dependant in tree[module]:
        reload(dependant)

Это прекрасно работало во всех тестах, которые я здесь проводил, но я не стал бы рекомендовать злоупотреблять этим. Но для обновления работающего сервера zope2 после редактирования нескольких строк кода, я думаю, я бы использовал это сам.

3 голосов
/ 01 декабря 2009

Возможно, вы захотите взглянуть на модуль Reloader от Ian Bicking, который делает то, что вы уже хотите:

http://pythonpaste.org/modules/reloader?highlight=reloader

Он не дает вам конкретно список зависимых файлов (что технически возможно, только если упаковщик был усердным и правильно определил зависимости), но просмотр кода даст вам точный список измененных файлов для перезапуска. процесс.

2 голосов
/ 01 декабря 2009

Некоторый самоанализ на помощь:

from types import ModuleType

def find_modules(module, all_mods = None):
   if all_mods is None:
      all_mods = set([module])
   for item_name in dir(module):
       item = getattr(module, item_name)
       if isinstance(item, ModuleType) and not item in all_mods:
           all_mods.add(item)
           find_modules(item, all_mods)
   return all_mods

Это дает вам набор со всеми загруженными модулями - просто вызовите функцию с вашим первым модулем в качестве единственного параметра. Затем вы можете выполнить итерацию по результирующему набору, загружая его так же просто, как: [перезагрузить (m) для m в find_modules ()]

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