comparison mercurial/match.py @ 43076:2372284d9457

formatting: blacken the codebase This is using my patch to black (https://github.com/psf/black/pull/826) so we don't un-wrap collection literals. Done with: hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S # skip-blame mass-reformatting only # no-check-commit reformats foo_bar functions Differential Revision: https://phab.mercurial-scm.org/D6971
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:45:02 -0400
parents 72890d8f9860
children 687b865b95ad
comparison
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
19 pathutil, 19 pathutil,
20 policy, 20 policy,
21 pycompat, 21 pycompat,
22 util, 22 util,
23 ) 23 )
24 from .utils import ( 24 from .utils import stringutil
25 stringutil, 25
26 rustmod = policy.importrust(r'filepatterns')
27
28 allpatternkinds = (
29 're',
30 'glob',
31 'path',
32 'relglob',
33 'relpath',
34 'relre',
35 'rootglob',
36 'listfile',
37 'listfile0',
38 'set',
39 'include',
40 'subinclude',
41 'rootfilesin',
26 ) 42 )
27
28 rustmod = policy.importrust(r'filepatterns')
29
30 allpatternkinds = ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
31 'rootglob',
32 'listfile', 'listfile0', 'set', 'include', 'subinclude',
33 'rootfilesin')
34 cwdrelativepatternkinds = ('relpath', 'glob') 43 cwdrelativepatternkinds = ('relpath', 'glob')
35 44
36 propertycache = util.propertycache 45 propertycache = util.propertycache
46
37 47
38 def _rematcher(regex): 48 def _rematcher(regex):
39 '''compile the regexp with the best available regexp engine and return a 49 '''compile the regexp with the best available regexp engine and return a
40 matcher function''' 50 matcher function'''
41 m = util.re.compile(regex) 51 m = util.re.compile(regex)
43 # slightly faster, provided by facebook's re2 bindings 53 # slightly faster, provided by facebook's re2 bindings
44 return m.test_match 54 return m.test_match
45 except AttributeError: 55 except AttributeError:
46 return m.match 56 return m.match
47 57
58
48 def _expandsets(kindpats, ctx=None, listsubrepos=False, badfn=None): 59 def _expandsets(kindpats, ctx=None, listsubrepos=False, badfn=None):
49 '''Returns the kindpats list with the 'set' patterns expanded to matchers''' 60 '''Returns the kindpats list with the 'set' patterns expanded to matchers'''
50 matchers = [] 61 matchers = []
51 other = [] 62 other = []
52 63
53 for kind, pat, source in kindpats: 64 for kind, pat, source in kindpats:
54 if kind == 'set': 65 if kind == 'set':
55 if ctx is None: 66 if ctx is None:
56 raise error.ProgrammingError("fileset expression with no " 67 raise error.ProgrammingError(
57 "context") 68 "fileset expression with no " "context"
69 )
58 matchers.append(ctx.matchfileset(pat, badfn=badfn)) 70 matchers.append(ctx.matchfileset(pat, badfn=badfn))
59 71
60 if listsubrepos: 72 if listsubrepos:
61 for subpath in ctx.substate: 73 for subpath in ctx.substate:
62 sm = ctx.sub(subpath).matchfileset(pat, badfn=badfn) 74 sm = ctx.sub(subpath).matchfileset(pat, badfn=badfn)
64 matchers.append(pm) 76 matchers.append(pm)
65 77
66 continue 78 continue
67 other.append((kind, pat, source)) 79 other.append((kind, pat, source))
68 return matchers, other 80 return matchers, other
81
69 82
70 def _expandsubinclude(kindpats, root): 83 def _expandsubinclude(kindpats, root):
71 '''Returns the list of subinclude matcher args and the kindpats without the 84 '''Returns the list of subinclude matcher args and the kindpats without the
72 subincludes in it.''' 85 subincludes in it.'''
73 relmatchers = [] 86 relmatchers = []
88 relmatchers.append((prefix, matcherargs)) 101 relmatchers.append((prefix, matcherargs))
89 else: 102 else:
90 other.append((kind, pat, source)) 103 other.append((kind, pat, source))
91 104
92 return relmatchers, other 105 return relmatchers, other
106
93 107
94 def _kindpatsalwaysmatch(kindpats): 108 def _kindpatsalwaysmatch(kindpats):
95 """"Checks whether the kindspats match everything, as e.g. 109 """"Checks whether the kindspats match everything, as e.g.
96 'relpath:.' does. 110 'relpath:.' does.
97 """ 111 """
98 for kind, pat, source in kindpats: 112 for kind, pat, source in kindpats:
99 if pat != '' or kind not in ['relpath', 'glob']: 113 if pat != '' or kind not in ['relpath', 'glob']:
100 return False 114 return False
101 return True 115 return True
102 116
103 def _buildkindpatsmatcher(matchercls, root, kindpats, ctx=None, 117
104 listsubrepos=False, badfn=None): 118 def _buildkindpatsmatcher(
119 matchercls, root, kindpats, ctx=None, listsubrepos=False, badfn=None
120 ):
105 matchers = [] 121 matchers = []
106 fms, kindpats = _expandsets(kindpats, ctx=ctx, 122 fms, kindpats = _expandsets(
107 listsubrepos=listsubrepos, badfn=badfn) 123 kindpats, ctx=ctx, listsubrepos=listsubrepos, badfn=badfn
124 )
108 if kindpats: 125 if kindpats:
109 m = matchercls(root, kindpats, badfn=badfn) 126 m = matchercls(root, kindpats, badfn=badfn)
110 matchers.append(m) 127 matchers.append(m)
111 if fms: 128 if fms:
112 matchers.extend(fms) 129 matchers.extend(fms)
114 return nevermatcher(badfn=badfn) 131 return nevermatcher(badfn=badfn)
115 if len(matchers) == 1: 132 if len(matchers) == 1:
116 return matchers[0] 133 return matchers[0]
117 return unionmatcher(matchers) 134 return unionmatcher(matchers)
118 135
119 def match(root, cwd, patterns=None, include=None, exclude=None, default='glob', 136
120 auditor=None, ctx=None, listsubrepos=False, warn=None, 137 def match(
121 badfn=None, icasefs=False): 138 root,
139 cwd,
140 patterns=None,
141 include=None,
142 exclude=None,
143 default='glob',
144 auditor=None,
145 ctx=None,
146 listsubrepos=False,
147 warn=None,
148 badfn=None,
149 icasefs=False,
150 ):
122 r"""build an object to match a set of file patterns 151 r"""build an object to match a set of file patterns
123 152
124 arguments: 153 arguments:
125 root - the canonical root of the tree you're matching against 154 root - the canonical root of the tree you're matching against
126 cwd - the current working directory, if relevant 155 cwd - the current working directory, if relevant
221 if patterns: 250 if patterns:
222 kindpats = normalize(patterns, default, root, cwd, auditor, warn) 251 kindpats = normalize(patterns, default, root, cwd, auditor, warn)
223 if _kindpatsalwaysmatch(kindpats): 252 if _kindpatsalwaysmatch(kindpats):
224 m = alwaysmatcher(badfn) 253 m = alwaysmatcher(badfn)
225 else: 254 else:
226 m = _buildkindpatsmatcher(patternmatcher, root, kindpats, ctx=ctx, 255 m = _buildkindpatsmatcher(
227 listsubrepos=listsubrepos, badfn=badfn) 256 patternmatcher,
257 root,
258 kindpats,
259 ctx=ctx,
260 listsubrepos=listsubrepos,
261 badfn=badfn,
262 )
228 else: 263 else:
229 # It's a little strange that no patterns means to match everything. 264 # It's a little strange that no patterns means to match everything.
230 # Consider changing this to match nothing (probably using nevermatcher). 265 # Consider changing this to match nothing (probably using nevermatcher).
231 m = alwaysmatcher(badfn) 266 m = alwaysmatcher(badfn)
232 267
233 if include: 268 if include:
234 kindpats = normalize(include, 'glob', root, cwd, auditor, warn) 269 kindpats = normalize(include, 'glob', root, cwd, auditor, warn)
235 im = _buildkindpatsmatcher(includematcher, root, kindpats, ctx=ctx, 270 im = _buildkindpatsmatcher(
236 listsubrepos=listsubrepos, badfn=None) 271 includematcher,
272 root,
273 kindpats,
274 ctx=ctx,
275 listsubrepos=listsubrepos,
276 badfn=None,
277 )
237 m = intersectmatchers(m, im) 278 m = intersectmatchers(m, im)
238 if exclude: 279 if exclude:
239 kindpats = normalize(exclude, 'glob', root, cwd, auditor, warn) 280 kindpats = normalize(exclude, 'glob', root, cwd, auditor, warn)
240 em = _buildkindpatsmatcher(includematcher, root, kindpats, ctx=ctx, 281 em = _buildkindpatsmatcher(
241 listsubrepos=listsubrepos, badfn=None) 282 includematcher,
283 root,
284 kindpats,
285 ctx=ctx,
286 listsubrepos=listsubrepos,
287 badfn=None,
288 )
242 m = differencematcher(m, em) 289 m = differencematcher(m, em)
243 return m 290 return m
244 291
292
245 def exact(files, badfn=None): 293 def exact(files, badfn=None):
246 return exactmatcher(files, badfn=badfn) 294 return exactmatcher(files, badfn=badfn)
247 295
296
248 def always(badfn=None): 297 def always(badfn=None):
249 return alwaysmatcher(badfn) 298 return alwaysmatcher(badfn)
250 299
300
251 def never(badfn=None): 301 def never(badfn=None):
252 return nevermatcher(badfn) 302 return nevermatcher(badfn)
303
253 304
254 def badmatch(match, badfn): 305 def badmatch(match, badfn):
255 """Make a copy of the given matcher, replacing its bad method with the given 306 """Make a copy of the given matcher, replacing its bad method with the given
256 one. 307 one.
257 """ 308 """
258 m = copy.copy(match) 309 m = copy.copy(match)
259 m.bad = badfn 310 m.bad = badfn
260 return m 311 return m
312
261 313
262 def _donormalize(patterns, default, root, cwd, auditor=None, warn=None): 314 def _donormalize(patterns, default, root, cwd, auditor=None, warn=None):
263 '''Convert 'kind:pat' from the patterns list to tuples with kind and 315 '''Convert 'kind:pat' from the patterns list to tuples with kind and
264 normalized and rooted patterns and with listfiles expanded.''' 316 normalized and rooted patterns and with listfiles expanded.'''
265 kindpats = [] 317 kindpats = []
276 else: 328 else:
277 files = files.splitlines() 329 files = files.splitlines()
278 files = [f for f in files if f] 330 files = [f for f in files if f]
279 except EnvironmentError: 331 except EnvironmentError:
280 raise error.Abort(_("unable to read file list (%s)") % pat) 332 raise error.Abort(_("unable to read file list (%s)") % pat)
281 for k, p, source in _donormalize(files, default, root, cwd, 333 for k, p, source in _donormalize(
282 auditor, warn): 334 files, default, root, cwd, auditor, warn
335 ):
283 kindpats.append((k, p, pat)) 336 kindpats.append((k, p, pat))
284 continue 337 continue
285 elif kind == 'include': 338 elif kind == 'include':
286 try: 339 try:
287 fullpath = os.path.join(root, util.localpath(pat)) 340 fullpath = os.path.join(root, util.localpath(pat))
288 includepats = readpatternfile(fullpath, warn) 341 includepats = readpatternfile(fullpath, warn)
289 for k, p, source in _donormalize(includepats, default, 342 for k, p, source in _donormalize(
290 root, cwd, auditor, warn): 343 includepats, default, root, cwd, auditor, warn
344 ):
291 kindpats.append((k, p, source or pat)) 345 kindpats.append((k, p, source or pat))
292 except error.Abort as inst: 346 except error.Abort as inst:
293 raise error.Abort('%s: %s' % (pat, inst[0])) 347 raise error.Abort('%s: %s' % (pat, inst[0]))
294 except IOError as inst: 348 except IOError as inst:
295 if warn: 349 if warn:
296 warn(_("skipping unreadable pattern file '%s': %s\n") % 350 warn(
297 (pat, stringutil.forcebytestr(inst.strerror))) 351 _("skipping unreadable pattern file '%s': %s\n")
352 % (pat, stringutil.forcebytestr(inst.strerror))
353 )
298 continue 354 continue
299 # else: re or relre - which cannot be normalized 355 # else: re or relre - which cannot be normalized
300 kindpats.append((kind, pat, '')) 356 kindpats.append((kind, pat, ''))
301 return kindpats 357 return kindpats
302 358
359
303 class basematcher(object): 360 class basematcher(object):
304
305 def __init__(self, badfn=None): 361 def __init__(self, badfn=None):
306 if badfn is not None: 362 if badfn is not None:
307 self.bad = badfn 363 self.bad = badfn
308 364
309 def __call__(self, fn): 365 def __call__(self, fn):
310 return self.matchfn(fn) 366 return self.matchfn(fn)
367
311 # Callbacks related to how the matcher is used by dirstate.walk. 368 # Callbacks related to how the matcher is used by dirstate.walk.
312 # Subscribers to these events must monkeypatch the matcher object. 369 # Subscribers to these events must monkeypatch the matcher object.
313 def bad(self, f, msg): 370 def bad(self, f, msg):
314 '''Callback from dirstate.walk for each explicit file that can't be 371 '''Callback from dirstate.walk for each explicit file that can't be
315 found/accessed, with an error message.''' 372 found/accessed, with an error message.'''
417 def anypats(self): 474 def anypats(self):
418 '''None of .always(), .isexact(), and .prefix() is true -- 475 '''None of .always(), .isexact(), and .prefix() is true --
419 optimizations will be difficult.''' 476 optimizations will be difficult.'''
420 return not self.always() and not self.isexact() and not self.prefix() 477 return not self.always() and not self.isexact() and not self.prefix()
421 478
479
422 class alwaysmatcher(basematcher): 480 class alwaysmatcher(basematcher):
423 '''Matches everything.''' 481 '''Matches everything.'''
424 482
425 def __init__(self, badfn=None): 483 def __init__(self, badfn=None):
426 super(alwaysmatcher, self).__init__(badfn) 484 super(alwaysmatcher, self).__init__(badfn)
437 def visitchildrenset(self, dir): 495 def visitchildrenset(self, dir):
438 return 'all' 496 return 'all'
439 497
440 def __repr__(self): 498 def __repr__(self):
441 return r'<alwaysmatcher>' 499 return r'<alwaysmatcher>'
500
442 501
443 class nevermatcher(basematcher): 502 class nevermatcher(basematcher):
444 '''Matches nothing.''' 503 '''Matches nothing.'''
445 504
446 def __init__(self, badfn=None): 505 def __init__(self, badfn=None):
464 return set() 523 return set()
465 524
466 def __repr__(self): 525 def __repr__(self):
467 return r'<nevermatcher>' 526 return r'<nevermatcher>'
468 527
528
469 class predicatematcher(basematcher): 529 class predicatematcher(basematcher):
470 """A matcher adapter for a simple boolean function""" 530 """A matcher adapter for a simple boolean function"""
471 531
472 def __init__(self, predfn, predrepr=None, badfn=None): 532 def __init__(self, predfn, predrepr=None, badfn=None):
473 super(predicatematcher, self).__init__(badfn) 533 super(predicatematcher, self).__init__(badfn)
474 self.matchfn = predfn 534 self.matchfn = predfn
475 self._predrepr = predrepr 535 self._predrepr = predrepr
476 536
477 @encoding.strmethod 537 @encoding.strmethod
478 def __repr__(self): 538 def __repr__(self):
479 s = (stringutil.buildrepr(self._predrepr) 539 s = stringutil.buildrepr(self._predrepr) or pycompat.byterepr(
480 or pycompat.byterepr(self.matchfn)) 540 self.matchfn
541 )
481 return '<predicatenmatcher pred=%s>' % s 542 return '<predicatenmatcher pred=%s>' % s
543
482 544
483 def normalizerootdir(dir, funcname): 545 def normalizerootdir(dir, funcname):
484 if dir == '.': 546 if dir == '.':
485 util.nouideprecwarn("match.%s() no longer accepts " 547 util.nouideprecwarn(
486 "'.', use '' instead." % funcname, '5.1') 548 "match.%s() no longer accepts " "'.', use '' instead." % funcname,
549 '5.1',
550 )
487 return '' 551 return ''
488 return dir 552 return dir
489 553
490 554
491 class patternmatcher(basematcher): 555 class patternmatcher(basematcher):
534 598
535 def visitdir(self, dir): 599 def visitdir(self, dir):
536 dir = normalizerootdir(dir, 'visitdir') 600 dir = normalizerootdir(dir, 'visitdir')
537 if self._prefix and dir in self._fileset: 601 if self._prefix and dir in self._fileset:
538 return 'all' 602 return 'all'
539 return (dir in self._fileset or 603 return (
540 dir in self._dirs or 604 dir in self._fileset
541 any(parentdir in self._fileset 605 or dir in self._dirs
542 for parentdir in util.finddirs(dir))) 606 or any(
607 parentdir in self._fileset for parentdir in util.finddirs(dir)
608 )
609 )
543 610
544 def visitchildrenset(self, dir): 611 def visitchildrenset(self, dir):
545 ret = self.visitdir(dir) 612 ret = self.visitdir(dir)
546 if ret is True: 613 if ret is True:
547 return 'this' 614 return 'this'
553 def prefix(self): 620 def prefix(self):
554 return self._prefix 621 return self._prefix
555 622
556 @encoding.strmethod 623 @encoding.strmethod
557 def __repr__(self): 624 def __repr__(self):
558 return ('<patternmatcher patterns=%r>' % pycompat.bytestr(self._pats)) 625 return '<patternmatcher patterns=%r>' % pycompat.bytestr(self._pats)
626
559 627
560 # This is basically a reimplementation of util.dirs that stores the children 628 # This is basically a reimplementation of util.dirs that stores the children
561 # instead of just a count of them, plus a small optional optimization to avoid 629 # instead of just a count of them, plus a small optional optimization to avoid
562 # some directories we don't need. 630 # some directories we don't need.
563 class _dirchildren(object): 631 class _dirchildren(object):
586 # Unlike manifest._splittopdir, this does not suffix `dirname` with a 654 # Unlike manifest._splittopdir, this does not suffix `dirname` with a
587 # slash. 655 # slash.
588 oldpos = len(path) 656 oldpos = len(path)
589 pos = path.rfind('/') 657 pos = path.rfind('/')
590 while pos != -1: 658 while pos != -1:
591 yield path[:pos], path[pos + 1:oldpos] 659 yield path[:pos], path[pos + 1 : oldpos]
592 oldpos = pos 660 oldpos = pos
593 pos = path.rfind('/', 0, pos) 661 pos = path.rfind('/', 0, pos)
594 yield '', path[:oldpos] 662 yield '', path[:oldpos]
595 663
596 def get(self, path): 664 def get(self, path):
597 return self._dirs.get(path, set()) 665 return self._dirs.get(path, set())
598 666
667
599 class includematcher(basematcher): 668 class includematcher(basematcher):
600
601 def __init__(self, root, kindpats, badfn=None): 669 def __init__(self, root, kindpats, badfn=None):
602 super(includematcher, self).__init__(badfn) 670 super(includematcher, self).__init__(badfn)
603 671
604 self._pats, self.matchfn = _buildmatch(kindpats, '(?:/|$)', root) 672 self._pats, self.matchfn = _buildmatch(kindpats, '(?:/|$)', root)
605 self._prefix = _prefix(kindpats) 673 self._prefix = _prefix(kindpats)
614 682
615 def visitdir(self, dir): 683 def visitdir(self, dir):
616 dir = normalizerootdir(dir, 'visitdir') 684 dir = normalizerootdir(dir, 'visitdir')
617 if self._prefix and dir in self._roots: 685 if self._prefix and dir in self._roots:
618 return 'all' 686 return 'all'
619 return (dir in self._roots or 687 return (
620 dir in self._dirs or 688 dir in self._roots
621 dir in self._parents or 689 or dir in self._dirs
622 any(parentdir in self._roots 690 or dir in self._parents
623 for parentdir in util.finddirs(dir))) 691 or any(parentdir in self._roots for parentdir in util.finddirs(dir))
692 )
624 693
625 @propertycache 694 @propertycache
626 def _allparentschildren(self): 695 def _allparentschildren(self):
627 # It may seem odd that we add dirs, roots, and parents, and then 696 # It may seem odd that we add dirs, roots, and parents, and then
628 # restrict to only parents. This is to catch the case of: 697 # restrict to only parents. This is to catch the case of:
629 # dirs = ['foo/bar'] 698 # dirs = ['foo/bar']
630 # parents = ['foo'] 699 # parents = ['foo']
631 # if we asked for the children of 'foo', but had only added 700 # if we asked for the children of 'foo', but had only added
632 # self._parents, we wouldn't be able to respond ['bar']. 701 # self._parents, we wouldn't be able to respond ['bar'].
633 return _dirchildren( 702 return _dirchildren(
634 itertools.chain(self._dirs, self._roots, self._parents), 703 itertools.chain(self._dirs, self._roots, self._parents),
635 onlyinclude=self._parents) 704 onlyinclude=self._parents,
705 )
636 706
637 def visitchildrenset(self, dir): 707 def visitchildrenset(self, dir):
638 if self._prefix and dir in self._roots: 708 if self._prefix and dir in self._roots:
639 return 'all' 709 return 'all'
640 # Note: this does *not* include the 'dir in self._parents' case from 710 # Note: this does *not* include the 'dir in self._parents' case from
641 # visitdir, that's handled below. 711 # visitdir, that's handled below.
642 if ('' in self._roots or 712 if (
643 dir in self._roots or 713 '' in self._roots
644 dir in self._dirs or 714 or dir in self._roots
645 any(parentdir in self._roots 715 or dir in self._dirs
646 for parentdir in util.finddirs(dir))): 716 or any(parentdir in self._roots for parentdir in util.finddirs(dir))
717 ):
647 return 'this' 718 return 'this'
648 719
649 if dir in self._parents: 720 if dir in self._parents:
650 return self._allparentschildren.get(dir) or set() 721 return self._allparentschildren.get(dir) or set()
651 return set() 722 return set()
652 723
653 @encoding.strmethod 724 @encoding.strmethod
654 def __repr__(self): 725 def __repr__(self):
655 return ('<includematcher includes=%r>' % pycompat.bytestr(self._pats)) 726 return '<includematcher includes=%r>' % pycompat.bytestr(self._pats)
727
656 728
657 class exactmatcher(basematcher): 729 class exactmatcher(basematcher):
658 r'''Matches the input files exactly. They are interpreted as paths, not 730 r'''Matches the input files exactly. They are interpreted as paths, not
659 patterns (so no kind-prefixes). 731 patterns (so no kind-prefixes).
660 732
700 return set() 772 return set()
701 773
702 candidates = self._fileset | self._dirs - {''} 774 candidates = self._fileset | self._dirs - {''}
703 if dir != '': 775 if dir != '':
704 d = dir + '/' 776 d = dir + '/'
705 candidates = set(c[len(d):] for c in candidates if 777 candidates = set(c[len(d) :] for c in candidates if c.startswith(d))
706 c.startswith(d))
707 # self._dirs includes all of the directories, recursively, so if 778 # self._dirs includes all of the directories, recursively, so if
708 # we're attempting to match foo/bar/baz.txt, it'll have '', 'foo', 779 # we're attempting to match foo/bar/baz.txt, it'll have '', 'foo',
709 # 'foo/bar' in it. Thus we can safely ignore a candidate that has a 780 # 'foo/bar' in it. Thus we can safely ignore a candidate that has a
710 # '/' in it, indicating a it's for a subdir-of-a-subdir; the 781 # '/' in it, indicating a it's for a subdir-of-a-subdir; the
711 # immediate subdir will be in there without a slash. 782 # immediate subdir will be in there without a slash.
718 def isexact(self): 789 def isexact(self):
719 return True 790 return True
720 791
721 @encoding.strmethod 792 @encoding.strmethod
722 def __repr__(self): 793 def __repr__(self):
723 return ('<exactmatcher files=%r>' % self._files) 794 return '<exactmatcher files=%r>' % self._files
795
724 796
725 class differencematcher(basematcher): 797 class differencematcher(basematcher):
726 '''Composes two matchers by matching if the first matches and the second 798 '''Composes two matchers by matching if the first matches and the second
727 does not. 799 does not.
728 800
729 The second matcher's non-matching-attributes (bad, explicitdir, 801 The second matcher's non-matching-attributes (bad, explicitdir,
730 traversedir) are ignored. 802 traversedir) are ignored.
731 ''' 803 '''
804
732 def __init__(self, m1, m2): 805 def __init__(self, m1, m2):
733 super(differencematcher, self).__init__() 806 super(differencematcher, self).__init__()
734 self._m1 = m1 807 self._m1 = m1
735 self._m2 = m2 808 self._m2 = m2
736 self.bad = m1.bad 809 self.bad = m1.bad
787 def isexact(self): 860 def isexact(self):
788 return self._m1.isexact() 861 return self._m1.isexact()
789 862
790 @encoding.strmethod 863 @encoding.strmethod
791 def __repr__(self): 864 def __repr__(self):
792 return ('<differencematcher m1=%r, m2=%r>' % (self._m1, self._m2)) 865 return '<differencematcher m1=%r, m2=%r>' % (self._m1, self._m2)
866
793 867
794 def intersectmatchers(m1, m2): 868 def intersectmatchers(m1, m2):
795 '''Composes two matchers by matching if both of them match. 869 '''Composes two matchers by matching if both of them match.
796 870
797 The second matcher's non-matching-attributes (bad, explicitdir, 871 The second matcher's non-matching-attributes (bad, explicitdir,
810 if m2.always(): 884 if m2.always():
811 m = copy.copy(m1) 885 m = copy.copy(m1)
812 return m 886 return m
813 return intersectionmatcher(m1, m2) 887 return intersectionmatcher(m1, m2)
814 888
889
815 class intersectionmatcher(basematcher): 890 class intersectionmatcher(basematcher):
816 def __init__(self, m1, m2): 891 def __init__(self, m1, m2):
817 super(intersectionmatcher, self).__init__() 892 super(intersectionmatcher, self).__init__()
818 self._m1 = m1 893 self._m1 = m1
819 self._m2 = m2 894 self._m2 = m2
869 def isexact(self): 944 def isexact(self):
870 return self._m1.isexact() or self._m2.isexact() 945 return self._m1.isexact() or self._m2.isexact()
871 946
872 @encoding.strmethod 947 @encoding.strmethod
873 def __repr__(self): 948 def __repr__(self):
874 return ('<intersectionmatcher m1=%r, m2=%r>' % (self._m1, self._m2)) 949 return '<intersectionmatcher m1=%r, m2=%r>' % (self._m1, self._m2)
950
875 951
876 class subdirmatcher(basematcher): 952 class subdirmatcher(basematcher):
877 """Adapt a matcher to work on a subdirectory only. 953 """Adapt a matcher to work on a subdirectory only.
878 954
879 The paths are remapped to remove/insert the path as needed: 955 The paths are remapped to remove/insert the path as needed:
904 super(subdirmatcher, self).__init__() 980 super(subdirmatcher, self).__init__()
905 self._path = path 981 self._path = path
906 self._matcher = matcher 982 self._matcher = matcher
907 self._always = matcher.always() 983 self._always = matcher.always()
908 984
909 self._files = [f[len(path) + 1:] for f in matcher._files 985 self._files = [
910 if f.startswith(path + "/")] 986 f[len(path) + 1 :]
987 for f in matcher._files
988 if f.startswith(path + "/")
989 ]
911 990
912 # If the parent repo had a path to this subrepo and the matcher is 991 # If the parent repo had a path to this subrepo and the matcher is
913 # a prefix matcher, this submatcher always matches. 992 # a prefix matcher, this submatcher always matches.
914 if matcher.prefix(): 993 if matcher.prefix():
915 self._always = any(f == path for f in matcher._files) 994 self._always = any(f == path for f in matcher._files)
946 def prefix(self): 1025 def prefix(self):
947 return self._matcher.prefix() and not self._always 1026 return self._matcher.prefix() and not self._always
948 1027
949 @encoding.strmethod 1028 @encoding.strmethod
950 def __repr__(self): 1029 def __repr__(self):
951 return ('<subdirmatcher path=%r, matcher=%r>' % 1030 return '<subdirmatcher path=%r, matcher=%r>' % (
952 (self._path, self._matcher)) 1031 self._path,
1032 self._matcher,
1033 )
1034
953 1035
954 class prefixdirmatcher(basematcher): 1036 class prefixdirmatcher(basematcher):
955 """Adapt a matcher to work on a parent directory. 1037 """Adapt a matcher to work on a parent directory.
956 1038
957 The matcher's non-matching-attributes (bad, explicitdir, traversedir) are 1039 The matcher's non-matching-attributes (bad, explicitdir, traversedir) are
997 return [self._pathprefix + f for f in self._matcher._files] 1079 return [self._pathprefix + f for f in self._matcher._files]
998 1080
999 def matchfn(self, f): 1081 def matchfn(self, f):
1000 if not f.startswith(self._pathprefix): 1082 if not f.startswith(self._pathprefix):
1001 return False 1083 return False
1002 return self._matcher.matchfn(f[len(self._pathprefix):]) 1084 return self._matcher.matchfn(f[len(self._pathprefix) :])
1003 1085
1004 @propertycache 1086 @propertycache
1005 def _pathdirs(self): 1087 def _pathdirs(self):
1006 return set(util.finddirs(self._path)) 1088 return set(util.finddirs(self._path))
1007 1089
1008 def visitdir(self, dir): 1090 def visitdir(self, dir):
1009 if dir == self._path: 1091 if dir == self._path:
1010 return self._matcher.visitdir('') 1092 return self._matcher.visitdir('')
1011 if dir.startswith(self._pathprefix): 1093 if dir.startswith(self._pathprefix):
1012 return self._matcher.visitdir(dir[len(self._pathprefix):]) 1094 return self._matcher.visitdir(dir[len(self._pathprefix) :])
1013 return dir in self._pathdirs 1095 return dir in self._pathdirs
1014 1096
1015 def visitchildrenset(self, dir): 1097 def visitchildrenset(self, dir):
1016 if dir == self._path: 1098 if dir == self._path:
1017 return self._matcher.visitchildrenset('') 1099 return self._matcher.visitchildrenset('')
1018 if dir.startswith(self._pathprefix): 1100 if dir.startswith(self._pathprefix):
1019 return self._matcher.visitchildrenset(dir[len(self._pathprefix):]) 1101 return self._matcher.visitchildrenset(dir[len(self._pathprefix) :])
1020 if dir in self._pathdirs: 1102 if dir in self._pathdirs:
1021 return 'this' 1103 return 'this'
1022 return set() 1104 return set()
1023 1105
1024 def isexact(self): 1106 def isexact(self):
1027 def prefix(self): 1109 def prefix(self):
1028 return self._matcher.prefix() 1110 return self._matcher.prefix()
1029 1111
1030 @encoding.strmethod 1112 @encoding.strmethod
1031 def __repr__(self): 1113 def __repr__(self):
1032 return ('<prefixdirmatcher path=%r, matcher=%r>' 1114 return '<prefixdirmatcher path=%r, matcher=%r>' % (
1033 % (pycompat.bytestr(self._path), self._matcher)) 1115 pycompat.bytestr(self._path),
1116 self._matcher,
1117 )
1118
1034 1119
1035 class unionmatcher(basematcher): 1120 class unionmatcher(basematcher):
1036 """A matcher that is the union of several matchers. 1121 """A matcher that is the union of several matchers.
1037 1122
1038 The non-matching-attributes (bad, explicitdir, traversedir) are taken from 1123 The non-matching-attributes (bad, explicitdir, traversedir) are taken from
1080 return 'this' 1165 return 'this'
1081 return r 1166 return r
1082 1167
1083 @encoding.strmethod 1168 @encoding.strmethod
1084 def __repr__(self): 1169 def __repr__(self):
1085 return ('<unionmatcher matchers=%r>' % self._matchers) 1170 return '<unionmatcher matchers=%r>' % self._matchers
1171
1086 1172
1087 def patkind(pattern, default=None): 1173 def patkind(pattern, default=None):
1088 '''If pattern is 'kind:pat' with a known kind, return kind. 1174 '''If pattern is 'kind:pat' with a known kind, return kind.
1089 1175
1090 >>> patkind(br're:.*\.c$') 1176 >>> patkind(br're:.*\.c$')
1097 >>> patkind(b'main.py', default=b're') 1183 >>> patkind(b'main.py', default=b're')
1098 're' 1184 're'
1099 ''' 1185 '''
1100 return _patsplit(pattern, default)[0] 1186 return _patsplit(pattern, default)[0]
1101 1187
1188
1102 def _patsplit(pattern, default): 1189 def _patsplit(pattern, default):
1103 """Split a string into the optional pattern kind prefix and the actual 1190 """Split a string into the optional pattern kind prefix and the actual
1104 pattern.""" 1191 pattern."""
1105 if ':' in pattern: 1192 if ':' in pattern:
1106 kind, pat = pattern.split(':', 1) 1193 kind, pat = pattern.split(':', 1)
1107 if kind in allpatternkinds: 1194 if kind in allpatternkinds:
1108 return kind, pat 1195 return kind, pat
1109 return default, pattern 1196 return default, pattern
1197
1110 1198
1111 def _globre(pat): 1199 def _globre(pat):
1112 r'''Convert an extended glob string to a regexp string. 1200 r'''Convert an extended glob string to a regexp string.
1113 1201
1114 >>> from . import pycompat 1202 >>> from . import pycompat
1133 ''' 1221 '''
1134 i, n = 0, len(pat) 1222 i, n = 0, len(pat)
1135 res = '' 1223 res = ''
1136 group = 0 1224 group = 0
1137 escape = util.stringutil.regexbytesescapemap.get 1225 escape = util.stringutil.regexbytesescapemap.get
1226
1138 def peek(): 1227 def peek():
1139 return i < n and pat[i:i + 1] 1228 return i < n and pat[i : i + 1]
1229
1140 while i < n: 1230 while i < n:
1141 c = pat[i:i + 1] 1231 c = pat[i : i + 1]
1142 i += 1 1232 i += 1
1143 if c not in '*?[{},\\': 1233 if c not in '*?[{},\\':
1144 res += escape(c, c) 1234 res += escape(c, c)
1145 elif c == '*': 1235 elif c == '*':
1146 if peek() == '*': 1236 if peek() == '*':
1154 res += '[^/]*' 1244 res += '[^/]*'
1155 elif c == '?': 1245 elif c == '?':
1156 res += '.' 1246 res += '.'
1157 elif c == '[': 1247 elif c == '[':
1158 j = i 1248 j = i
1159 if j < n and pat[j:j + 1] in '!]': 1249 if j < n and pat[j : j + 1] in '!]':
1160 j += 1 1250 j += 1
1161 while j < n and pat[j:j + 1] != ']': 1251 while j < n and pat[j : j + 1] != ']':
1162 j += 1 1252 j += 1
1163 if j >= n: 1253 if j >= n:
1164 res += '\\[' 1254 res += '\\['
1165 else: 1255 else:
1166 stuff = pat[i:j].replace('\\','\\\\') 1256 stuff = pat[i:j].replace('\\', '\\\\')
1167 i = j + 1 1257 i = j + 1
1168 if stuff[0:1] == '!': 1258 if stuff[0:1] == '!':
1169 stuff = '^' + stuff[1:] 1259 stuff = '^' + stuff[1:]
1170 elif stuff[0:1] == '^': 1260 elif stuff[0:1] == '^':
1171 stuff = '\\' + stuff 1261 stuff = '\\' + stuff
1187 res += escape(c, c) 1277 res += escape(c, c)
1188 else: 1278 else:
1189 res += escape(c, c) 1279 res += escape(c, c)
1190 return res 1280 return res
1191 1281
1282
1192 def _regex(kind, pat, globsuffix): 1283 def _regex(kind, pat, globsuffix):
1193 '''Convert a (normalized) pattern of any kind into a 1284 '''Convert a (normalized) pattern of any kind into a
1194 regular expression. 1285 regular expression.
1195 globsuffix is appended to the regexp of globs.''' 1286 globsuffix is appended to the regexp of globs.'''
1196 1287
1197 if rustmod is not None: 1288 if rustmod is not None:
1198 try: 1289 try:
1199 return rustmod.build_single_regex( 1290 return rustmod.build_single_regex(kind, pat, globsuffix)
1200 kind,
1201 pat,
1202 globsuffix
1203 )
1204 except rustmod.PatternError: 1291 except rustmod.PatternError:
1205 raise error.ProgrammingError( 1292 raise error.ProgrammingError(
1206 'not a regex pattern: %s:%s' % (kind, pat) 1293 'not a regex pattern: %s:%s' % (kind, pat)
1207 ) 1294 )
1208 1295
1225 if kind == 'relglob': 1312 if kind == 'relglob':
1226 globre = _globre(pat) 1313 globre = _globre(pat)
1227 if globre.startswith('[^/]*'): 1314 if globre.startswith('[^/]*'):
1228 # When pat has the form *XYZ (common), make the returned regex more 1315 # When pat has the form *XYZ (common), make the returned regex more
1229 # legible by returning the regex for **XYZ instead of **/*XYZ. 1316 # legible by returning the regex for **XYZ instead of **/*XYZ.
1230 return '.*' + globre[len('[^/]*'):] + globsuffix 1317 return '.*' + globre[len('[^/]*') :] + globsuffix
1231 return '(?:|.*/)' + globre + globsuffix 1318 return '(?:|.*/)' + globre + globsuffix
1232 if kind == 'relre': 1319 if kind == 'relre':
1233 if pat.startswith('^'): 1320 if pat.startswith('^'):
1234 return pat 1321 return pat
1235 return '.*' + pat 1322 return '.*' + pat
1236 if kind in ('glob', 'rootglob'): 1323 if kind in ('glob', 'rootglob'):
1237 return _globre(pat) + globsuffix 1324 return _globre(pat) + globsuffix
1238 raise error.ProgrammingError('not a regex pattern: %s:%s' % (kind, pat)) 1325 raise error.ProgrammingError('not a regex pattern: %s:%s' % (kind, pat))
1239 1326
1327
1240 def _buildmatch(kindpats, globsuffix, root): 1328 def _buildmatch(kindpats, globsuffix, root):
1241 '''Return regexp string and a matcher function for kindpats. 1329 '''Return regexp string and a matcher function for kindpats.
1242 globsuffix is appended to the regexp of globs.''' 1330 globsuffix is appended to the regexp of globs.'''
1243 matchfuncs = [] 1331 matchfuncs = []
1244 1332
1245 subincludes, kindpats = _expandsubinclude(kindpats, root) 1333 subincludes, kindpats = _expandsubinclude(kindpats, root)
1246 if subincludes: 1334 if subincludes:
1247 submatchers = {} 1335 submatchers = {}
1336
1248 def matchsubinclude(f): 1337 def matchsubinclude(f):
1249 for prefix, matcherargs in subincludes: 1338 for prefix, matcherargs in subincludes:
1250 if f.startswith(prefix): 1339 if f.startswith(prefix):
1251 mf = submatchers.get(prefix) 1340 mf = submatchers.get(prefix)
1252 if mf is None: 1341 if mf is None:
1253 mf = match(*matcherargs) 1342 mf = match(*matcherargs)
1254 submatchers[prefix] = mf 1343 submatchers[prefix] = mf
1255 1344
1256 if mf(f[len(prefix):]): 1345 if mf(f[len(prefix) :]):
1257 return True 1346 return True
1258 return False 1347 return False
1348
1259 matchfuncs.append(matchsubinclude) 1349 matchfuncs.append(matchsubinclude)
1260 1350
1261 regex = '' 1351 regex = ''
1262 if kindpats: 1352 if kindpats:
1263 if all(k == 'rootfilesin' for k, p, s in kindpats): 1353 if all(k == 'rootfilesin' for k, p, s in kindpats):
1264 dirs = {p for k, p, s in kindpats} 1354 dirs = {p for k, p, s in kindpats}
1355
1265 def mf(f): 1356 def mf(f):
1266 i = f.rfind('/') 1357 i = f.rfind('/')
1267 if i >= 0: 1358 if i >= 0:
1268 dir = f[:i] 1359 dir = f[:i]
1269 else: 1360 else:
1270 dir = '.' 1361 dir = '.'
1271 return dir in dirs 1362 return dir in dirs
1363
1272 regex = b'rootfilesin: %s' % stringutil.pprint(list(sorted(dirs))) 1364 regex = b'rootfilesin: %s' % stringutil.pprint(list(sorted(dirs)))
1273 matchfuncs.append(mf) 1365 matchfuncs.append(mf)
1274 else: 1366 else:
1275 regex, mf = _buildregexmatch(kindpats, globsuffix) 1367 regex, mf = _buildregexmatch(kindpats, globsuffix)
1276 matchfuncs.append(mf) 1368 matchfuncs.append(mf)
1278 if len(matchfuncs) == 1: 1370 if len(matchfuncs) == 1:
1279 return regex, matchfuncs[0] 1371 return regex, matchfuncs[0]
1280 else: 1372 else:
1281 return regex, lambda f: any(mf(f) for mf in matchfuncs) 1373 return regex, lambda f: any(mf(f) for mf in matchfuncs)
1282 1374
1375
1283 MAX_RE_SIZE = 20000 1376 MAX_RE_SIZE = 20000
1377
1284 1378
1285 def _joinregexes(regexps): 1379 def _joinregexes(regexps):
1286 """gather multiple regular expressions into a single one""" 1380 """gather multiple regular expressions into a single one"""
1287 return '|'.join(regexps) 1381 return '|'.join(regexps)
1382
1288 1383
1289 def _buildregexmatch(kindpats, globsuffix): 1384 def _buildregexmatch(kindpats, globsuffix):
1290 """Build a match function from a list of kinds and kindpats, 1385 """Build a match function from a list of kinds and kindpats,
1291 return regexp string and a matcher function. 1386 return regexp string and a matcher function.
1292 1387
1330 for k, p, s in kindpats: 1425 for k, p, s in kindpats:
1331 try: 1426 try:
1332 _rematcher(_regex(k, p, globsuffix)) 1427 _rematcher(_regex(k, p, globsuffix))
1333 except re.error: 1428 except re.error:
1334 if s: 1429 if s:
1335 raise error.Abort(_("%s: invalid pattern (%s): %s") % 1430 raise error.Abort(
1336 (s, k, p)) 1431 _("%s: invalid pattern (%s): %s") % (s, k, p)
1432 )
1337 else: 1433 else:
1338 raise error.Abort(_("invalid pattern (%s): %s") % (k, p)) 1434 raise error.Abort(_("invalid pattern (%s): %s") % (k, p))
1339 raise error.Abort(_("invalid pattern")) 1435 raise error.Abort(_("invalid pattern"))
1436
1340 1437
1341 def _patternrootsanddirs(kindpats): 1438 def _patternrootsanddirs(kindpats):
1342 '''Returns roots and directories corresponding to each pattern. 1439 '''Returns roots and directories corresponding to each pattern.
1343 1440
1344 This calculates the roots and directories exactly matching the patterns and 1441 This calculates the roots and directories exactly matching the patterns and
1347 directories. 1444 directories.
1348 ''' 1445 '''
1349 r = [] 1446 r = []
1350 d = [] 1447 d = []
1351 for kind, pat, source in kindpats: 1448 for kind, pat, source in kindpats:
1352 if kind in ('glob', 'rootglob'): # find the non-glob prefix 1449 if kind in ('glob', 'rootglob'): # find the non-glob prefix
1353 root = [] 1450 root = []
1354 for p in pat.split('/'): 1451 for p in pat.split('/'):
1355 if '[' in p or '{' in p or '*' in p or '?' in p: 1452 if '[' in p or '{' in p or '*' in p or '?' in p:
1356 break 1453 break
1357 root.append(p) 1454 root.append(p)
1362 r.append(pat) 1459 r.append(pat)
1363 elif kind in ('rootfilesin',): 1460 elif kind in ('rootfilesin',):
1364 if pat == '.': 1461 if pat == '.':
1365 pat = '' 1462 pat = ''
1366 d.append(pat) 1463 d.append(pat)
1367 else: # relglob, re, relre 1464 else: # relglob, re, relre
1368 r.append('') 1465 r.append('')
1369 return r, d 1466 return r, d
1467
1370 1468
1371 def _roots(kindpats): 1469 def _roots(kindpats):
1372 '''Returns root directories to match recursively from the given patterns.''' 1470 '''Returns root directories to match recursively from the given patterns.'''
1373 roots, dirs = _patternrootsanddirs(kindpats) 1471 roots, dirs = _patternrootsanddirs(kindpats)
1374 return roots 1472 return roots
1473
1375 1474
1376 def _rootsdirsandparents(kindpats): 1475 def _rootsdirsandparents(kindpats):
1377 '''Returns roots and exact directories from patterns. 1476 '''Returns roots and exact directories from patterns.
1378 1477
1379 `roots` are directories to match recursively, `dirs` should 1478 `roots` are directories to match recursively, `dirs` should
1414 # returning. 1513 # returning.
1415 # FIXME: all uses of this function do not need anything in 'roots' and 1514 # FIXME: all uses of this function do not need anything in 'roots' and
1416 # 'dirs' to also be in 'parents', consider removing them before returning. 1515 # 'dirs' to also be in 'parents', consider removing them before returning.
1417 return r, d, p 1516 return r, d, p
1418 1517
1518
1419 def _explicitfiles(kindpats): 1519 def _explicitfiles(kindpats):
1420 '''Returns the potential explicit filenames from the patterns. 1520 '''Returns the potential explicit filenames from the patterns.
1421 1521
1422 >>> _explicitfiles([(b'path', b'foo/bar', b'')]) 1522 >>> _explicitfiles([(b'path', b'foo/bar', b'')])
1423 ['foo/bar'] 1523 ['foo/bar']
1427 # Keep only the pattern kinds where one can specify filenames (vs only 1527 # Keep only the pattern kinds where one can specify filenames (vs only
1428 # directory names). 1528 # directory names).
1429 filable = [kp for kp in kindpats if kp[0] not in ('rootfilesin',)] 1529 filable = [kp for kp in kindpats if kp[0] not in ('rootfilesin',)]
1430 return _roots(filable) 1530 return _roots(filable)
1431 1531
1532
1432 def _prefix(kindpats): 1533 def _prefix(kindpats):
1433 '''Whether all the patterns match a prefix (i.e. recursively)''' 1534 '''Whether all the patterns match a prefix (i.e. recursively)'''
1434 for kind, pat, source in kindpats: 1535 for kind, pat, source in kindpats:
1435 if kind not in ('path', 'relpath'): 1536 if kind not in ('path', 'relpath'):
1436 return False 1537 return False
1437 return True 1538 return True
1438 1539
1540
1439 _commentre = None 1541 _commentre = None
1542
1440 1543
1441 def readpatternfile(filepath, warn, sourceinfo=False): 1544 def readpatternfile(filepath, warn, sourceinfo=False):
1442 '''parse a pattern file, returning a list of 1545 '''parse a pattern file, returning a list of
1443 patterns. These patterns should be given to compile() 1546 patterns. These patterns should be given to compile()
1444 to be validated and converted into a match function. 1547 to be validated and converted into a match function.
1462 This is useful to debug ignore patterns. 1565 This is useful to debug ignore patterns.
1463 ''' 1566 '''
1464 1567
1465 if rustmod is not None: 1568 if rustmod is not None:
1466 result, warnings = rustmod.read_pattern_file( 1569 result, warnings = rustmod.read_pattern_file(
1467 filepath, 1570 filepath, bool(warn), sourceinfo,
1468 bool(warn),
1469 sourceinfo,
1470 ) 1571 )
1471 1572
1472 for warning_params in warnings: 1573 for warning_params in warnings:
1473 # Can't be easily emitted from Rust, because it would require 1574 # Can't be easily emitted from Rust, because it would require
1474 # a mechanism for both gettext and calling the `warn` function. 1575 # a mechanism for both gettext and calling the `warn` function.
1494 if not _commentre: 1595 if not _commentre:
1495 _commentre = util.re.compile(br'((?:^|[^\\])(?:\\\\)*)#.*') 1596 _commentre = util.re.compile(br'((?:^|[^\\])(?:\\\\)*)#.*')
1496 # remove comments prefixed by an even number of escapes 1597 # remove comments prefixed by an even number of escapes
1497 m = _commentre.search(line) 1598 m = _commentre.search(line)
1498 if m: 1599 if m:
1499 line = line[:m.end(1)] 1600 line = line[: m.end(1)]
1500 # fixup properly escaped comments that survived the above 1601 # fixup properly escaped comments that survived the above
1501 line = line.replace("\\#", "#") 1602 line = line.replace("\\#", "#")
1502 line = line.rstrip() 1603 line = line.rstrip()
1503 if not line: 1604 if not line:
1504 continue 1605 continue
1507 s = line[7:].strip() 1608 s = line[7:].strip()
1508 try: 1609 try:
1509 syntax = syntaxes[s] 1610 syntax = syntaxes[s]
1510 except KeyError: 1611 except KeyError:
1511 if warn: 1612 if warn:
1512 warn(_("%s: ignoring invalid syntax '%s'\n") % 1613 warn(
1513 (filepath, s)) 1614 _("%s: ignoring invalid syntax '%s'\n") % (filepath, s)
1615 )
1514 continue 1616 continue
1515 1617
1516 linesyntax = syntax 1618 linesyntax = syntax
1517 for s, rels in syntaxes.iteritems(): 1619 for s, rels in syntaxes.iteritems():
1518 if line.startswith(rels): 1620 if line.startswith(rels):
1519 linesyntax = rels 1621 linesyntax = rels
1520 line = line[len(rels):] 1622 line = line[len(rels) :]
1521 break 1623 break
1522 elif line.startswith(s+':'): 1624 elif line.startswith(s + ':'):
1523 linesyntax = rels 1625 linesyntax = rels
1524 line = line[len(s) + 1:] 1626 line = line[len(s) + 1 :]
1525 break 1627 break
1526 if sourceinfo: 1628 if sourceinfo:
1527 patterns.append((linesyntax + line, lineno, line)) 1629 patterns.append((linesyntax + line, lineno, line))
1528 else: 1630 else:
1529 patterns.append(linesyntax + line) 1631 patterns.append(linesyntax + line)