15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# Copyright 2014 The Chromium Authors. All rights reserved.
25d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
35d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# found in the LICENSE file.
45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import collections
65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import os
75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class PathSet(collections.MutableSet):
105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  """A set of paths.
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  All mutation methods can take both directories or individual files, but the
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  iterator yields the individual files. All paths are automatically normalized.
145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  """
155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def __init__(self, iterable=None):
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self._paths = set()
175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if iterable:
185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      self |= iterable
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def __contains__(self, path):
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return os.path.realpath(path) in self._paths
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def __iter__(self):
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return iter(self._paths)
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def __len__(self):
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return len(self._paths)
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def add(self, path):
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    path = os.path.realpath(path)
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if os.path.isfile(path):
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      self._paths.add(path)
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for root, _, files in os.walk(path):
345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      for basename in files:
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        file_path = os.path.join(root, basename)
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if os.path.isfile(file_path):
375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          self._paths.add(file_path)
385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def discard(self, path):
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    path = os.path.realpath(path)
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self._paths.discard(path)
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for root, _, files in os.walk(path):
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      for basename in files:
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self._paths.discard(os.path.join(root, basename))
45