3

クラスの_ResolveDependenciesメソッドが循環依存を検出できる理由がわかりません。requiresとand requiresDepsTreeの場合は状況を検出できるようですが、循環依存ではありません。abebe

class DepsTree(object):
  """Represents the set of dependencies between source files."""

  def __init__(self, sources):
    """Initializes the tree with a set of sources.

    Args:
      sources: A set of JavaScript sources.

    Raises:
      MultipleProvideError: A namespace is provided by muplitple sources.
      NamespaceNotFoundError: A namespace is required but never provided.
    """

    self._sources = sources
    self._provides_map = dict()

    # Ensure nothing was provided twice.
    for source in sources:
      for provide in source.provides:
        if provide in self._provides_map:
          raise MultipleProvideError(
              provide, [self._provides_map[provide], source])

        self._provides_map[provide] = source

    # Check that all required namespaces are provided.
    for source in sources:
      for require in source.requires:
        if require not in self._provides_map:
          raise NamespaceNotFoundError(require, source)

  def GetDependencies(self, required_namespaces):
    """Get source dependencies, in order, for the given namespaces.

    Args:
      required_namespaces: A string (for one) or list (for one or more) of
        namespaces.

    Returns:
      A list of source objects that provide those namespaces and all
      requirements, in dependency order.

    Raises:
      NamespaceNotFoundError: A namespace is requested but doesn't exist.
      CircularDependencyError: A cycle is detected in the dependency tree.
    """
    if type(required_namespaces) is str:
      required_namespaces = [required_namespaces]

    deps_sources = []

    for namespace in required_namespaces:
      for source in DepsTree._ResolveDependencies(
          namespace, [], self._provides_map, []):
        if source not in deps_sources:
          deps_sources.append(source)

    return deps_sources

  @staticmethod
  def _ResolveDependencies(required_namespace, deps_list, provides_map,
                           traversal_path):
    """Resolve dependencies for Closure source files.

    Follows the dependency tree down and builds a list of sources in dependency
    order.  This function will recursively call itself to fill all dependencies
    below the requested namespaces, and then append its sources at the end of
    the list.

    Args:
      required_namespace: String of required namespace.
      deps_list: List of sources in dependency order.  This function will append
        the required source once all of its dependencies are satisfied.
      provides_map: Map from namespace to source that provides it.
      traversal_path: List of namespaces of our path from the root down the
        dependency/recursion tree.  Used to identify cyclical dependencies.
        This is a list used as a stack -- when the function is entered, the
        current namespace is pushed and popped right before returning.
        Each recursive call will check that the current namespace does not
        appear in the list, throwing a CircularDependencyError if it does.

    Returns:
      The given deps_list object filled with sources in dependency order.

    Raises:
      NamespaceNotFoundError: A namespace is requested but doesn't exist.
      CircularDependencyError: A cycle is detected in the dependency tree.
    """

    source = provides_map.get(required_namespace)
    if not source:
      raise NamespaceNotFoundError(required_namespace)

    if required_namespace in traversal_path:
      traversal_path.append(required_namespace)  # do this *after* the test

      # This must be a cycle.
      raise CircularDependencyError(traversal_path)

    traversal_path.append(required_namespace)

    for require in source.requires:

      # Append all other dependencies before we append our own.
      DepsTree._ResolveDependencies(require, deps_list, provides_map,
                                    traversal_path)
    deps_list.append(source)

    traversal_path.pop()

    return deps_list
4

1 に答える 1

2

短いバージョン:_ResolveDependencies依存関係ツリーの深さ優先走査を実行し、パスを記録します。すでにパスにあるノードに遭遇した場合、これは循環があることを意味します。

_ResolveDependenciesによって具現化された依存関係のフォレストをトラバースしSource.requiresます。_ResolveDependencies任意の時点でのアクティブな呼び出しは、依存関係ツリーを通るパスに対応します (したがって_pathin traversal_path)。traversal_pathこれは、再帰の前に名前空間を追加し、後で削除することで追跡されます。つまり、traversal_pathの呼び出しが名前空間を処理している場合にのみ、名前空間が存在_ResolveDependenciesします。_ResolveDependenciesに既に存在する名前空間をチェックするように が要求された場合、へtraversal_pathの別の呼び出しが_ResolveDependencies名前空間を処理しており、ノードからそれ自体へのパスが存在するため、サイクルが発生します。

例として、最も単純な依存関係のサイクルを考えてみましょう。「a」には「c」が必要であり、「a」が必要です。また、ブランチに依存関係がない場合に何が起こるかを示すために、「a」が「b」を必要とすることも考えてみましょう。次の呼び出しシーケンスが得られます (疑似 python で)。ほとんどの場合、変数の値は変数名に置き換えられます (例: a) ns.name。注: これはソース コードではなく、プログラムの実行時に実行される一連のステートメントを表しています。

_RD(ns={name:a, req: [b, c]}, path=[]):
  パス内の場合: # false
  パス += [a]
  [b,c] の各 subns について:
    _RD(ns={name: b, req: []}, path=[a]):
      パスに b がある場合: # false
      パス += [b]
      [] 内の各サブに対して:
      # このブランチで完了
      パス -= [b]      
    _RD(ns={name: c, req: [a]}, path=[a]):
      パス内の場合: # false
      パス += [c]
      [a] の各 subns について:
        _RD(ns={name: a req: [b,c]}, path=[a, c]):
          パス内の場合: # true
            CircularDependencyError をスローします
于 2011-08-07T18:33:02.297 に答える