Bug 1497898 - Allow the gitignore filter to work on name components only, r=ato

We end up with a lot of rules like (?:.*)/.*\.ext which are basically
trying to find the last component in a path and match against
that. These are rather slow to run so the easiest thing tdo is just
pass in the last component of the path when we know that's the only
thing the rule can match.

The changes to surrounding code to use this API will be made in future
commits.

Depends on D8222

Differential Revision: https://phabricator.services.mozilla.com/D8223

--HG--
extra : moz-landing-system : lando
This commit is contained in:
James Graham 2018-11-16 18:48:36 +00:00
Родитель 7a9dd60df5
Коммит f7f73f6d13
3 изменённых файлов: 51 добавлений и 31 удалений

Просмотреть файл

@ -4,18 +4,23 @@ import os
end_space = re.compile(r"([^\\]\s)*$")
def fnmatch_translate(pat, path_name=False):
def fnmatch_translate(pat, allow_component_only=True):
parts = []
seq = False
i = 0
if pat[0] == "/" or path_name:
component_pattern = False
if pat[0] == "/":
parts.append("^")
any_char = "[^/]"
if pat[0] == "/":
pat = pat[1:]
else:
any_char = "."
parts.append("^(?:.*/)?")
if allow_component_only and "/" not in pat:
component_pattern = True
parts.append("^")
else:
parts.append("^(?:.*/)?")
if pat[-1] == "/":
# If the last character is / match this directory or any subdirectory
pat = pat[:-1]
@ -44,7 +49,7 @@ def fnmatch_translate(pat, path_name=False):
parts.append(c)
elif c == "-":
parts.append(c)
elif not (path_name and c == "/"):
else:
parts += re.escape(c)
elif c == "[":
parts.append("[")
@ -70,7 +75,7 @@ def fnmatch_translate(pat, path_name=False):
raise ValueError
parts.append(suffix)
try:
return re.compile("".join(parts))
return component_pattern, re.compile("".join(parts))
except Exception:
raise

Просмотреть файл

