1# This file is part of Buildbot. Buildbot is free software: you can 2# redistribute it and/or modify it under the terms of the GNU General Public 3# License as published by the Free Software Foundation, version 2. 4# 5# This program is distributed in the hope that it will be useful, but WITHOUT 6# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 7# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 8# details. 9# 10# You should have received a copy of the GNU General Public License along with 11# this program; if not, write to the Free Software Foundation, Inc., 51 12# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 13# 14# Copyright Buildbot Team Members 15 16import re 17 18_ident_re = re.compile('^[a-zA-Z_-][.a-zA-Z0-9_-]*$') 19 20 21def ident(x): 22 if _ident_re.match(x): 23 return x 24 raise TypeError 25 26 27class Matcher: 28 29 def __init__(self): 30 self._patterns = {} 31 self._dirty = True 32 33 def __setitem__(self, path, value): 34 assert path not in self._patterns, "duplicate path {}".format(path) 35 self._patterns[path] = value 36 self._dirty = True 37 38 def __repr__(self): 39 return '<Matcher %r>' % (self._patterns,) 40 41 path_elt_re = re.compile('^(.?):([a-z0-9_.]+)$') 42 type_fns = dict(n=int, i=ident) 43 44 def __getitem__(self, path): 45 if self._dirty: 46 self._compile() 47 48 patterns = self._by_length.get(len(path), {}) 49 for pattern in patterns: 50 kwargs = {} 51 for pattern_elt, path_elt in zip(pattern, path): 52 mo = self.path_elt_re.match(pattern_elt) 53 if mo: 54 type_flag, arg_name = mo.groups() 55 if type_flag: 56 try: 57 type_fn = self.type_fns[type_flag] 58 except Exception: 59 assert type_flag in self.type_fns, \ 60 "no such type flag {}".format(type_flag) 61 try: 62 path_elt = type_fn(path_elt) 63 except Exception: 64 break 65 kwargs[arg_name] = path_elt 66 else: 67 if pattern_elt != path_elt: 68 break 69 else: 70 # complete match 71 return patterns[pattern], kwargs 72 else: 73 raise KeyError('No match for %r' % (path,)) 74 75 def iterPatterns(self): 76 return list(self._patterns.items()) 77 78 def _compile(self): 79 self._by_length = {} 80 for k, v in self.iterPatterns(): 81 length = len(k) 82 self._by_length.setdefault(length, {})[k] = v 83