Краткая версия: _ResolveDependencies
выполняет первичный обход дерева зависимостей, записывая путь. Если он встречает узел, который уже находится в пути, это означает, что существует цикл.
_ResolveDependencies
пересекает лес зависимостей, заключенный в Source.requires
. Активные вызовы _ResolveDependencies
в любой момент времени соответствуют пути через дерево зависимостей (следовательно, _path
в traversal_path
); это отслеживается путем добавления пространства имен к traversal_path
до рекурсии и удаления его после. Другими словами, пространство имен находится в traversal_path
тогда и только тогда, когда вызов _ResolveDependencies
обрабатывает пространство имен. Если _ResolveDependencies
требуется проверить пространство имен, которое уже существует в traversal_path
, тогда другой вызов _ResolveDependencies
обрабатывает пространство имен, и существует путь от узла к самому себе, следовательно, цикл.
В качестве примера рассмотрим простейший цикл зависимости: «a» требует «c», требует «a». Давайте также добавим «a», требующий «b», чтобы показать, что происходит, когда в ветви нет зависимости. Вы получите следующую последовательность вызовов (в псевдопионе). В большинстве случаев значение переменной заменяется на имя переменной (например, a
для ns.name
). Примечание: это не исходный код, а последовательность операторов, которые выполняются при запуске программы.
_RD(ns={name:a, req: [b, c]}, path=[]):
if a in path: # false
path += [a]
for each subns in [b,c]:
_RD(ns={name: b, req: []}, path=[a]):
if b in path: # false
path += [b]
for each subns in []:
# done with this branch
path -= [b]
_RD(ns={name: c, req: [a]}, path=[a]):
if c in path: # false
path += [c]
for each subns in [a]:
_RD(ns={name: a req: [b,c]}, path=[a, c]):
if a in path: # true
throw CircularDependencyError