@ -43,7 +43,7 @@ item_classes = {"testharness": TestharnessTest,
class TypeData(object):
def __init__(self, manifest, type_cls):
def __init__(self, manifest, type_cls, meta_filters):
"""Dict-like object containing the TestItems for each test type.
Loading an actual Item class for each test is unnecessarily
@ -53,14 +53,13 @@ class TypeData(object):
subclass when the test is accessed. In order to remain
API-compatible with consumers that depend on getting an Item
from iteration, we do egerly load all items when iterating
over the class.
"""
over the class."""
self.manifest = manifest
self.type_cls = type_cls
self.data = {}
self.json_data = None
self.json_data = {}
self.tests_root = None
self.data = {}
self.meta_filters = meta_filters or []
def __getitem__(self, key):
if key not in self.data:
@ -108,15 +107,14 @@ class TypeData(object):
def iteritems(self):
self.load_all()
for path, tests in iteritems(self.data):
yield path, tests
return iteritems(self.data)
def load(self, key):
"""Load a specific Item given a path"""
if self.json_data is not None:
data = set()
path = from_os_path(key)
for test in self.json_data.get(path, []):
for test in iterfilter(self.meta_filters, self.json_data.get(path, [])):
manifest_item = self.type_cls.from_json(self.manifest,
self.tests_root,
path,
@ -134,7 +132,7 @@ class TypeData(object):
if key in self.data:
continue
data = set()
for test in self.json_data.get(path, []):
for test in iterfilter(self.meta_filters, self.json_data.get(path, [])):
manifest_item = self.type_cls.from_json(self.manifest,
self.tests_root,
path,
@ -157,7 +155,6 @@ class TypeData(object):
rv |= set(to_os_path(item) for item in iterkeys(self.json_data))
return rv
class ManifestData(dict):
def __init__(self, manifest, meta_filters=None):
"""Dictionary subclass containing a TypeData instance for each test type,
@ -183,10 +180,10 @@ class ManifestData(dict):
class Manifest(object):
def __init__(self, url_base="/"):
def __init__(self, url_base="/", meta_filters=None):
assert url_base is not None
self._path_hash = {}
self._data = ManifestData(self)
self._data = ManifestData(self, meta_filters)
self._reftest_nodes_by_url = None
self.url_base = url_base
@ -197,7 +194,8 @@ class Manifest(object):
if not types:
types = sorted(self._data.keys())
for item_type in types:
for path, tests in sorted(self._data[item_type]):
for path in sorted(self._data[item_type]):
tests = self._data[item_type][path]
yield item_type, path, tests
def iterpath(self, path):
@ -351,14 +349,12 @@ class Manifest(object):
if version != CURRENT_VERSION:
raise ManifestVersionMismatch
self = cls(url_base=obj.get("url_base", "/"))
self = cls(url_base=obj.get("url_base", "/"), meta_filters=meta_filters)
if not hasattr(obj, "items") and hasattr(obj, "paths"):
raise ManifestError
self._path_hash = {to_os_path(k): v for k, v in iteritems(obj["paths"])}
meta_filters = meta_filters or []
for test_type, type_paths in iteritems(obj["items"]):
if test_type not in item_classes:
raise ManifestError
@ -382,7 +378,10 @@ def load(tests_root, manifest, types=None, meta_filters=None):
logger.debug("Creating new manifest at %s" % manifest)
try:
with open(manifest) as f:
rv = Manifest.from_json(tests_root, json.load(f), types=types, meta_filters=meta_filters)
rv = Manifest.from_json(tests_root,
json.load(f),
types=types,
meta_filters=meta_filters)
except IOError:
return None
except ValueError:
@ -390,7 +389,10 @@ def load(tests_root, manifest, types=None, meta_filters=None):
return None
return rv
return Manifest.from_json(tests_root, json.load(manifest), types=types, meta_filters=meta_filters)
return Manifest.from_json(tests_root,
json.load(manifest),
types=types,
meta_filters=meta_filters)
def write(manifest, manifest_path):

Просмотреть файл

@ -402,7 +402,7 @@ class ManifestLoader(object):
download=self.manifest_download)
def update_manifest(self, manifest_path, tests_path, url_base="/",
recreate=False, download=False):
meta_filters=None, recreate=False, download=False):
self.logger.info("Updating test manifest %s" % manifest_path)
json_data = None
@ -424,22 +424,35 @@ class ManifestLoader(object):
manifest_file = manifest.Manifest(url_base)
else:
try:
manifest_file = manifest.Manifest.from_json(tests_path, json_data)
manifest_file = manifest.Manifest.from_json(tests_path,
json_data,
meta_filters=meta_filters)
except manifest.ManifestVersionMismatch:
manifest_file = manifest.Manifest(url_base)
manifest_file = manifest.Manifest(url_base, meta_filters=meta_filters)
manifest_update.update(tests_path, manifest_file, True)
manifest.write(manifest_file, manifest_path)
return manifest_file
def load_manifest(self, tests_path, manifest_path, url_base="/", **kwargs):
if (not os.path.exists(manifest_path) or
self.force_manifest_update):
self.update_manifest(manifest_path, tests_path, url_base, download=self.manifest_download)
try:
manifest_file = manifest.load(tests_path, manifest_path, types=self.types, meta_filters=self.meta_filters)
if (not os.path.exists(manifest_path) or
self.force_manifest_update):
manifest_file = self.update_manifest(manifest_path,
tests_path,
url_base,
meta_filters=self.meta_filters,
download=self.manifest_download)
else:
manifest_file = manifest.load(tests_path,
manifest_path,
types=self.types,
meta_filters=self.meta_filters)
except manifest.ManifestVersionMismatch:
manifest_file = manifest.Manifest(url_base)
if manifest_file.url_base != url_base:
self.logger.info("Updating url_base in manifest from %s to %s" % (manifest_file.url_base,
url_base))