Example #1
0
 def run_pylint(self, path):
     finder = PathFinder(FileSystem())
     executive = Executive()
     env = os.environ.copy()
     env['PYTHONPATH'] = os.pathsep.join([
         get_blink_tools_dir(),
         finder.path_from_blink_source('build', 'scripts'),
         get_blinkpy_thirdparty_dir(),
         finder.path_from_blink_source('bindings', 'scripts'),
         finder.path_from_chromium_base('build', 'android'),
         finder.path_from_chromium_base('third_party', 'catapult', 'devil'),
         finder.path_from_chromium_base('third_party', 'pymock'),
     ])
     return executive.run_command([
         sys.executable,
         finder.path_from_depot_tools_base('pylint.py'),
         '--output-format=parseable',
         '--rcfile=' + finder.path_from_blink_tools('blinkpy', 'pylintrc'),
         path,
     ],
                                  env=env,
                                  error_handler=executive.ignore_error)
class DirectoryOwnersExtractor(object):
    def __init__(self, host):
        self.filesystem = host.filesystem
        self.finder = PathFinder(self.filesystem)
        self.executive = host.executive
        self.owner_map = None

    def list_owners(self, changed_files):
        """Looks up the owners for the given set of changed files.

        Args:
            changed_files: A list of file paths relative to the repository root.

        Returns:
            A dict mapping tuples of owner email addresses to lists of
            owned directories (paths relative to the root of web tests).
        """
        email_map = collections.defaultdict(set)
        external_root_owners = self.finder.path_from_web_tests(
            'external', 'OWNERS')
        for relpath in changed_files:
            # Try to find the first *non-empty* OWNERS file.
            absolute_path = self.finder.path_from_chromium_base(relpath)
            owners = None
            owners_file = self.find_owners_file(absolute_path)
            while owners_file:
                owners = self.extract_owners(owners_file)
                if owners:
                    break
                # Found an empty OWNERS file. Try again from the parent directory.
                absolute_path = self.filesystem.dirname(
                    self.filesystem.dirname(owners_file))
                owners_file = self.find_owners_file(absolute_path)
            # Skip web_tests/external/OWNERS.
            if not owners or owners_file == external_root_owners:
                continue

            owned_directory = self.filesystem.dirname(owners_file)
            owned_directory_relpath = self.filesystem.relpath(
                owned_directory, self.finder.web_tests_dir())
            email_map[tuple(owners)].add(owned_directory_relpath)
        return {
            owners: sorted(owned_directories)
            for owners, owned_directories in email_map.iteritems()
        }

    def find_owners_file(self, start_path):
        """Finds the first enclosing OWNERS file for a given path.

        Starting from the given path, walks up the directory tree until the
        first OWNERS file is found or web_tests/external is reached.

        Args:
            start_path: A relative path from the root of the repository, or an
                absolute path. The path can be a file or a directory.

        Returns:
            The absolute path to the first OWNERS file found; None if not found
            or if start_path is outside of web_tests/external.
        """
        abs_start_path = (start_path if self.filesystem.isabs(start_path) else
                          self.finder.path_from_chromium_base(start_path))
        directory = (abs_start_path if self.filesystem.isdir(abs_start_path)
                     else self.filesystem.dirname(abs_start_path))
        external_root = self.finder.path_from_web_tests('external')
        if not directory.startswith(external_root):
            return None
        # Stop at web_tests, which is the parent of external_root.
        while directory != self.finder.web_tests_dir():
            owners_file = self.filesystem.join(directory, 'OWNERS')
            if self.filesystem.isfile(
                    self.finder.path_from_chromium_base(owners_file)):
                return owners_file
            directory = self.filesystem.dirname(directory)
        return None

    def extract_owners(self, owners_file):
        """Extracts owners from an OWNERS file.

        Args:
            owners_file: An absolute path to an OWNERS file.

        Returns:
            A list of valid owners (email addresses).
        """
        contents = self._read_text_file(owners_file)
        email_regexp = re.compile(BASIC_EMAIL_REGEXP)
        addresses = []
        for line in contents.splitlines():
            line = line.strip()
            if email_regexp.match(line):
                addresses.append(line)
        return addresses

    def extract_component(self, owners_file):
        """Extracts the component from an OWNERS file.

        Args:
            owners_file: An absolute path to an OWNERS file.

        Returns:
            A string, or None if not found.
        """
        dir_metadata = self._read_dir_metadata(owners_file)
        if dir_metadata and dir_metadata.component:
            return dir_metadata.component

        contents = self._read_text_file(owners_file)
        search = re.search(COMPONENT_REGEXP, contents, re.MULTILINE)
        if search:
            return search.group(1)
        return None

    def is_wpt_notify_enabled(self, owners_file):
        """Checks if the OWNERS file enables WPT-NOTIFY.

        Args:
            owners_file: An absolute path to an OWNERS file.

        Returns:
            A boolean.
        """
        dir_metadata = self._read_dir_metadata(owners_file)
        if dir_metadata and dir_metadata.should_notify is not None:
            return dir_metadata.should_notify

        contents = self._read_text_file(owners_file)
        return bool(re.search(WPT_NOTIFY_REGEXP, contents, re.MULTILINE))

    @memoized
    def _read_text_file(self, path):
        return self.filesystem.read_text_file(path)

    @memoized
    def _read_dir_metadata(self, path):
        """Read the content from a path.

        Args:
            path: An absolute path.

        Returns:
            A WPTDirMetadata object, or None if not found.
        """
        root_path = self.finder.web_tests_dir()
        dir_path = self.filesystem.dirname(path)

        # dirmd starts with an absolute directory path, `dir_path`, traverses all
        # parent directories and stops at `root_path` to find the first available DIR_METADATA
        # file. `root_path` is the web_tests directory.
        json_data = self.executive.run_command([
            self.finder.path_from_depot_tools_base('dirmd'), 'compute',
            '-root', root_path, dir_path
        ])
        try:
            data = json.loads(json_data)
        except ValueError:
            return None

        relative_path = self.filesystem.relpath(dir_path, root_path)
        return WPTDirMetadata(data, relative_path)