测试我的拓扑/依赖排序

时间:2016-03-06 02:10:25

标签: python dependency-injection language-agnostic dependencies dependency-management

这更像是一个谜题而不是问题本身,因为我认为我已经有了正确的答案 - 我只是不知道如何测试它(如果它在所有情况下都有效)。

我的程序从用户那里获取有关需要加载哪些模块的输入(以未排序的列表或集合的形式)。其中一些模块依赖于其他模块。模块依赖信息的存储方式如下:

all_modules = { 'moduleE':[], 'moduleD':['moduleC'], 'moduleC':['moduleB'], 'moduleB':[], 'moduleA':['moduleD'] }

其中moduleE没有依赖关系,而moduleD依赖于moduleC等......

此外,没有所有可能模块的明确列表,因为用户可以生成自己的模块,并且我不时创建新模块,因此这个解决方案必须相当通用(因此测试适用于所有情况)。

我想要做的是获取要按顺序运行的模块列表,以便依赖于其他模块的模块仅在其依赖项之后运行。

所以我编写了以下代码来尝试这样做:

def sort_dependencies(modules_to_sort,all_modules,recursions):
    ## Takes a small set of modules the user wants to run (as a list) and 
    ## the full dependency tree (as a dict) and returns a list of all the
    ## modules/dependencies needed to be run, in the order to be run in.
    ## Cycles poorly detected as recursions going above 10. 
    ## If that happens, this function returns False.
    if recursions == 10:
        return False
    result = []
    for module in modules_to_sort:
        if module not in result:
            result.append(module)
        dependencies = all_modules[module]
        for dependency in dependencies:
            if dependency not in result:
                result.append(dependency)
            else:
                result += [ result.pop(result.index(dependency)) ]
        subdependencies = sort_dependencies(dependencies, all_modules, recursions+1)
        if subdependencies == False:
            return False
        else:
            for subdependency in subdependencies:
                if subdependency not in result:
                    result.append(subdependency)
                else:
                    result += [ result.pop(result.index(subdependency)) ]
    return result

它的工作原理如下:

>>> all_modules = { 'moduleE':[], 'moduleD':['moduleC'], 'moduleC':['moduleB'], 'moduleB':[], 'moduleA':['moduleD'] }
>>> sort_dependencies(['moduleA'],all_modules,0)
['moduleA', 'moduleD', 'moduleC', 'moduleB']

请注意,不会返回'moduleE',因为用户不需要运行它。

问题是 - 它是否适用于任何给定的all_modules字典和任何给定的required_to_load列表?是否有我可以放入的依赖图和许多用户模块列表来尝试,如果它们有效,我可以说所有图形/用户列表都可以工作吗?

在Marshall Farrier的出色建议之后,看起来我正在尝试做的是拓扑排序,所以在看了thisthis之后我实现了以下内容:

编辑:现在进行循环依赖检查!

def sort_dependencies(all_modules):
    post_order = []
    tree_edges = {}
    for fromNode,toNodes in all_modules.items():
        if fromNode not in tree_edges:
            tree_edges[fromNode] = 'root'
            for toNode in toNodes:
                if toNode not in tree_edges:
                    post_order += get_posts(fromNode,toNode,tree_edges)
            post_order.append(fromNode)
    return post_order

def get_posts(fromNode,toNode,tree_edges):
    post_order = []
    tree_edges[toNode] = fromNode
    for dependancy in all_modules[toNode]:
        if dependancy not in tree_edges:
            post_order += get_posts(toNode,dependancy,tree_edges)
        else:
            parent = tree_edges[toNode]
            while parent != 'root':
                if parent == dependancy: print 'cyclic dependancy found!'; exit()
                parent = tree_edges[parent]
    return post_order + [toNode]

sort_dependencies(all_modules)

但是,像上面那样的拓扑排序对整个树进行排序,并且实际上并不返回用户需要运行的模块。当然,具有树的拓扑类型有助于解决这个问题,但它与OP的问题并不是同一个问题。我认为对于我的数据,拓扑排序可能是最好的,但是对于像apt / yum / pip / npm中的所有包一样的大图,在OP中使用原始算法可能更好(我不知道它是否是实际上适用于所有情况......)因为它只排序需要使用的东西。

所以我提出的问题没有答案,因为问题实际上是“我如何测试这个?”

0 个答案:

没有答案