Files
basilisk55/python/mozlint/mozlint/pathutils.py
T

157 lines
5.0 KiB
Python

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from __future__ import unicode_literals
import os
from mozpack import path as mozpath
from mozpack.files import FileFinder
class FilterPath(object):
"""Helper class to make comparing and matching file paths easier."""
def __init__(self, path, exclude=None):
self.path = os.path.normpath(path)
self._finder = None
self.exclude = exclude
@property
def finder(self):
if self._finder:
return self._finder
self._finder = FileFinder(
self.path, find_executables=False, ignore=self.exclude)
return self._finder
@property
def ext(self):
return os.path.splitext(self.path)[1]
@property
def exists(self):
return os.path.exists(self.path)
@property
def isfile(self):
return os.path.isfile(self.path)
@property
def isdir(self):
return os.path.isdir(self.path)
def join(self, *args):
return FilterPath(os.path.join(self, *args))
def match(self, patterns):
return any(mozpath.match(self.path, pattern.path) for pattern in patterns)
def contains(self, other):
"""Return True if other is a subdirectory of self or equals self."""
if isinstance(other, FilterPath):
other = other.path
a = os.path.abspath(self.path)
b = os.path.normpath(os.path.abspath(other))
if b.startswith(a):
return True
return False
def __repr__(self):
return repr(self.path)
def filterpaths(paths, linter, **lintargs):
"""Filters a list of paths.
Given a list of paths, and a linter definition plus extra
arguments, return the set of paths that should be linted.
:param paths: A starting list of paths to possibly lint.
:param linter: A linter definition.
:param lintargs: Extra arguments passed to the linter.
:returns: A list of file paths to lint.
"""
include = linter.get('include', [])
exclude = lintargs.get('exclude', [])
exclude.extend(linter.get('exclude', []))
root = lintargs['root']
if not lintargs.get('use_filters', True) or (not include and not exclude):
return paths
def normalize(path):
if not os.path.isabs(path):
path = os.path.join(root, path)
return FilterPath(path)
include = map(normalize, include)
exclude = map(normalize, exclude)
# Paths with and without globs will be handled separately,
# pull them apart now.
includepaths = [p for p in include if p.exists]
excludepaths = [p for p in exclude if p.exists]
includeglobs = [p for p in include if not p.exists]
excludeglobs = [p for p in exclude if not p.exists]
extensions = linter.get('extensions')
keep = set()
discard = set()
for path in map(FilterPath, paths):
# Exclude bad file extensions
if extensions and path.isfile and path.ext not in extensions:
continue
if path.match(excludeglobs):
continue
# First handle include/exclude directives
# that exist (i.e don't have globs)
for inc in includepaths:
# Only excludes that are subdirectories of the include
# path matter.
excs = [e for e in excludepaths if inc.contains(e)]
if path.contains(inc):
# If specified path is an ancestor of include path,
# then lint the include path.
keep.add(inc)
# We can't apply these exclude paths without explicitly
# including every sibling file. Rather than do that,
# just return them and hope the underlying linter will
# deal with them.
discard.update(excs)
elif inc.contains(path):
# If the include path is an ancestor of the specified
# path, then add the specified path only if there are
# no exclude paths in-between them.
if not any(e.contains(path) for e in excs):
keep.add(path)
# Next handle include/exclude directives that
# contain globs.
if path.isfile:
# If the specified path is a file it must be both
# matched by an include directive and not matched
# by an exclude directive.
if not path.match(includeglobs):
continue
keep.add(path)
elif path.isdir:
# If the specified path is a directory, use a
# FileFinder to resolve all relevant globs.
path.exclude = [e.path for e in excludeglobs]
for pattern in includeglobs:
for p, f in path.finder.find(pattern.path):
keep.add(path.join(p))
# Only pass paths we couldn't exclude here to the underlying linter
lintargs['exclude'] = [f.path for f in discard]
return [f.path for f in keep]