Bug 1336559 - Refactor keyed-by matching algorithm into standalone utility function, r?dustin draft
authorAndrew Halberstadt <ahalberstadt@mozilla.com>
Fri, 24 Feb 2017 08:56:11 -0500
changeset 489236 61b29b7e3a7a08a63543fdd6f37a19c7b4e576b2
parent 489235 65b4987c510a3d3d4739d6da75c31d66494f0b0f
child 489237 3d16da502fd03d120906a55e7b9159ad0a27cc25
push id46769
push userahalberstadt@mozilla.com
push dateFri, 24 Feb 2017 14:12:11 +0000
reviewersdustin
bugs1336559
milestone54.0a1
Bug 1336559 - Refactor keyed-by matching algorithm into standalone utility function, r?dustin MozReview-Commit-ID: Jqyhbj7nC6z
taskcluster/taskgraph/util/attributes.py
taskcluster/taskgraph/util/schema.py
--- a/taskcluster/taskgraph/util/attributes.py
+++ b/taskcluster/taskgraph/util/attributes.py
@@ -1,12 +1,15 @@
 # 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/.
 
+import re
+
+
 INTEGRATION_PROJECTS = set([
     'mozilla-inbound',
     'autoland',
 ])
 
 RELEASE_PROJECTS = set([
     'mozilla-central',
     'mozilla-aurora',
@@ -33,16 +36,37 @@ def attrmatch(attributes, **kwargs):
         elif callable(kwval):
             if not kwval(attval):
                 return False
         elif kwval != attributes[kwkey]:
             return False
     return True
 
 
+def keymatch(attributes, target):
+    """Determine if any keys in attributes are a match to target, then return
+    a list of matching values. First exact matches will be checked. Failing
+    that, regex matches and finally a default key.
+    """
+    # exact match
+    if target in attributes:
+        return [attributes[target]]
+
+    # regular expression match
+    matches = [v for k, v in attributes.iteritems() if re.match(k + '$', target)]
+    if matches:
+        return matches
+
+    # default
+    if 'default' in attributes:
+        return [attributes['default']]
+
+    return []
+
+
 def match_run_on_projects(project, run_on_projects):
     """Determine whether the given project is included in the `run-on-projects`
     parameter, applying expansions for things like "integration" mentioned in
     the attribute documentation."""
     if 'all' in run_on_projects:
         return True
     if 'integration' in run_on_projects:
         if project in INTEGRATION_PROJECTS:
--- a/taskcluster/taskgraph/util/schema.py
+++ b/taskcluster/taskgraph/util/schema.py
@@ -1,19 +1,20 @@
 # 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 absolute_import, print_function, unicode_literals
 
-import re
 import copy
 import pprint
 import voluptuous
 
+from .attributes import keymatch
+
 
 def validate_schema(schema, obj, msg_prefix):
     """
     Validate that object satisfies schema.  If not, generate a useful exception
     beginning with msg_prefix.
     """
     try:
         # deep copy the result since it may include mutable defaults
@@ -107,32 +108,21 @@ def resolve_keyed_by(item, field, item_n
     while True:
         if not isinstance(value, dict) or len(value) != 1 or not value.keys()[0].startswith('by-'):
             return item
 
         keyed_by = value.keys()[0][3:]  # strip off 'by-' prefix
         key = extra_values.get(keyed_by) if keyed_by in extra_values else item[keyed_by]
         alternatives = value.values()[0]
 
-        # exact match
-        if key in alternatives:
-            value = container[subfield] = alternatives[key]
-            continue
-
-        # regular expression match
-        matches = [(k, v) for k, v in alternatives.iteritems() if re.match(k + '$', key)]
+        matches = keymatch(alternatives, key)
         if len(matches) > 1:
             raise Exception(
                 "Multiple matching values for {} {!r} found while "
                 "determining item {} in {}".format(
                     keyed_by, key, field, item_name))
         elif matches:
-            value = container[subfield] = matches[0][1]
-            continue
-
-        # default
-        if 'default' in alternatives:
-            value = container[subfield] = alternatives['default']
+            value = container[subfield] = matches[0]
             continue
 
         raise Exception(
             "No {} matching {!r} nor 'default' found while determining item {} in {}".format(
                 keyed_by, key, field, item_name))