##// END OF EJS Templates
match: add missing "return set()", add FIXME to test to doc a bug...
Kyle Lippincott -
r38993:b9f94d67 default
parent child Browse files
Show More
@@ -1,1320 +1,1321
1 1 # match.py - filename matching
2 2 #
3 3 # Copyright 2008, 2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import, print_function
9 9
10 10 import copy
11 11 import itertools
12 12 import os
13 13 import re
14 14
15 15 from .i18n import _
16 16 from . import (
17 17 encoding,
18 18 error,
19 19 pathutil,
20 20 pycompat,
21 21 util,
22 22 )
23 23 from .utils import (
24 24 stringutil,
25 25 )
26 26
27 27 allpatternkinds = ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
28 28 'listfile', 'listfile0', 'set', 'include', 'subinclude',
29 29 'rootfilesin')
30 30 cwdrelativepatternkinds = ('relpath', 'glob')
31 31
32 32 propertycache = util.propertycache
33 33
34 34 def _rematcher(regex):
35 35 '''compile the regexp with the best available regexp engine and return a
36 36 matcher function'''
37 37 m = util.re.compile(regex)
38 38 try:
39 39 # slightly faster, provided by facebook's re2 bindings
40 40 return m.test_match
41 41 except AttributeError:
42 42 return m.match
43 43
44 44 def _expandsets(root, cwd, kindpats, ctx, listsubrepos, badfn):
45 45 '''Returns the kindpats list with the 'set' patterns expanded to matchers'''
46 46 matchers = []
47 47 other = []
48 48
49 49 for kind, pat, source in kindpats:
50 50 if kind == 'set':
51 51 if not ctx:
52 52 raise error.ProgrammingError("fileset expression with no "
53 53 "context")
54 54 matchers.append(ctx.matchfileset(pat, badfn=badfn))
55 55
56 56 if listsubrepos:
57 57 for subpath in ctx.substate:
58 58 sm = ctx.sub(subpath).matchfileset(pat, badfn=badfn)
59 59 pm = prefixdirmatcher(root, cwd, subpath, sm, badfn=badfn)
60 60 matchers.append(pm)
61 61
62 62 continue
63 63 other.append((kind, pat, source))
64 64 return matchers, other
65 65
66 66 def _expandsubinclude(kindpats, root):
67 67 '''Returns the list of subinclude matcher args and the kindpats without the
68 68 subincludes in it.'''
69 69 relmatchers = []
70 70 other = []
71 71
72 72 for kind, pat, source in kindpats:
73 73 if kind == 'subinclude':
74 74 sourceroot = pathutil.dirname(util.normpath(source))
75 75 pat = util.pconvert(pat)
76 76 path = pathutil.join(sourceroot, pat)
77 77
78 78 newroot = pathutil.dirname(path)
79 79 matcherargs = (newroot, '', [], ['include:%s' % path])
80 80
81 81 prefix = pathutil.canonpath(root, root, newroot)
82 82 if prefix:
83 83 prefix += '/'
84 84 relmatchers.append((prefix, matcherargs))
85 85 else:
86 86 other.append((kind, pat, source))
87 87
88 88 return relmatchers, other
89 89
90 90 def _kindpatsalwaysmatch(kindpats):
91 91 """"Checks whether the kindspats match everything, as e.g.
92 92 'relpath:.' does.
93 93 """
94 94 for kind, pat, source in kindpats:
95 95 if pat != '' or kind not in ['relpath', 'glob']:
96 96 return False
97 97 return True
98 98
99 99 def _buildkindpatsmatcher(matchercls, root, cwd, kindpats, ctx=None,
100 100 listsubrepos=False, badfn=None):
101 101 matchers = []
102 102 fms, kindpats = _expandsets(root, cwd, kindpats, ctx=ctx,
103 103 listsubrepos=listsubrepos, badfn=badfn)
104 104 if kindpats:
105 105 m = matchercls(root, cwd, kindpats, listsubrepos=listsubrepos,
106 106 badfn=badfn)
107 107 matchers.append(m)
108 108 if fms:
109 109 matchers.extend(fms)
110 110 if not matchers:
111 111 return nevermatcher(root, cwd, badfn=badfn)
112 112 if len(matchers) == 1:
113 113 return matchers[0]
114 114 return unionmatcher(matchers)
115 115
116 116 def match(root, cwd, patterns=None, include=None, exclude=None, default='glob',
117 117 exact=False, auditor=None, ctx=None, listsubrepos=False, warn=None,
118 118 badfn=None, icasefs=False):
119 119 """build an object to match a set of file patterns
120 120
121 121 arguments:
122 122 root - the canonical root of the tree you're matching against
123 123 cwd - the current working directory, if relevant
124 124 patterns - patterns to find
125 125 include - patterns to include (unless they are excluded)
126 126 exclude - patterns to exclude (even if they are included)
127 127 default - if a pattern in patterns has no explicit type, assume this one
128 128 exact - patterns are actually filenames (include/exclude still apply)
129 129 warn - optional function used for printing warnings
130 130 badfn - optional bad() callback for this matcher instead of the default
131 131 icasefs - make a matcher for wdir on case insensitive filesystems, which
132 132 normalizes the given patterns to the case in the filesystem
133 133
134 134 a pattern is one of:
135 135 'glob:<glob>' - a glob relative to cwd
136 136 're:<regexp>' - a regular expression
137 137 'path:<path>' - a path relative to repository root, which is matched
138 138 recursively
139 139 'rootfilesin:<path>' - a path relative to repository root, which is
140 140 matched non-recursively (will not match subdirectories)
141 141 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
142 142 'relpath:<path>' - a path relative to cwd
143 143 'relre:<regexp>' - a regexp that needn't match the start of a name
144 144 'set:<fileset>' - a fileset expression
145 145 'include:<path>' - a file of patterns to read and include
146 146 'subinclude:<path>' - a file of patterns to match against files under
147 147 the same directory
148 148 '<something>' - a pattern of the specified default type
149 149 """
150 150 normalize = _donormalize
151 151 if icasefs:
152 152 if exact:
153 153 raise error.ProgrammingError("a case-insensitive exact matcher "
154 154 "doesn't make sense")
155 155 dirstate = ctx.repo().dirstate
156 156 dsnormalize = dirstate.normalize
157 157
158 158 def normalize(patterns, default, root, cwd, auditor, warn):
159 159 kp = _donormalize(patterns, default, root, cwd, auditor, warn)
160 160 kindpats = []
161 161 for kind, pats, source in kp:
162 162 if kind not in ('re', 'relre'): # regex can't be normalized
163 163 p = pats
164 164 pats = dsnormalize(pats)
165 165
166 166 # Preserve the original to handle a case only rename.
167 167 if p != pats and p in dirstate:
168 168 kindpats.append((kind, p, source))
169 169
170 170 kindpats.append((kind, pats, source))
171 171 return kindpats
172 172
173 173 if exact:
174 174 m = exactmatcher(root, cwd, patterns, badfn)
175 175 elif patterns:
176 176 kindpats = normalize(patterns, default, root, cwd, auditor, warn)
177 177 if _kindpatsalwaysmatch(kindpats):
178 178 m = alwaysmatcher(root, cwd, badfn, relativeuipath=True)
179 179 else:
180 180 m = _buildkindpatsmatcher(patternmatcher, root, cwd, kindpats,
181 181 ctx=ctx, listsubrepos=listsubrepos,
182 182 badfn=badfn)
183 183 else:
184 184 # It's a little strange that no patterns means to match everything.
185 185 # Consider changing this to match nothing (probably using nevermatcher).
186 186 m = alwaysmatcher(root, cwd, badfn)
187 187
188 188 if include:
189 189 kindpats = normalize(include, 'glob', root, cwd, auditor, warn)
190 190 im = _buildkindpatsmatcher(includematcher, root, cwd, kindpats, ctx=ctx,
191 191 listsubrepos=listsubrepos, badfn=None)
192 192 m = intersectmatchers(m, im)
193 193 if exclude:
194 194 kindpats = normalize(exclude, 'glob', root, cwd, auditor, warn)
195 195 em = _buildkindpatsmatcher(includematcher, root, cwd, kindpats, ctx=ctx,
196 196 listsubrepos=listsubrepos, badfn=None)
197 197 m = differencematcher(m, em)
198 198 return m
199 199
200 200 def exact(root, cwd, files, badfn=None):
201 201 return exactmatcher(root, cwd, files, badfn=badfn)
202 202
203 203 def always(root, cwd):
204 204 return alwaysmatcher(root, cwd)
205 205
206 206 def never(root, cwd):
207 207 return nevermatcher(root, cwd)
208 208
209 209 def badmatch(match, badfn):
210 210 """Make a copy of the given matcher, replacing its bad method with the given
211 211 one.
212 212 """
213 213 m = copy.copy(match)
214 214 m.bad = badfn
215 215 return m
216 216
217 217 def _donormalize(patterns, default, root, cwd, auditor, warn):
218 218 '''Convert 'kind:pat' from the patterns list to tuples with kind and
219 219 normalized and rooted patterns and with listfiles expanded.'''
220 220 kindpats = []
221 221 for kind, pat in [_patsplit(p, default) for p in patterns]:
222 222 if kind in cwdrelativepatternkinds:
223 223 pat = pathutil.canonpath(root, cwd, pat, auditor)
224 224 elif kind in ('relglob', 'path', 'rootfilesin'):
225 225 pat = util.normpath(pat)
226 226 elif kind in ('listfile', 'listfile0'):
227 227 try:
228 228 files = util.readfile(pat)
229 229 if kind == 'listfile0':
230 230 files = files.split('\0')
231 231 else:
232 232 files = files.splitlines()
233 233 files = [f for f in files if f]
234 234 except EnvironmentError:
235 235 raise error.Abort(_("unable to read file list (%s)") % pat)
236 236 for k, p, source in _donormalize(files, default, root, cwd,
237 237 auditor, warn):
238 238 kindpats.append((k, p, pat))
239 239 continue
240 240 elif kind == 'include':
241 241 try:
242 242 fullpath = os.path.join(root, util.localpath(pat))
243 243 includepats = readpatternfile(fullpath, warn)
244 244 for k, p, source in _donormalize(includepats, default,
245 245 root, cwd, auditor, warn):
246 246 kindpats.append((k, p, source or pat))
247 247 except error.Abort as inst:
248 248 raise error.Abort('%s: %s' % (pat, inst[0]))
249 249 except IOError as inst:
250 250 if warn:
251 251 warn(_("skipping unreadable pattern file '%s': %s\n") %
252 252 (pat, stringutil.forcebytestr(inst.strerror)))
253 253 continue
254 254 # else: re or relre - which cannot be normalized
255 255 kindpats.append((kind, pat, ''))
256 256 return kindpats
257 257
258 258 class basematcher(object):
259 259
260 260 def __init__(self, root, cwd, badfn=None, relativeuipath=True):
261 261 self._root = root
262 262 self._cwd = cwd
263 263 if badfn is not None:
264 264 self.bad = badfn
265 265 self._relativeuipath = relativeuipath
266 266
267 267 def __call__(self, fn):
268 268 return self.matchfn(fn)
269 269 def __iter__(self):
270 270 for f in self._files:
271 271 yield f
272 272 # Callbacks related to how the matcher is used by dirstate.walk.
273 273 # Subscribers to these events must monkeypatch the matcher object.
274 274 def bad(self, f, msg):
275 275 '''Callback from dirstate.walk for each explicit file that can't be
276 276 found/accessed, with an error message.'''
277 277
278 278 # If an explicitdir is set, it will be called when an explicitly listed
279 279 # directory is visited.
280 280 explicitdir = None
281 281
282 282 # If an traversedir is set, it will be called when a directory discovered
283 283 # by recursive traversal is visited.
284 284 traversedir = None
285 285
286 286 def abs(self, f):
287 287 '''Convert a repo path back to path that is relative to the root of the
288 288 matcher.'''
289 289 return f
290 290
291 291 def rel(self, f):
292 292 '''Convert repo path back to path that is relative to cwd of matcher.'''
293 293 return util.pathto(self._root, self._cwd, f)
294 294
295 295 def uipath(self, f):
296 296 '''Convert repo path to a display path. If patterns or -I/-X were used
297 297 to create this matcher, the display path will be relative to cwd.
298 298 Otherwise it is relative to the root of the repo.'''
299 299 return (self._relativeuipath and self.rel(f)) or self.abs(f)
300 300
301 301 @propertycache
302 302 def _files(self):
303 303 return []
304 304
305 305 def files(self):
306 306 '''Explicitly listed files or patterns or roots:
307 307 if no patterns or .always(): empty list,
308 308 if exact: list exact files,
309 309 if not .anypats(): list all files and dirs,
310 310 else: optimal roots'''
311 311 return self._files
312 312
313 313 @propertycache
314 314 def _fileset(self):
315 315 return set(self._files)
316 316
317 317 def exact(self, f):
318 318 '''Returns True if f is in .files().'''
319 319 return f in self._fileset
320 320
321 321 def matchfn(self, f):
322 322 return False
323 323
324 324 def visitdir(self, dir):
325 325 '''Decides whether a directory should be visited based on whether it
326 326 has potential matches in it or one of its subdirectories. This is
327 327 based on the match's primary, included, and excluded patterns.
328 328
329 329 Returns the string 'all' if the given directory and all subdirectories
330 330 should be visited. Otherwise returns True or False indicating whether
331 331 the given directory should be visited.
332 332 '''
333 333 return True
334 334
335 335 def visitchildrenset(self, dir):
336 336 '''Decides whether a directory should be visited based on whether it
337 337 has potential matches in it or one of its subdirectories, and
338 338 potentially lists which subdirectories of that directory should be
339 339 visited. This is based on the match's primary, included, and excluded
340 340 patterns.
341 341
342 342 This function is very similar to 'visitdir', and the following mapping
343 343 can be applied:
344 344
345 345 visitdir | visitchildrenlist
346 346 ----------+-------------------
347 347 False | set()
348 348 'all' | 'all'
349 349 True | 'this' OR non-empty set of subdirs to visit
350 350
351 351 Example:
352 352 Assume matchers ['path:foo/bar', 'rootfilesin:qux'], we would return
353 353 the following values (assuming the implementation of visitchildrenset
354 354 is capable of recognizing this; some implementations are not).
355 355
356 356 '.' -> {'foo', 'qux'}
357 357 'baz' -> set()
358 358 'foo' -> {'bar'}
359 359 # Ideally this would be 'all', but since the prefix nature of matchers
360 360 # is applied to the entire matcher, we have to downgrade to this
361 361 # 'this' due to the non-prefix 'rootfilesin'-kind matcher.
362 362 'foo/bar' -> 'this'
363 363 'qux' -> 'this'
364 364 '''
365 365 return 'this'
366 366
367 367 def always(self):
368 368 '''Matcher will match everything and .files() will be empty --
369 369 optimization might be possible.'''
370 370 return False
371 371
372 372 def isexact(self):
373 373 '''Matcher will match exactly the list of files in .files() --
374 374 optimization might be possible.'''
375 375 return False
376 376
377 377 def prefix(self):
378 378 '''Matcher will match the paths in .files() recursively --
379 379 optimization might be possible.'''
380 380 return False
381 381
382 382 def anypats(self):
383 383 '''None of .always(), .isexact(), and .prefix() is true --
384 384 optimizations will be difficult.'''
385 385 return not self.always() and not self.isexact() and not self.prefix()
386 386
387 387 class alwaysmatcher(basematcher):
388 388 '''Matches everything.'''
389 389
390 390 def __init__(self, root, cwd, badfn=None, relativeuipath=False):
391 391 super(alwaysmatcher, self).__init__(root, cwd, badfn,
392 392 relativeuipath=relativeuipath)
393 393
394 394 def always(self):
395 395 return True
396 396
397 397 def matchfn(self, f):
398 398 return True
399 399
400 400 def visitdir(self, dir):
401 401 return 'all'
402 402
403 403 def visitchildrenset(self, dir):
404 404 return 'all'
405 405
406 406 def __repr__(self):
407 407 return r'<alwaysmatcher>'
408 408
409 409 class nevermatcher(basematcher):
410 410 '''Matches nothing.'''
411 411
412 412 def __init__(self, root, cwd, badfn=None):
413 413 super(nevermatcher, self).__init__(root, cwd, badfn)
414 414
415 415 # It's a little weird to say that the nevermatcher is an exact matcher
416 416 # or a prefix matcher, but it seems to make sense to let callers take
417 417 # fast paths based on either. There will be no exact matches, nor any
418 418 # prefixes (files() returns []), so fast paths iterating over them should
419 419 # be efficient (and correct).
420 420 def isexact(self):
421 421 return True
422 422
423 423 def prefix(self):
424 424 return True
425 425
426 426 def visitdir(self, dir):
427 427 return False
428 428
429 429 def visitchildrenset(self, dir):
430 430 return set()
431 431
432 432 def __repr__(self):
433 433 return r'<nevermatcher>'
434 434
435 435 class predicatematcher(basematcher):
436 436 """A matcher adapter for a simple boolean function"""
437 437
438 438 def __init__(self, root, cwd, predfn, predrepr=None, badfn=None):
439 439 super(predicatematcher, self).__init__(root, cwd, badfn)
440 440 self.matchfn = predfn
441 441 self._predrepr = predrepr
442 442
443 443 @encoding.strmethod
444 444 def __repr__(self):
445 445 s = (stringutil.buildrepr(self._predrepr)
446 446 or pycompat.byterepr(self.matchfn))
447 447 return '<predicatenmatcher pred=%s>' % s
448 448
449 449 class patternmatcher(basematcher):
450 450
451 451 def __init__(self, root, cwd, kindpats, listsubrepos=False, badfn=None):
452 452 super(patternmatcher, self).__init__(root, cwd, badfn)
453 453
454 454 self._files = _explicitfiles(kindpats)
455 455 self._prefix = _prefix(kindpats)
456 456 self._pats, self.matchfn = _buildmatch(kindpats, '$', listsubrepos,
457 457 root)
458 458
459 459 @propertycache
460 460 def _dirs(self):
461 461 return set(util.dirs(self._fileset)) | {'.'}
462 462
463 463 def visitdir(self, dir):
464 464 if self._prefix and dir in self._fileset:
465 465 return 'all'
466 466 return ('.' in self._fileset or
467 467 dir in self._fileset or
468 468 dir in self._dirs or
469 469 any(parentdir in self._fileset
470 470 for parentdir in util.finddirs(dir)))
471 471
472 472 def visitchildrenset(self, dir):
473 473 ret = self.visitdir(dir)
474 474 if ret is True:
475 475 return 'this'
476 476 elif not ret:
477 477 return set()
478 478 assert ret == 'all'
479 479 return 'all'
480 480
481 481 def prefix(self):
482 482 return self._prefix
483 483
484 484 @encoding.strmethod
485 485 def __repr__(self):
486 486 return ('<patternmatcher patterns=%r>' % pycompat.bytestr(self._pats))
487 487
488 488 class includematcher(basematcher):
489 489
490 490 def __init__(self, root, cwd, kindpats, listsubrepos=False, badfn=None):
491 491 super(includematcher, self).__init__(root, cwd, badfn)
492 492
493 493 self._pats, self.matchfn = _buildmatch(kindpats, '(?:/|$)',
494 494 listsubrepos, root)
495 495 self._prefix = _prefix(kindpats)
496 496 roots, dirs, parents = _rootsdirsandparents(kindpats)
497 497 # roots are directories which are recursively included.
498 498 self._roots = set(roots)
499 499 # dirs are directories which are non-recursively included.
500 500 self._dirs = set(dirs)
501 501 # parents are directories which are non-recursively included because
502 502 # they are needed to get to items in _dirs or _roots.
503 503 self._parents = set(parents)
504 504
505 505 def visitdir(self, dir):
506 506 if self._prefix and dir in self._roots:
507 507 return 'all'
508 508 return ('.' in self._roots or
509 509 dir in self._roots or
510 510 dir in self._dirs or
511 511 dir in self._parents or
512 512 any(parentdir in self._roots
513 513 for parentdir in util.finddirs(dir)))
514 514
515 515 def visitchildrenset(self, dir):
516 516 if self._prefix and dir in self._roots:
517 517 return 'all'
518 518 # Note: this does *not* include the 'dir in self._parents' case from
519 519 # visitdir, that's handled below.
520 520 if ('.' in self._roots or
521 521 dir in self._roots or
522 522 dir in self._dirs or
523 523 any(parentdir in self._roots
524 524 for parentdir in util.finddirs(dir))):
525 525 return 'this'
526 526
527 527 ret = set()
528 528 if dir in self._parents:
529 529 # We add a '/' on to `dir` so that we don't return items that are
530 530 # prefixed by `dir` but are actually siblings of `dir`.
531 531 suffixeddir = dir + '/' if dir != '.' else ''
532 532 # Look in all _roots, _dirs, and _parents for things that start with
533 533 # 'suffixeddir'.
534 534 for d in [q for q in
535 535 itertools.chain(self._roots, self._dirs, self._parents) if
536 536 q.startswith(suffixeddir)]:
537 537 # Don't emit '.' in the response for the root directory
538 538 if not suffixeddir and d == '.':
539 539 continue
540 540
541 541 # We return the item name without the `suffixeddir` prefix or a
542 542 # slash suffix
543 543 d = d[len(suffixeddir):]
544 544 if '/' in d:
545 545 # This is a subdirectory-of-a-subdirectory, i.e.
546 546 # suffixeddir='foo/', d was 'foo/bar/baz' before removing
547 547 # 'foo/'.
548 548 d = d[:d.index('/')]
549 549 ret.add(d)
550 550 return ret
551 551
552 552 @encoding.strmethod
553 553 def __repr__(self):
554 554 return ('<includematcher includes=%r>' % pycompat.bytestr(self._pats))
555 555
556 556 class exactmatcher(basematcher):
557 557 '''Matches the input files exactly. They are interpreted as paths, not
558 558 patterns (so no kind-prefixes).
559 559 '''
560 560
561 561 def __init__(self, root, cwd, files, badfn=None):
562 562 super(exactmatcher, self).__init__(root, cwd, badfn)
563 563
564 564 if isinstance(files, list):
565 565 self._files = files
566 566 else:
567 567 self._files = list(files)
568 568
569 569 matchfn = basematcher.exact
570 570
571 571 @propertycache
572 572 def _dirs(self):
573 573 return set(util.dirs(self._fileset)) | {'.'}
574 574
575 575 def visitdir(self, dir):
576 576 return dir in self._dirs
577 577
578 578 def visitchildrenset(self, dir):
579 579 if dir in self._dirs:
580 580 candidates = self._dirs - {'.'}
581 581 if dir != '.':
582 582 d = dir + '/'
583 583 candidates = set(c[len(d):] for c in candidates if
584 584 c.startswith(d))
585 585 # self._dirs includes all of the directories, recursively, so if
586 586 # we're attempting to match foo/bar/baz.txt, it'll have '.', 'foo',
587 587 # 'foo/bar' in it. Thus we can safely ignore a candidate that has a
588 588 # '/' in it, indicating a it's for a subdir-of-a-subdir; the
589 589 # immediate subdir will be in there without a slash.
590 590 ret = set(c for c in candidates if '/' not in c)
591 591 # We need to emit 'this' for foo/bar, not set(), not {'baz.txt'}.
592 592 if not ret:
593 593 return 'this'
594 594 return ret
595 595 return set()
596 596
597 597 def isexact(self):
598 598 return True
599 599
600 600 @encoding.strmethod
601 601 def __repr__(self):
602 602 return ('<exactmatcher files=%r>' % self._files)
603 603
604 604 class differencematcher(basematcher):
605 605 '''Composes two matchers by matching if the first matches and the second
606 606 does not.
607 607
608 608 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
609 609 traversedir) are ignored.
610 610 '''
611 611 def __init__(self, m1, m2):
612 612 super(differencematcher, self).__init__(m1._root, m1._cwd)
613 613 self._m1 = m1
614 614 self._m2 = m2
615 615 self.bad = m1.bad
616 616 self.explicitdir = m1.explicitdir
617 617 self.traversedir = m1.traversedir
618 618
619 619 def matchfn(self, f):
620 620 return self._m1(f) and not self._m2(f)
621 621
622 622 @propertycache
623 623 def _files(self):
624 624 if self.isexact():
625 625 return [f for f in self._m1.files() if self(f)]
626 626 # If m1 is not an exact matcher, we can't easily figure out the set of
627 627 # files, because its files() are not always files. For example, if
628 628 # m1 is "path:dir" and m2 is "rootfileins:.", we don't
629 629 # want to remove "dir" from the set even though it would match m2,
630 630 # because the "dir" in m1 may not be a file.
631 631 return self._m1.files()
632 632
633 633 def visitdir(self, dir):
634 634 if self._m2.visitdir(dir) == 'all':
635 635 return False
636 636 return bool(self._m1.visitdir(dir))
637 637
638 638 def visitchildrenset(self, dir):
639 639 m2_set = self._m2.visitchildrenset(dir)
640 640 if m2_set == 'all':
641 641 return set()
642 642 m1_set = self._m1.visitchildrenset(dir)
643 643 # Possible values for m1: 'all', 'this', set(...), set()
644 644 # Possible values for m2: 'this', set(...), set()
645 645 # If m2 has nothing under here that we care about, return m1, even if
646 646 # it's 'all'. This is a change in behavior from visitdir, which would
647 647 # return True, not 'all', for some reason.
648 648 if not m2_set:
649 649 return m1_set
650 650 if m1_set in ['all', 'this']:
651 651 # Never return 'all' here if m2_set is any kind of non-empty (either
652 652 # 'this' or set(foo)), since m2 might return set() for a
653 653 # subdirectory.
654 654 return 'this'
655 655 # Possible values for m1: set(...), set()
656 656 # Possible values for m2: 'this', set(...)
657 657 # We ignore m2's set results. They're possibly incorrect:
658 658 # m1 = path:dir/subdir, m2=rootfilesin:dir, visitchildrenset('.'):
659 659 # m1 returns {'dir'}, m2 returns {'dir'}, if we subtracted we'd
660 660 # return set(), which is *not* correct, we still need to visit 'dir'!
661 661 return m1_set
662 662
663 663 def isexact(self):
664 664 return self._m1.isexact()
665 665
666 666 @encoding.strmethod
667 667 def __repr__(self):
668 668 return ('<differencematcher m1=%r, m2=%r>' % (self._m1, self._m2))
669 669
670 670 def intersectmatchers(m1, m2):
671 671 '''Composes two matchers by matching if both of them match.
672 672
673 673 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
674 674 traversedir) are ignored.
675 675 '''
676 676 if m1 is None or m2 is None:
677 677 return m1 or m2
678 678 if m1.always():
679 679 m = copy.copy(m2)
680 680 # TODO: Consider encapsulating these things in a class so there's only
681 681 # one thing to copy from m1.
682 682 m.bad = m1.bad
683 683 m.explicitdir = m1.explicitdir
684 684 m.traversedir = m1.traversedir
685 685 m.abs = m1.abs
686 686 m.rel = m1.rel
687 687 m._relativeuipath |= m1._relativeuipath
688 688 return m
689 689 if m2.always():
690 690 m = copy.copy(m1)
691 691 m._relativeuipath |= m2._relativeuipath
692 692 return m
693 693 return intersectionmatcher(m1, m2)
694 694
695 695 class intersectionmatcher(basematcher):
696 696 def __init__(self, m1, m2):
697 697 super(intersectionmatcher, self).__init__(m1._root, m1._cwd)
698 698 self._m1 = m1
699 699 self._m2 = m2
700 700 self.bad = m1.bad
701 701 self.explicitdir = m1.explicitdir
702 702 self.traversedir = m1.traversedir
703 703
704 704 @propertycache
705 705 def _files(self):
706 706 if self.isexact():
707 707 m1, m2 = self._m1, self._m2
708 708 if not m1.isexact():
709 709 m1, m2 = m2, m1
710 710 return [f for f in m1.files() if m2(f)]
711 711 # It neither m1 nor m2 is an exact matcher, we can't easily intersect
712 712 # the set of files, because their files() are not always files. For
713 713 # example, if intersecting a matcher "-I glob:foo.txt" with matcher of
714 714 # "path:dir2", we don't want to remove "dir2" from the set.
715 715 return self._m1.files() + self._m2.files()
716 716
717 717 def matchfn(self, f):
718 718 return self._m1(f) and self._m2(f)
719 719
720 720 def visitdir(self, dir):
721 721 visit1 = self._m1.visitdir(dir)
722 722 if visit1 == 'all':
723 723 return self._m2.visitdir(dir)
724 724 # bool() because visit1=True + visit2='all' should not be 'all'
725 725 return bool(visit1 and self._m2.visitdir(dir))
726 726
727 727 def visitchildrenset(self, dir):
728 728 m1_set = self._m1.visitchildrenset(dir)
729 729 if not m1_set:
730 730 return set()
731 731 m2_set = self._m2.visitchildrenset(dir)
732 732 if not m2_set:
733 733 return set()
734 734
735 735 if m1_set == 'all':
736 736 return m2_set
737 737 elif m2_set == 'all':
738 738 return m1_set
739 739
740 740 if m1_set == 'this' or m2_set == 'this':
741 741 return 'this'
742 742
743 743 assert isinstance(m1_set, set) and isinstance(m2_set, set)
744 744 return m1_set.intersection(m2_set)
745 745
746 746 def always(self):
747 747 return self._m1.always() and self._m2.always()
748 748
749 749 def isexact(self):
750 750 return self._m1.isexact() or self._m2.isexact()
751 751
752 752 @encoding.strmethod
753 753 def __repr__(self):
754 754 return ('<intersectionmatcher m1=%r, m2=%r>' % (self._m1, self._m2))
755 755
756 756 class subdirmatcher(basematcher):
757 757 """Adapt a matcher to work on a subdirectory only.
758 758
759 759 The paths are remapped to remove/insert the path as needed:
760 760
761 761 >>> from . import pycompat
762 762 >>> m1 = match(b'root', b'', [b'a.txt', b'sub/b.txt'])
763 763 >>> m2 = subdirmatcher(b'sub', m1)
764 764 >>> bool(m2(b'a.txt'))
765 765 False
766 766 >>> bool(m2(b'b.txt'))
767 767 True
768 768 >>> bool(m2.matchfn(b'a.txt'))
769 769 False
770 770 >>> bool(m2.matchfn(b'b.txt'))
771 771 True
772 772 >>> m2.files()
773 773 ['b.txt']
774 774 >>> m2.exact(b'b.txt')
775 775 True
776 776 >>> util.pconvert(m2.rel(b'b.txt'))
777 777 'sub/b.txt'
778 778 >>> def bad(f, msg):
779 779 ... print(pycompat.sysstr(b"%s: %s" % (f, msg)))
780 780 >>> m1.bad = bad
781 781 >>> m2.bad(b'x.txt', b'No such file')
782 782 sub/x.txt: No such file
783 783 >>> m2.abs(b'c.txt')
784 784 'sub/c.txt'
785 785 """
786 786
787 787 def __init__(self, path, matcher):
788 788 super(subdirmatcher, self).__init__(matcher._root, matcher._cwd)
789 789 self._path = path
790 790 self._matcher = matcher
791 791 self._always = matcher.always()
792 792
793 793 self._files = [f[len(path) + 1:] for f in matcher._files
794 794 if f.startswith(path + "/")]
795 795
796 796 # If the parent repo had a path to this subrepo and the matcher is
797 797 # a prefix matcher, this submatcher always matches.
798 798 if matcher.prefix():
799 799 self._always = any(f == path for f in matcher._files)
800 800
801 801 def bad(self, f, msg):
802 802 self._matcher.bad(self._path + "/" + f, msg)
803 803
804 804 def abs(self, f):
805 805 return self._matcher.abs(self._path + "/" + f)
806 806
807 807 def rel(self, f):
808 808 return self._matcher.rel(self._path + "/" + f)
809 809
810 810 def uipath(self, f):
811 811 return self._matcher.uipath(self._path + "/" + f)
812 812
813 813 def matchfn(self, f):
814 814 # Some information is lost in the superclass's constructor, so we
815 815 # can not accurately create the matching function for the subdirectory
816 816 # from the inputs. Instead, we override matchfn() and visitdir() to
817 817 # call the original matcher with the subdirectory path prepended.
818 818 return self._matcher.matchfn(self._path + "/" + f)
819 819
820 820 def visitdir(self, dir):
821 821 if dir == '.':
822 822 dir = self._path
823 823 else:
824 824 dir = self._path + "/" + dir
825 825 return self._matcher.visitdir(dir)
826 826
827 827 def visitchildrenset(self, dir):
828 828 if dir == '.':
829 829 dir = self._path
830 830 else:
831 831 dir = self._path + "/" + dir
832 832 return self._matcher.visitchildrenset(dir)
833 833
834 834 def always(self):
835 835 return self._always
836 836
837 837 def prefix(self):
838 838 return self._matcher.prefix() and not self._always
839 839
840 840 @encoding.strmethod
841 841 def __repr__(self):
842 842 return ('<subdirmatcher path=%r, matcher=%r>' %
843 843 (self._path, self._matcher))
844 844
845 845 class prefixdirmatcher(basematcher):
846 846 """Adapt a matcher to work on a parent directory.
847 847
848 848 The matcher's non-matching-attributes (root, cwd, bad, explicitdir,
849 849 traversedir) are ignored.
850 850
851 851 The prefix path should usually be the relative path from the root of
852 852 this matcher to the root of the wrapped matcher.
853 853
854 854 >>> m1 = match(util.localpath(b'root/d/e'), b'f', [b'../a.txt', b'b.txt'])
855 855 >>> m2 = prefixdirmatcher(b'root', b'd/e/f', b'd/e', m1)
856 856 >>> bool(m2(b'a.txt'),)
857 857 False
858 858 >>> bool(m2(b'd/e/a.txt'))
859 859 True
860 860 >>> bool(m2(b'd/e/b.txt'))
861 861 False
862 862 >>> m2.files()
863 863 ['d/e/a.txt', 'd/e/f/b.txt']
864 864 >>> m2.exact(b'd/e/a.txt')
865 865 True
866 866 >>> m2.visitdir(b'd')
867 867 True
868 868 >>> m2.visitdir(b'd/e')
869 869 True
870 870 >>> m2.visitdir(b'd/e/f')
871 871 True
872 872 >>> m2.visitdir(b'd/e/g')
873 873 False
874 874 >>> m2.visitdir(b'd/ef')
875 875 False
876 876 """
877 877
878 878 def __init__(self, root, cwd, path, matcher, badfn=None):
879 879 super(prefixdirmatcher, self).__init__(root, cwd, badfn)
880 880 if not path:
881 881 raise error.ProgrammingError('prefix path must not be empty')
882 882 self._path = path
883 883 self._pathprefix = path + '/'
884 884 self._matcher = matcher
885 885
886 886 @propertycache
887 887 def _files(self):
888 888 return [self._pathprefix + f for f in self._matcher._files]
889 889
890 890 def matchfn(self, f):
891 891 if not f.startswith(self._pathprefix):
892 892 return False
893 893 return self._matcher.matchfn(f[len(self._pathprefix):])
894 894
895 895 @propertycache
896 896 def _pathdirs(self):
897 897 return set(util.finddirs(self._path)) | {'.'}
898 898
899 899 def visitdir(self, dir):
900 900 if dir == self._path:
901 901 return self._matcher.visitdir('.')
902 902 if dir.startswith(self._pathprefix):
903 903 return self._matcher.visitdir(dir[len(self._pathprefix):])
904 904 return dir in self._pathdirs
905 905
906 906 def visitchildrenset(self, dir):
907 907 if dir == self._path:
908 908 return self._matcher.visitchildrenset('.')
909 909 if dir.startswith(self._pathprefix):
910 910 return self._matcher.visitchildrenset(dir[len(self._pathprefix):])
911 911 if dir in self._pathdirs:
912 912 return 'this'
913 return set()
913 914
914 915 def isexact(self):
915 916 return self._matcher.isexact()
916 917
917 918 def prefix(self):
918 919 return self._matcher.prefix()
919 920
920 921 @encoding.strmethod
921 922 def __repr__(self):
922 923 return ('<prefixdirmatcher path=%r, matcher=%r>'
923 924 % (pycompat.bytestr(self._path), self._matcher))
924 925
925 926 class unionmatcher(basematcher):
926 927 """A matcher that is the union of several matchers.
927 928
928 929 The non-matching-attributes (root, cwd, bad, explicitdir, traversedir) are
929 930 taken from the first matcher.
930 931 """
931 932
932 933 def __init__(self, matchers):
933 934 m1 = matchers[0]
934 935 super(unionmatcher, self).__init__(m1._root, m1._cwd)
935 936 self.explicitdir = m1.explicitdir
936 937 self.traversedir = m1.traversedir
937 938 self._matchers = matchers
938 939
939 940 def matchfn(self, f):
940 941 for match in self._matchers:
941 942 if match(f):
942 943 return True
943 944 return False
944 945
945 946 def visitdir(self, dir):
946 947 r = False
947 948 for m in self._matchers:
948 949 v = m.visitdir(dir)
949 950 if v == 'all':
950 951 return v
951 952 r |= v
952 953 return r
953 954
954 955 def visitchildrenset(self, dir):
955 956 r = set()
956 957 this = False
957 958 for m in self._matchers:
958 959 v = m.visitchildrenset(dir)
959 960 if not v:
960 961 continue
961 962 if v == 'all':
962 963 return v
963 964 if this or v == 'this':
964 965 this = True
965 966 # don't break, we might have an 'all' in here.
966 967 continue
967 968 assert isinstance(v, set)
968 969 r = r.union(v)
969 970 if this:
970 971 return 'this'
971 972 return r
972 973
973 974 @encoding.strmethod
974 975 def __repr__(self):
975 976 return ('<unionmatcher matchers=%r>' % self._matchers)
976 977
977 978 def patkind(pattern, default=None):
978 979 '''If pattern is 'kind:pat' with a known kind, return kind.'''
979 980 return _patsplit(pattern, default)[0]
980 981
981 982 def _patsplit(pattern, default):
982 983 """Split a string into the optional pattern kind prefix and the actual
983 984 pattern."""
984 985 if ':' in pattern:
985 986 kind, pat = pattern.split(':', 1)
986 987 if kind in allpatternkinds:
987 988 return kind, pat
988 989 return default, pattern
989 990
990 991 def _globre(pat):
991 992 r'''Convert an extended glob string to a regexp string.
992 993
993 994 >>> from . import pycompat
994 995 >>> def bprint(s):
995 996 ... print(pycompat.sysstr(s))
996 997 >>> bprint(_globre(br'?'))
997 998 .
998 999 >>> bprint(_globre(br'*'))
999 1000 [^/]*
1000 1001 >>> bprint(_globre(br'**'))
1001 1002 .*
1002 1003 >>> bprint(_globre(br'**/a'))
1003 1004 (?:.*/)?a
1004 1005 >>> bprint(_globre(br'a/**/b'))
1005 1006 a/(?:.*/)?b
1006 1007 >>> bprint(_globre(br'[a*?!^][^b][!c]'))
1007 1008 [a*?!^][\^b][^c]
1008 1009 >>> bprint(_globre(br'{a,b}'))
1009 1010 (?:a|b)
1010 1011 >>> bprint(_globre(br'.\*\?'))
1011 1012 \.\*\?
1012 1013 '''
1013 1014 i, n = 0, len(pat)
1014 1015 res = ''
1015 1016 group = 0
1016 1017 escape = util.stringutil.reescape
1017 1018 def peek():
1018 1019 return i < n and pat[i:i + 1]
1019 1020 while i < n:
1020 1021 c = pat[i:i + 1]
1021 1022 i += 1
1022 1023 if c not in '*?[{},\\':
1023 1024 res += escape(c)
1024 1025 elif c == '*':
1025 1026 if peek() == '*':
1026 1027 i += 1
1027 1028 if peek() == '/':
1028 1029 i += 1
1029 1030 res += '(?:.*/)?'
1030 1031 else:
1031 1032 res += '.*'
1032 1033 else:
1033 1034 res += '[^/]*'
1034 1035 elif c == '?':
1035 1036 res += '.'
1036 1037 elif c == '[':
1037 1038 j = i
1038 1039 if j < n and pat[j:j + 1] in '!]':
1039 1040 j += 1
1040 1041 while j < n and pat[j:j + 1] != ']':
1041 1042 j += 1
1042 1043 if j >= n:
1043 1044 res += '\\['
1044 1045 else:
1045 1046 stuff = pat[i:j].replace('\\','\\\\')
1046 1047 i = j + 1
1047 1048 if stuff[0:1] == '!':
1048 1049 stuff = '^' + stuff[1:]
1049 1050 elif stuff[0:1] == '^':
1050 1051 stuff = '\\' + stuff
1051 1052 res = '%s[%s]' % (res, stuff)
1052 1053 elif c == '{':
1053 1054 group += 1
1054 1055 res += '(?:'
1055 1056 elif c == '}' and group:
1056 1057 res += ')'
1057 1058 group -= 1
1058 1059 elif c == ',' and group:
1059 1060 res += '|'
1060 1061 elif c == '\\':
1061 1062 p = peek()
1062 1063 if p:
1063 1064 i += 1
1064 1065 res += escape(p)
1065 1066 else:
1066 1067 res += escape(c)
1067 1068 else:
1068 1069 res += escape(c)
1069 1070 return res
1070 1071
1071 1072 def _regex(kind, pat, globsuffix):
1072 1073 '''Convert a (normalized) pattern of any kind into a regular expression.
1073 1074 globsuffix is appended to the regexp of globs.'''
1074 1075 if not pat:
1075 1076 return ''
1076 1077 if kind == 're':
1077 1078 return pat
1078 1079 if kind in ('path', 'relpath'):
1079 1080 if pat == '.':
1080 1081 return ''
1081 1082 return util.stringutil.reescape(pat) + '(?:/|$)'
1082 1083 if kind == 'rootfilesin':
1083 1084 if pat == '.':
1084 1085 escaped = ''
1085 1086 else:
1086 1087 # Pattern is a directory name.
1087 1088 escaped = util.stringutil.reescape(pat) + '/'
1088 1089 # Anything after the pattern must be a non-directory.
1089 1090 return escaped + '[^/]+$'
1090 1091 if kind == 'relglob':
1091 1092 return '(?:|.*/)' + _globre(pat) + globsuffix
1092 1093 if kind == 'relre':
1093 1094 if pat.startswith('^'):
1094 1095 return pat
1095 1096 return '.*' + pat
1096 1097 if kind == 'glob':
1097 1098 return _globre(pat) + globsuffix
1098 1099 raise error.ProgrammingError('not a regex pattern: %s:%s' % (kind, pat))
1099 1100
1100 1101 def _buildmatch(kindpats, globsuffix, listsubrepos, root):
1101 1102 '''Return regexp string and a matcher function for kindpats.
1102 1103 globsuffix is appended to the regexp of globs.'''
1103 1104 matchfuncs = []
1104 1105
1105 1106 subincludes, kindpats = _expandsubinclude(kindpats, root)
1106 1107 if subincludes:
1107 1108 submatchers = {}
1108 1109 def matchsubinclude(f):
1109 1110 for prefix, matcherargs in subincludes:
1110 1111 if f.startswith(prefix):
1111 1112 mf = submatchers.get(prefix)
1112 1113 if mf is None:
1113 1114 mf = match(*matcherargs)
1114 1115 submatchers[prefix] = mf
1115 1116
1116 1117 if mf(f[len(prefix):]):
1117 1118 return True
1118 1119 return False
1119 1120 matchfuncs.append(matchsubinclude)
1120 1121
1121 1122 regex = ''
1122 1123 if kindpats:
1123 1124 regex, mf = _buildregexmatch(kindpats, globsuffix)
1124 1125 matchfuncs.append(mf)
1125 1126
1126 1127 if len(matchfuncs) == 1:
1127 1128 return regex, matchfuncs[0]
1128 1129 else:
1129 1130 return regex, lambda f: any(mf(f) for mf in matchfuncs)
1130 1131
1131 1132 def _buildregexmatch(kindpats, globsuffix):
1132 1133 """Build a match function from a list of kinds and kindpats,
1133 1134 return regexp string and a matcher function."""
1134 1135 try:
1135 1136 regex = '(?:%s)' % '|'.join([_regex(k, p, globsuffix)
1136 1137 for (k, p, s) in kindpats])
1137 1138 if len(regex) > 20000:
1138 1139 raise OverflowError
1139 1140 return regex, _rematcher(regex)
1140 1141 except OverflowError:
1141 1142 # We're using a Python with a tiny regex engine and we
1142 1143 # made it explode, so we'll divide the pattern list in two
1143 1144 # until it works
1144 1145 l = len(kindpats)
1145 1146 if l < 2:
1146 1147 raise
1147 1148 regexa, a = _buildregexmatch(kindpats[:l//2], globsuffix)
1148 1149 regexb, b = _buildregexmatch(kindpats[l//2:], globsuffix)
1149 1150 return regex, lambda s: a(s) or b(s)
1150 1151 except re.error:
1151 1152 for k, p, s in kindpats:
1152 1153 try:
1153 1154 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
1154 1155 except re.error:
1155 1156 if s:
1156 1157 raise error.Abort(_("%s: invalid pattern (%s): %s") %
1157 1158 (s, k, p))
1158 1159 else:
1159 1160 raise error.Abort(_("invalid pattern (%s): %s") % (k, p))
1160 1161 raise error.Abort(_("invalid pattern"))
1161 1162
1162 1163 def _patternrootsanddirs(kindpats):
1163 1164 '''Returns roots and directories corresponding to each pattern.
1164 1165
1165 1166 This calculates the roots and directories exactly matching the patterns and
1166 1167 returns a tuple of (roots, dirs) for each. It does not return other
1167 1168 directories which may also need to be considered, like the parent
1168 1169 directories.
1169 1170 '''
1170 1171 r = []
1171 1172 d = []
1172 1173 for kind, pat, source in kindpats:
1173 1174 if kind == 'glob': # find the non-glob prefix
1174 1175 root = []
1175 1176 for p in pat.split('/'):
1176 1177 if '[' in p or '{' in p or '*' in p or '?' in p:
1177 1178 break
1178 1179 root.append(p)
1179 1180 r.append('/'.join(root) or '.')
1180 1181 elif kind in ('relpath', 'path'):
1181 1182 r.append(pat or '.')
1182 1183 elif kind in ('rootfilesin',):
1183 1184 d.append(pat or '.')
1184 1185 else: # relglob, re, relre
1185 1186 r.append('.')
1186 1187 return r, d
1187 1188
1188 1189 def _roots(kindpats):
1189 1190 '''Returns root directories to match recursively from the given patterns.'''
1190 1191 roots, dirs = _patternrootsanddirs(kindpats)
1191 1192 return roots
1192 1193
1193 1194 def _rootsdirsandparents(kindpats):
1194 1195 '''Returns roots and exact directories from patterns.
1195 1196
1196 1197 `roots` are directories to match recursively, `dirs` should
1197 1198 be matched non-recursively, and `parents` are the implicitly required
1198 1199 directories to walk to items in either roots or dirs.
1199 1200
1200 1201 Returns a tuple of (roots, dirs, parents).
1201 1202
1202 1203 >>> _rootsdirsandparents(
1203 1204 ... [(b'glob', b'g/h/*', b''), (b'glob', b'g/h', b''),
1204 1205 ... (b'glob', b'g*', b'')])
1205 1206 (['g/h', 'g/h', '.'], [], ['g', '.'])
1206 1207 >>> _rootsdirsandparents(
1207 1208 ... [(b'rootfilesin', b'g/h', b''), (b'rootfilesin', b'', b'')])
1208 1209 ([], ['g/h', '.'], ['g', '.'])
1209 1210 >>> _rootsdirsandparents(
1210 1211 ... [(b'relpath', b'r', b''), (b'path', b'p/p', b''),
1211 1212 ... (b'path', b'', b'')])
1212 1213 (['r', 'p/p', '.'], [], ['p', '.'])
1213 1214 >>> _rootsdirsandparents(
1214 1215 ... [(b'relglob', b'rg*', b''), (b're', b're/', b''),
1215 1216 ... (b'relre', b'rr', b'')])
1216 1217 (['.', '.', '.'], [], ['.'])
1217 1218 '''
1218 1219 r, d = _patternrootsanddirs(kindpats)
1219 1220
1220 1221 p = []
1221 1222 # Append the parents as non-recursive/exact directories, since they must be
1222 1223 # scanned to get to either the roots or the other exact directories.
1223 1224 p.extend(util.dirs(d))
1224 1225 p.extend(util.dirs(r))
1225 1226 # util.dirs() does not include the root directory, so add it manually
1226 1227 p.append('.')
1227 1228
1228 1229 return r, d, p
1229 1230
1230 1231 def _explicitfiles(kindpats):
1231 1232 '''Returns the potential explicit filenames from the patterns.
1232 1233
1233 1234 >>> _explicitfiles([(b'path', b'foo/bar', b'')])
1234 1235 ['foo/bar']
1235 1236 >>> _explicitfiles([(b'rootfilesin', b'foo/bar', b'')])
1236 1237 []
1237 1238 '''
1238 1239 # Keep only the pattern kinds where one can specify filenames (vs only
1239 1240 # directory names).
1240 1241 filable = [kp for kp in kindpats if kp[0] not in ('rootfilesin',)]
1241 1242 return _roots(filable)
1242 1243
1243 1244 def _prefix(kindpats):
1244 1245 '''Whether all the patterns match a prefix (i.e. recursively)'''
1245 1246 for kind, pat, source in kindpats:
1246 1247 if kind not in ('path', 'relpath'):
1247 1248 return False
1248 1249 return True
1249 1250
1250 1251 _commentre = None
1251 1252
1252 1253 def readpatternfile(filepath, warn, sourceinfo=False):
1253 1254 '''parse a pattern file, returning a list of
1254 1255 patterns. These patterns should be given to compile()
1255 1256 to be validated and converted into a match function.
1256 1257
1257 1258 trailing white space is dropped.
1258 1259 the escape character is backslash.
1259 1260 comments start with #.
1260 1261 empty lines are skipped.
1261 1262
1262 1263 lines can be of the following formats:
1263 1264
1264 1265 syntax: regexp # defaults following lines to non-rooted regexps
1265 1266 syntax: glob # defaults following lines to non-rooted globs
1266 1267 re:pattern # non-rooted regular expression
1267 1268 glob:pattern # non-rooted glob
1268 1269 pattern # pattern of the current default type
1269 1270
1270 1271 if sourceinfo is set, returns a list of tuples:
1271 1272 (pattern, lineno, originalline). This is useful to debug ignore patterns.
1272 1273 '''
1273 1274
1274 1275 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:',
1275 1276 'include': 'include', 'subinclude': 'subinclude'}
1276 1277 syntax = 'relre:'
1277 1278 patterns = []
1278 1279
1279 1280 fp = open(filepath, 'rb')
1280 1281 for lineno, line in enumerate(util.iterfile(fp), start=1):
1281 1282 if "#" in line:
1282 1283 global _commentre
1283 1284 if not _commentre:
1284 1285 _commentre = util.re.compile(br'((?:^|[^\\])(?:\\\\)*)#.*')
1285 1286 # remove comments prefixed by an even number of escapes
1286 1287 m = _commentre.search(line)
1287 1288 if m:
1288 1289 line = line[:m.end(1)]
1289 1290 # fixup properly escaped comments that survived the above
1290 1291 line = line.replace("\\#", "#")
1291 1292 line = line.rstrip()
1292 1293 if not line:
1293 1294 continue
1294 1295
1295 1296 if line.startswith('syntax:'):
1296 1297 s = line[7:].strip()
1297 1298 try:
1298 1299 syntax = syntaxes[s]
1299 1300 except KeyError:
1300 1301 if warn:
1301 1302 warn(_("%s: ignoring invalid syntax '%s'\n") %
1302 1303 (filepath, s))
1303 1304 continue
1304 1305
1305 1306 linesyntax = syntax
1306 1307 for s, rels in syntaxes.iteritems():
1307 1308 if line.startswith(rels):
1308 1309 linesyntax = rels
1309 1310 line = line[len(rels):]
1310 1311 break
1311 1312 elif line.startswith(s+':'):
1312 1313 linesyntax = rels
1313 1314 line = line[len(s) + 1:]
1314 1315 break
1315 1316 if sourceinfo:
1316 1317 patterns.append((linesyntax + line, lineno, line))
1317 1318 else:
1318 1319 patterns.append(linesyntax + line)
1319 1320 fp.close()
1320 1321 return patterns
@@ -1,817 +1,819
1 1 from __future__ import absolute_import
2 2
3 3 import unittest
4 4
5 5 import silenttestrunner
6 6
7 7 from mercurial import (
8 8 match as matchmod,
9 9 util,
10 10 )
11 11
12 12 class BaseMatcherTests(unittest.TestCase):
13 13
14 14 def testVisitdir(self):
15 15 m = matchmod.basematcher('', '')
16 16 self.assertTrue(m.visitdir('.'))
17 17 self.assertTrue(m.visitdir('dir'))
18 18
19 19 def testVisitchildrenset(self):
20 20 m = matchmod.basematcher('', '')
21 21 self.assertEqual(m.visitchildrenset('.'), 'this')
22 22 self.assertEqual(m.visitchildrenset('dir'), 'this')
23 23
24 24 class AlwaysMatcherTests(unittest.TestCase):
25 25
26 26 def testVisitdir(self):
27 27 m = matchmod.alwaysmatcher('', '')
28 28 self.assertEqual(m.visitdir('.'), 'all')
29 29 self.assertEqual(m.visitdir('dir'), 'all')
30 30
31 31 def testVisitchildrenset(self):
32 32 m = matchmod.alwaysmatcher('', '')
33 33 self.assertEqual(m.visitchildrenset('.'), 'all')
34 34 self.assertEqual(m.visitchildrenset('dir'), 'all')
35 35
36 36 class NeverMatcherTests(unittest.TestCase):
37 37
38 38 def testVisitdir(self):
39 39 m = matchmod.nevermatcher('', '')
40 40 self.assertFalse(m.visitdir('.'))
41 41 self.assertFalse(m.visitdir('dir'))
42 42
43 43 def testVisitchildrenset(self):
44 44 m = matchmod.nevermatcher('', '')
45 45 self.assertEqual(m.visitchildrenset('.'), set())
46 46 self.assertEqual(m.visitchildrenset('dir'), set())
47 47
48 48 class PredicateMatcherTests(unittest.TestCase):
49 49 # predicatematcher does not currently define either of these methods, so
50 50 # this is equivalent to BaseMatcherTests.
51 51
52 52 def testVisitdir(self):
53 53 m = matchmod.predicatematcher('', '', lambda *a: False)
54 54 self.assertTrue(m.visitdir('.'))
55 55 self.assertTrue(m.visitdir('dir'))
56 56
57 57 def testVisitchildrenset(self):
58 58 m = matchmod.predicatematcher('', '', lambda *a: False)
59 59 self.assertEqual(m.visitchildrenset('.'), 'this')
60 60 self.assertEqual(m.visitchildrenset('dir'), 'this')
61 61
62 62 class PatternMatcherTests(unittest.TestCase):
63 63
64 64 def testVisitdirPrefix(self):
65 65 m = matchmod.match('x', '', patterns=['path:dir/subdir'])
66 66 assert isinstance(m, matchmod.patternmatcher)
67 67 self.assertTrue(m.visitdir('.'))
68 68 self.assertTrue(m.visitdir('dir'))
69 69 self.assertEqual(m.visitdir('dir/subdir'), 'all')
70 70 # OPT: This should probably be 'all' if its parent is?
71 71 self.assertTrue(m.visitdir('dir/subdir/x'))
72 72 self.assertFalse(m.visitdir('folder'))
73 73
74 74 def testVisitchildrensetPrefix(self):
75 75 m = matchmod.match('x', '', patterns=['path:dir/subdir'])
76 76 assert isinstance(m, matchmod.patternmatcher)
77 77 self.assertEqual(m.visitchildrenset('.'), 'this')
78 78 self.assertEqual(m.visitchildrenset('dir'), 'this')
79 79 self.assertEqual(m.visitchildrenset('dir/subdir'), 'all')
80 80 # OPT: This should probably be 'all' if its parent is?
81 81 self.assertEqual(m.visitchildrenset('dir/subdir/x'), 'this')
82 82 self.assertEqual(m.visitchildrenset('folder'), set())
83 83
84 84 def testVisitdirRootfilesin(self):
85 85 m = matchmod.match('x', '', patterns=['rootfilesin:dir/subdir'])
86 86 assert isinstance(m, matchmod.patternmatcher)
87 87 self.assertTrue(m.visitdir('.'))
88 88 self.assertFalse(m.visitdir('dir/subdir/x'))
89 89 self.assertFalse(m.visitdir('folder'))
90 90 # FIXME: These should probably be True.
91 91 self.assertFalse(m.visitdir('dir'))
92 92 self.assertFalse(m.visitdir('dir/subdir'))
93 93
94 94 def testVisitchildrensetRootfilesin(self):
95 95 m = matchmod.match('x', '', patterns=['rootfilesin:dir/subdir'])
96 96 assert isinstance(m, matchmod.patternmatcher)
97 97 self.assertEqual(m.visitchildrenset('.'), 'this')
98 98 self.assertEqual(m.visitchildrenset('dir/subdir/x'), set())
99 99 self.assertEqual(m.visitchildrenset('folder'), set())
100 # FIXME: These should probably be {'subdir'} and 'this', respectively,
101 # or at least 'this' and 'this'.
100 102 self.assertEqual(m.visitchildrenset('dir'), set())
101 103 self.assertEqual(m.visitchildrenset('dir/subdir'), set())
102 104
103 105 def testVisitdirGlob(self):
104 106 m = matchmod.match('x', '', patterns=['glob:dir/z*'])
105 107 assert isinstance(m, matchmod.patternmatcher)
106 108 self.assertTrue(m.visitdir('.'))
107 109 self.assertTrue(m.visitdir('dir'))
108 110 self.assertFalse(m.visitdir('folder'))
109 111 # OPT: these should probably be False.
110 112 self.assertTrue(m.visitdir('dir/subdir'))
111 113 self.assertTrue(m.visitdir('dir/subdir/x'))
112 114
113 115 def testVisitchildrensetGlob(self):
114 116 m = matchmod.match('x', '', patterns=['glob:dir/z*'])
115 117 assert isinstance(m, matchmod.patternmatcher)
116 118 self.assertEqual(m.visitchildrenset('.'), 'this')
117 119 self.assertEqual(m.visitchildrenset('folder'), set())
118 120 self.assertEqual(m.visitchildrenset('dir'), 'this')
119 121 # OPT: these should probably be set().
120 122 self.assertEqual(m.visitchildrenset('dir/subdir'), 'this')
121 123 self.assertEqual(m.visitchildrenset('dir/subdir/x'), 'this')
122 124
123 125 class IncludeMatcherTests(unittest.TestCase):
124 126
125 127 def testVisitdirPrefix(self):
126 128 m = matchmod.match('x', '', include=['path:dir/subdir'])
127 129 assert isinstance(m, matchmod.includematcher)
128 130 self.assertTrue(m.visitdir('.'))
129 131 self.assertTrue(m.visitdir('dir'))
130 132 self.assertEqual(m.visitdir('dir/subdir'), 'all')
131 133 # OPT: This should probably be 'all' if its parent is?
132 134 self.assertTrue(m.visitdir('dir/subdir/x'))
133 135 self.assertFalse(m.visitdir('folder'))
134 136
135 137 def testVisitchildrensetPrefix(self):
136 138 m = matchmod.match('x', '', include=['path:dir/subdir'])
137 139 assert isinstance(m, matchmod.includematcher)
138 140 self.assertEqual(m.visitchildrenset('.'), {'dir'})
139 141 self.assertEqual(m.visitchildrenset('dir'), {'subdir'})
140 142 self.assertEqual(m.visitchildrenset('dir/subdir'), 'all')
141 143 # OPT: This should probably be 'all' if its parent is?
142 144 self.assertEqual(m.visitchildrenset('dir/subdir/x'), 'this')
143 145 self.assertEqual(m.visitchildrenset('folder'), set())
144 146
145 147 def testVisitdirRootfilesin(self):
146 148 m = matchmod.match('x', '', include=['rootfilesin:dir/subdir'])
147 149 assert isinstance(m, matchmod.includematcher)
148 150 self.assertTrue(m.visitdir('.'))
149 151 self.assertTrue(m.visitdir('dir'))
150 152 self.assertTrue(m.visitdir('dir/subdir'))
151 153 self.assertFalse(m.visitdir('dir/subdir/x'))
152 154 self.assertFalse(m.visitdir('folder'))
153 155
154 156 def testVisitchildrensetRootfilesin(self):
155 157 m = matchmod.match('x', '', include=['rootfilesin:dir/subdir'])
156 158 assert isinstance(m, matchmod.includematcher)
157 159 self.assertEqual(m.visitchildrenset('.'), {'dir'})
158 160 self.assertEqual(m.visitchildrenset('dir'), {'subdir'})
159 161 self.assertEqual(m.visitchildrenset('dir/subdir'), 'this')
160 162 self.assertEqual(m.visitchildrenset('dir/subdir/x'), set())
161 163 self.assertEqual(m.visitchildrenset('folder'), set())
162 164
163 165 def testVisitdirGlob(self):
164 166 m = matchmod.match('x', '', include=['glob:dir/z*'])
165 167 assert isinstance(m, matchmod.includematcher)
166 168 self.assertTrue(m.visitdir('.'))
167 169 self.assertTrue(m.visitdir('dir'))
168 170 self.assertFalse(m.visitdir('folder'))
169 171 # OPT: these should probably be False.
170 172 self.assertTrue(m.visitdir('dir/subdir'))
171 173 self.assertTrue(m.visitdir('dir/subdir/x'))
172 174
173 175 def testVisitchildrensetGlob(self):
174 176 m = matchmod.match('x', '', include=['glob:dir/z*'])
175 177 assert isinstance(m, matchmod.includematcher)
176 178 self.assertEqual(m.visitchildrenset('.'), {'dir'})
177 179 self.assertEqual(m.visitchildrenset('folder'), set())
178 180 self.assertEqual(m.visitchildrenset('dir'), 'this')
179 181 # OPT: these should probably be set().
180 182 self.assertEqual(m.visitchildrenset('dir/subdir'), 'this')
181 183 self.assertEqual(m.visitchildrenset('dir/subdir/x'), 'this')
182 184
183 185 class ExactMatcherTests(unittest.TestCase):
184 186
185 187 def testVisitdir(self):
186 188 m = matchmod.match('x', '', patterns=['dir/subdir/foo.txt'], exact=True)
187 189 assert isinstance(m, matchmod.exactmatcher)
188 190 self.assertTrue(m.visitdir('.'))
189 191 self.assertTrue(m.visitdir('dir'))
190 192 self.assertTrue(m.visitdir('dir/subdir'))
191 193 self.assertFalse(m.visitdir('dir/subdir/foo.txt'))
192 194 self.assertFalse(m.visitdir('dir/foo'))
193 195 self.assertFalse(m.visitdir('dir/subdir/x'))
194 196 self.assertFalse(m.visitdir('folder'))
195 197
196 198 def testVisitchildrenset(self):
197 199 m = matchmod.match('x', '', patterns=['dir/subdir/foo.txt'], exact=True)
198 200 assert isinstance(m, matchmod.exactmatcher)
199 201 self.assertEqual(m.visitchildrenset('.'), {'dir'})
200 202 self.assertEqual(m.visitchildrenset('dir'), {'subdir'})
201 203 self.assertEqual(m.visitchildrenset('dir/subdir'), 'this')
202 204 self.assertEqual(m.visitchildrenset('dir/subdir/x'), set())
203 205 self.assertEqual(m.visitchildrenset('dir/subdir/foo.txt'), set())
204 206 self.assertEqual(m.visitchildrenset('folder'), set())
205 207
206 208 class DifferenceMatcherTests(unittest.TestCase):
207 209
208 210 def testVisitdirM2always(self):
209 211 m1 = matchmod.alwaysmatcher('', '')
210 212 m2 = matchmod.alwaysmatcher('', '')
211 213 dm = matchmod.differencematcher(m1, m2)
212 214 # dm should be equivalent to a nevermatcher.
213 215 self.assertFalse(dm.visitdir('.'))
214 216 self.assertFalse(dm.visitdir('dir'))
215 217 self.assertFalse(dm.visitdir('dir/subdir'))
216 218 self.assertFalse(dm.visitdir('dir/subdir/z'))
217 219 self.assertFalse(dm.visitdir('dir/foo'))
218 220 self.assertFalse(dm.visitdir('dir/subdir/x'))
219 221 self.assertFalse(dm.visitdir('folder'))
220 222
221 223 def testVisitchildrensetM2always(self):
222 224 m1 = matchmod.alwaysmatcher('', '')
223 225 m2 = matchmod.alwaysmatcher('', '')
224 226 dm = matchmod.differencematcher(m1, m2)
225 227 # dm should be equivalent to a nevermatcher.
226 228 self.assertEqual(dm.visitchildrenset('.'), set())
227 229 self.assertEqual(dm.visitchildrenset('dir'), set())
228 230 self.assertEqual(dm.visitchildrenset('dir/subdir'), set())
229 231 self.assertEqual(dm.visitchildrenset('dir/subdir/z'), set())
230 232 self.assertEqual(dm.visitchildrenset('dir/foo'), set())
231 233 self.assertEqual(dm.visitchildrenset('dir/subdir/x'), set())
232 234 self.assertEqual(dm.visitchildrenset('folder'), set())
233 235
234 236 def testVisitdirM2never(self):
235 237 m1 = matchmod.alwaysmatcher('', '')
236 238 m2 = matchmod.nevermatcher('', '')
237 239 dm = matchmod.differencematcher(m1, m2)
238 240 # dm should be equivalent to a alwaysmatcher. OPT: if m2 is a
239 241 # nevermatcher, we could return 'all' for these.
240 242 #
241 243 # We're testing Equal-to-True instead of just 'assertTrue' since
242 244 # assertTrue does NOT verify that it's a bool, just that it's truthy.
243 245 # While we may want to eventually make these return 'all', they should
244 246 # not currently do so.
245 247 self.assertEqual(dm.visitdir('.'), True)
246 248 self.assertEqual(dm.visitdir('dir'), True)
247 249 self.assertEqual(dm.visitdir('dir/subdir'), True)
248 250 self.assertEqual(dm.visitdir('dir/subdir/z'), True)
249 251 self.assertEqual(dm.visitdir('dir/foo'), True)
250 252 self.assertEqual(dm.visitdir('dir/subdir/x'), True)
251 253 self.assertEqual(dm.visitdir('folder'), True)
252 254
253 255 def testVisitchildrensetM2never(self):
254 256 m1 = matchmod.alwaysmatcher('', '')
255 257 m2 = matchmod.nevermatcher('', '')
256 258 dm = matchmod.differencematcher(m1, m2)
257 259 # dm should be equivalent to a alwaysmatcher.
258 260 self.assertEqual(dm.visitchildrenset('.'), 'all')
259 261 self.assertEqual(dm.visitchildrenset('dir'), 'all')
260 262 self.assertEqual(dm.visitchildrenset('dir/subdir'), 'all')
261 263 self.assertEqual(dm.visitchildrenset('dir/subdir/z'), 'all')
262 264 self.assertEqual(dm.visitchildrenset('dir/foo'), 'all')
263 265 self.assertEqual(dm.visitchildrenset('dir/subdir/x'), 'all')
264 266 self.assertEqual(dm.visitchildrenset('folder'), 'all')
265 267
266 268 def testVisitdirM2SubdirPrefix(self):
267 269 m1 = matchmod.alwaysmatcher('', '')
268 270 m2 = matchmod.match('', '', patterns=['path:dir/subdir'])
269 271 dm = matchmod.differencematcher(m1, m2)
270 272 self.assertEqual(dm.visitdir('.'), True)
271 273 self.assertEqual(dm.visitdir('dir'), True)
272 274 self.assertFalse(dm.visitdir('dir/subdir'))
273 275 # OPT: We should probably return False for these; we don't because
274 276 # patternmatcher.visitdir() (our m2) doesn't return 'all' for subdirs of
275 277 # an 'all' pattern, just True.
276 278 self.assertEqual(dm.visitdir('dir/subdir/z'), True)
277 279 self.assertEqual(dm.visitdir('dir/subdir/x'), True)
278 280 # OPT: We could return 'all' for these.
279 281 self.assertEqual(dm.visitdir('dir/foo'), True)
280 282 self.assertEqual(dm.visitdir('folder'), True)
281 283
282 284 def testVisitchildrensetM2SubdirPrefix(self):
283 285 m1 = matchmod.alwaysmatcher('', '')
284 286 m2 = matchmod.match('', '', patterns=['path:dir/subdir'])
285 287 dm = matchmod.differencematcher(m1, m2)
286 288 self.assertEqual(dm.visitchildrenset('.'), 'this')
287 289 self.assertEqual(dm.visitchildrenset('dir'), 'this')
288 290 self.assertEqual(dm.visitchildrenset('dir/subdir'), set())
289 291 self.assertEqual(dm.visitchildrenset('dir/foo'), 'all')
290 292 self.assertEqual(dm.visitchildrenset('folder'), 'all')
291 293 # OPT: We should probably return set() for these; we don't because
292 294 # patternmatcher.visitdir() (our m2) doesn't return 'all' for subdirs of
293 295 # an 'all' pattern, just 'this'.
294 296 self.assertEqual(dm.visitchildrenset('dir/subdir/z'), 'this')
295 297 self.assertEqual(dm.visitchildrenset('dir/subdir/x'), 'this')
296 298
297 299 # We're using includematcher instead of patterns because it behaves slightly
298 300 # better (giving narrower results) than patternmatcher.
299 301 def testVisitdirIncludeIncludfe(self):
300 302 m1 = matchmod.match('', '', include=['path:dir/subdir'])
301 303 m2 = matchmod.match('', '', include=['rootfilesin:dir'])
302 304 dm = matchmod.differencematcher(m1, m2)
303 305 self.assertEqual(dm.visitdir('.'), True)
304 306 self.assertEqual(dm.visitdir('dir'), True)
305 307 self.assertEqual(dm.visitdir('dir/subdir'), True)
306 308 self.assertFalse(dm.visitdir('dir/foo'))
307 309 self.assertFalse(dm.visitdir('folder'))
308 310 # OPT: We should probably return False for these; we don't because
309 311 # patternmatcher.visitdir() (our m2) doesn't return 'all' for subdirs of
310 312 # an 'all' pattern, just True.
311 313 self.assertEqual(dm.visitdir('dir/subdir/z'), True)
312 314 self.assertEqual(dm.visitdir('dir/subdir/x'), True)
313 315
314 316 def testVisitchildrensetIncludeInclude(self):
315 317 m1 = matchmod.match('', '', include=['path:dir/subdir'])
316 318 m2 = matchmod.match('', '', include=['rootfilesin:dir'])
317 319 dm = matchmod.differencematcher(m1, m2)
318 320 self.assertEqual(dm.visitchildrenset('.'), {'dir'})
319 321 self.assertEqual(dm.visitchildrenset('dir'), {'subdir'})
320 322 self.assertEqual(dm.visitchildrenset('dir/subdir'), 'all')
321 323 self.assertEqual(dm.visitchildrenset('dir/foo'), set())
322 324 self.assertEqual(dm.visitchildrenset('folder'), set())
323 325 # OPT: We should probably return set() for these; we don't because
324 326 # patternmatcher.visitdir() (our m2) doesn't return 'all' for subdirs of
325 327 # an 'all' pattern, just 'this'.
326 328 self.assertEqual(dm.visitchildrenset('dir/subdir/z'), 'this')
327 329 self.assertEqual(dm.visitchildrenset('dir/subdir/x'), 'this')
328 330
329 331 class IntersectionMatcherTests(unittest.TestCase):
330 332
331 333 def testVisitdirM2always(self):
332 334 m1 = matchmod.alwaysmatcher('', '')
333 335 m2 = matchmod.alwaysmatcher('', '')
334 336 im = matchmod.intersectmatchers(m1, m2)
335 337 # im should be equivalent to a alwaysmatcher.
336 338 self.assertEqual(im.visitdir('.'), 'all')
337 339 self.assertEqual(im.visitdir('dir'), 'all')
338 340 self.assertEqual(im.visitdir('dir/subdir'), 'all')
339 341 self.assertEqual(im.visitdir('dir/subdir/z'), 'all')
340 342 self.assertEqual(im.visitdir('dir/foo'), 'all')
341 343 self.assertEqual(im.visitdir('dir/subdir/x'), 'all')
342 344 self.assertEqual(im.visitdir('folder'), 'all')
343 345
344 346 def testVisitchildrensetM2always(self):
345 347 m1 = matchmod.alwaysmatcher('', '')
346 348 m2 = matchmod.alwaysmatcher('', '')
347 349 im = matchmod.intersectmatchers(m1, m2)
348 350 # im should be equivalent to a alwaysmatcher.
349 351 self.assertEqual(im.visitchildrenset('.'), 'all')
350 352 self.assertEqual(im.visitchildrenset('dir'), 'all')
351 353 self.assertEqual(im.visitchildrenset('dir/subdir'), 'all')
352 354 self.assertEqual(im.visitchildrenset('dir/subdir/z'), 'all')
353 355 self.assertEqual(im.visitchildrenset('dir/foo'), 'all')
354 356 self.assertEqual(im.visitchildrenset('dir/subdir/x'), 'all')
355 357 self.assertEqual(im.visitchildrenset('folder'), 'all')
356 358
357 359 def testVisitdirM2never(self):
358 360 m1 = matchmod.alwaysmatcher('', '')
359 361 m2 = matchmod.nevermatcher('', '')
360 362 im = matchmod.intersectmatchers(m1, m2)
361 363 # im should be equivalent to a nevermatcher.
362 364 self.assertFalse(im.visitdir('.'))
363 365 self.assertFalse(im.visitdir('dir'))
364 366 self.assertFalse(im.visitdir('dir/subdir'))
365 367 self.assertFalse(im.visitdir('dir/subdir/z'))
366 368 self.assertFalse(im.visitdir('dir/foo'))
367 369 self.assertFalse(im.visitdir('dir/subdir/x'))
368 370 self.assertFalse(im.visitdir('folder'))
369 371
370 372 def testVisitchildrensetM2never(self):
371 373 m1 = matchmod.alwaysmatcher('', '')
372 374 m2 = matchmod.nevermatcher('', '')
373 375 im = matchmod.intersectmatchers(m1, m2)
374 376 # im should be equivalent to a nevermqtcher.
375 377 self.assertEqual(im.visitchildrenset('.'), set())
376 378 self.assertEqual(im.visitchildrenset('dir'), set())
377 379 self.assertEqual(im.visitchildrenset('dir/subdir'), set())
378 380 self.assertEqual(im.visitchildrenset('dir/subdir/z'), set())
379 381 self.assertEqual(im.visitchildrenset('dir/foo'), set())
380 382 self.assertEqual(im.visitchildrenset('dir/subdir/x'), set())
381 383 self.assertEqual(im.visitchildrenset('folder'), set())
382 384
383 385 def testVisitdirM2SubdirPrefix(self):
384 386 m1 = matchmod.alwaysmatcher('', '')
385 387 m2 = matchmod.match('', '', patterns=['path:dir/subdir'])
386 388 im = matchmod.intersectmatchers(m1, m2)
387 389 self.assertEqual(im.visitdir('.'), True)
388 390 self.assertEqual(im.visitdir('dir'), True)
389 391 self.assertEqual(im.visitdir('dir/subdir'), 'all')
390 392 self.assertFalse(im.visitdir('dir/foo'))
391 393 self.assertFalse(im.visitdir('folder'))
392 394 # OPT: We should probably return 'all' for these; we don't because
393 395 # patternmatcher.visitdir() (our m2) doesn't return 'all' for subdirs of
394 396 # an 'all' pattern, just True.
395 397 self.assertEqual(im.visitdir('dir/subdir/z'), True)
396 398 self.assertEqual(im.visitdir('dir/subdir/x'), True)
397 399
398 400 def testVisitchildrensetM2SubdirPrefix(self):
399 401 m1 = matchmod.alwaysmatcher('', '')
400 402 m2 = matchmod.match('', '', include=['path:dir/subdir'])
401 403 im = matchmod.intersectmatchers(m1, m2)
402 404 self.assertEqual(im.visitchildrenset('.'), {'dir'})
403 405 self.assertEqual(im.visitchildrenset('dir'), {'subdir'})
404 406 self.assertEqual(im.visitchildrenset('dir/subdir'), 'all')
405 407 self.assertEqual(im.visitchildrenset('dir/foo'), set())
406 408 self.assertEqual(im.visitchildrenset('folder'), set())
407 409 # OPT: We should probably return 'all' for these
408 410 self.assertEqual(im.visitchildrenset('dir/subdir/z'), 'this')
409 411 self.assertEqual(im.visitchildrenset('dir/subdir/x'), 'this')
410 412
411 413 # We're using includematcher instead of patterns because it behaves slightly
412 414 # better (giving narrower results) than patternmatcher.
413 415 def testVisitdirIncludeIncludfe(self):
414 416 m1 = matchmod.match('', '', include=['path:dir/subdir'])
415 417 m2 = matchmod.match('', '', include=['rootfilesin:dir'])
416 418 im = matchmod.intersectmatchers(m1, m2)
417 419 self.assertEqual(im.visitdir('.'), True)
418 420 self.assertEqual(im.visitdir('dir'), True)
419 421 self.assertFalse(im.visitdir('dir/subdir'))
420 422 self.assertFalse(im.visitdir('dir/foo'))
421 423 self.assertFalse(im.visitdir('folder'))
422 424 self.assertFalse(im.visitdir('dir/subdir/z'))
423 425 self.assertFalse(im.visitdir('dir/subdir/x'))
424 426
425 427 def testVisitchildrensetIncludeInclude(self):
426 428 m1 = matchmod.match('', '', include=['path:dir/subdir'])
427 429 m2 = matchmod.match('', '', include=['rootfilesin:dir'])
428 430 im = matchmod.intersectmatchers(m1, m2)
429 431 self.assertEqual(im.visitchildrenset('.'), {'dir'})
430 432 self.assertEqual(im.visitchildrenset('dir'), 'this')
431 433 self.assertEqual(im.visitchildrenset('dir/subdir'), set())
432 434 self.assertEqual(im.visitchildrenset('dir/foo'), set())
433 435 self.assertEqual(im.visitchildrenset('folder'), set())
434 436 self.assertEqual(im.visitchildrenset('dir/subdir/z'), set())
435 437 self.assertEqual(im.visitchildrenset('dir/subdir/x'), set())
436 438
437 439 # We're using includematcher instead of patterns because it behaves slightly
438 440 # better (giving narrower results) than patternmatcher.
439 441 def testVisitdirIncludeInclude2(self):
440 442 m1 = matchmod.match('', '', include=['path:dir/subdir'])
441 443 m2 = matchmod.match('', '', include=['path:folder'])
442 444 im = matchmod.intersectmatchers(m1, m2)
443 445 # FIXME: is True correct here?
444 446 self.assertEqual(im.visitdir('.'), True)
445 447 self.assertFalse(im.visitdir('dir'))
446 448 self.assertFalse(im.visitdir('dir/subdir'))
447 449 self.assertFalse(im.visitdir('dir/foo'))
448 450 self.assertFalse(im.visitdir('folder'))
449 451 self.assertFalse(im.visitdir('dir/subdir/z'))
450 452 self.assertFalse(im.visitdir('dir/subdir/x'))
451 453
452 454 def testVisitchildrensetIncludeInclude2(self):
453 455 m1 = matchmod.match('', '', include=['path:dir/subdir'])
454 456 m2 = matchmod.match('', '', include=['path:folder'])
455 457 im = matchmod.intersectmatchers(m1, m2)
456 458 # FIXME: is set() correct here?
457 459 self.assertEqual(im.visitchildrenset('.'), set())
458 460 self.assertEqual(im.visitchildrenset('dir'), set())
459 461 self.assertEqual(im.visitchildrenset('dir/subdir'), set())
460 462 self.assertEqual(im.visitchildrenset('dir/foo'), set())
461 463 self.assertEqual(im.visitchildrenset('folder'), set())
462 464 self.assertEqual(im.visitchildrenset('dir/subdir/z'), set())
463 465 self.assertEqual(im.visitchildrenset('dir/subdir/x'), set())
464 466
465 467 # We're using includematcher instead of patterns because it behaves slightly
466 468 # better (giving narrower results) than patternmatcher.
467 469 def testVisitdirIncludeInclude3(self):
468 470 m1 = matchmod.match('', '', include=['path:dir/subdir/x'])
469 471 m2 = matchmod.match('', '', include=['path:dir/subdir'])
470 472 im = matchmod.intersectmatchers(m1, m2)
471 473 self.assertEqual(im.visitdir('.'), True)
472 474 self.assertEqual(im.visitdir('dir'), True)
473 475 self.assertEqual(im.visitdir('dir/subdir'), True)
474 476 self.assertFalse(im.visitdir('dir/foo'))
475 477 self.assertFalse(im.visitdir('folder'))
476 478 self.assertFalse(im.visitdir('dir/subdir/z'))
477 479 # OPT: this should probably be 'all' not True.
478 480 self.assertEqual(im.visitdir('dir/subdir/x'), True)
479 481
480 482 def testVisitchildrensetIncludeInclude3(self):
481 483 m1 = matchmod.match('', '', include=['path:dir/subdir/x'])
482 484 m2 = matchmod.match('', '', include=['path:dir/subdir'])
483 485 im = matchmod.intersectmatchers(m1, m2)
484 486 self.assertEqual(im.visitchildrenset('.'), {'dir'})
485 487 self.assertEqual(im.visitchildrenset('dir'), {'subdir'})
486 488 self.assertEqual(im.visitchildrenset('dir/subdir'), {'x'})
487 489 self.assertEqual(im.visitchildrenset('dir/foo'), set())
488 490 self.assertEqual(im.visitchildrenset('folder'), set())
489 491 self.assertEqual(im.visitchildrenset('dir/subdir/z'), set())
490 492 # OPT: this should probably be 'all' not 'this'.
491 493 self.assertEqual(im.visitchildrenset('dir/subdir/x'), 'this')
492 494
493 495 # We're using includematcher instead of patterns because it behaves slightly
494 496 # better (giving narrower results) than patternmatcher.
495 497 def testVisitdirIncludeInclude4(self):
496 498 m1 = matchmod.match('', '', include=['path:dir/subdir/x'])
497 499 m2 = matchmod.match('', '', include=['path:dir/subdir/z'])
498 500 im = matchmod.intersectmatchers(m1, m2)
499 501 # OPT: these next three could probably be False as well.
500 502 self.assertEqual(im.visitdir('.'), True)
501 503 self.assertEqual(im.visitdir('dir'), True)
502 504 self.assertEqual(im.visitdir('dir/subdir'), True)
503 505 self.assertFalse(im.visitdir('dir/foo'))
504 506 self.assertFalse(im.visitdir('folder'))
505 507 self.assertFalse(im.visitdir('dir/subdir/z'))
506 508 self.assertFalse(im.visitdir('dir/subdir/x'))
507 509
508 510 def testVisitchildrensetIncludeInclude4(self):
509 511 m1 = matchmod.match('', '', include=['path:dir/subdir/x'])
510 512 m2 = matchmod.match('', '', include=['path:dir/subdir/z'])
511 513 im = matchmod.intersectmatchers(m1, m2)
512 514 # OPT: these next two could probably be set() as well.
513 515 self.assertEqual(im.visitchildrenset('.'), {'dir'})
514 516 self.assertEqual(im.visitchildrenset('dir'), {'subdir'})
515 517 self.assertEqual(im.visitchildrenset('dir/subdir'), set())
516 518 self.assertEqual(im.visitchildrenset('dir/foo'), set())
517 519 self.assertEqual(im.visitchildrenset('folder'), set())
518 520 self.assertEqual(im.visitchildrenset('dir/subdir/z'), set())
519 521 self.assertEqual(im.visitchildrenset('dir/subdir/x'), set())
520 522
521 523 class UnionMatcherTests(unittest.TestCase):
522 524
523 525 def testVisitdirM2always(self):
524 526 m1 = matchmod.alwaysmatcher('', '')
525 527 m2 = matchmod.alwaysmatcher('', '')
526 528 um = matchmod.unionmatcher([m1, m2])
527 529 # um should be equivalent to a alwaysmatcher.
528 530 self.assertEqual(um.visitdir('.'), 'all')
529 531 self.assertEqual(um.visitdir('dir'), 'all')
530 532 self.assertEqual(um.visitdir('dir/subdir'), 'all')
531 533 self.assertEqual(um.visitdir('dir/subdir/z'), 'all')
532 534 self.assertEqual(um.visitdir('dir/foo'), 'all')
533 535 self.assertEqual(um.visitdir('dir/subdir/x'), 'all')
534 536 self.assertEqual(um.visitdir('folder'), 'all')
535 537
536 538 def testVisitchildrensetM2always(self):
537 539 m1 = matchmod.alwaysmatcher('', '')
538 540 m2 = matchmod.alwaysmatcher('', '')
539 541 um = matchmod.unionmatcher([m1, m2])
540 542 # um should be equivalent to a alwaysmatcher.
541 543 self.assertEqual(um.visitchildrenset('.'), 'all')
542 544 self.assertEqual(um.visitchildrenset('dir'), 'all')
543 545 self.assertEqual(um.visitchildrenset('dir/subdir'), 'all')
544 546 self.assertEqual(um.visitchildrenset('dir/subdir/z'), 'all')
545 547 self.assertEqual(um.visitchildrenset('dir/foo'), 'all')
546 548 self.assertEqual(um.visitchildrenset('dir/subdir/x'), 'all')
547 549 self.assertEqual(um.visitchildrenset('folder'), 'all')
548 550
549 551 def testVisitdirM1never(self):
550 552 m1 = matchmod.nevermatcher('', '')
551 553 m2 = matchmod.alwaysmatcher('', '')
552 554 um = matchmod.unionmatcher([m1, m2])
553 555 # um should be equivalent to a alwaysmatcher.
554 556 self.assertEqual(um.visitdir('.'), 'all')
555 557 self.assertEqual(um.visitdir('dir'), 'all')
556 558 self.assertEqual(um.visitdir('dir/subdir'), 'all')
557 559 self.assertEqual(um.visitdir('dir/subdir/z'), 'all')
558 560 self.assertEqual(um.visitdir('dir/foo'), 'all')
559 561 self.assertEqual(um.visitdir('dir/subdir/x'), 'all')
560 562 self.assertEqual(um.visitdir('folder'), 'all')
561 563
562 564 def testVisitchildrensetM1never(self):
563 565 m1 = matchmod.nevermatcher('', '')
564 566 m2 = matchmod.alwaysmatcher('', '')
565 567 um = matchmod.unionmatcher([m1, m2])
566 568 # um should be equivalent to a alwaysmatcher.
567 569 self.assertEqual(um.visitchildrenset('.'), 'all')
568 570 self.assertEqual(um.visitchildrenset('dir'), 'all')
569 571 self.assertEqual(um.visitchildrenset('dir/subdir'), 'all')
570 572 self.assertEqual(um.visitchildrenset('dir/subdir/z'), 'all')
571 573 self.assertEqual(um.visitchildrenset('dir/foo'), 'all')
572 574 self.assertEqual(um.visitchildrenset('dir/subdir/x'), 'all')
573 575 self.assertEqual(um.visitchildrenset('folder'), 'all')
574 576
575 577 def testVisitdirM2never(self):
576 578 m1 = matchmod.alwaysmatcher('', '')
577 579 m2 = matchmod.nevermatcher('', '')
578 580 um = matchmod.unionmatcher([m1, m2])
579 581 # um should be equivalent to a alwaysmatcher.
580 582 self.assertEqual(um.visitdir('.'), 'all')
581 583 self.assertEqual(um.visitdir('dir'), 'all')
582 584 self.assertEqual(um.visitdir('dir/subdir'), 'all')
583 585 self.assertEqual(um.visitdir('dir/subdir/z'), 'all')
584 586 self.assertEqual(um.visitdir('dir/foo'), 'all')
585 587 self.assertEqual(um.visitdir('dir/subdir/x'), 'all')
586 588 self.assertEqual(um.visitdir('folder'), 'all')
587 589
588 590 def testVisitchildrensetM2never(self):
589 591 m1 = matchmod.alwaysmatcher('', '')
590 592 m2 = matchmod.nevermatcher('', '')
591 593 um = matchmod.unionmatcher([m1, m2])
592 594 # um should be equivalent to a alwaysmatcher.
593 595 self.assertEqual(um.visitchildrenset('.'), 'all')
594 596 self.assertEqual(um.visitchildrenset('dir'), 'all')
595 597 self.assertEqual(um.visitchildrenset('dir/subdir'), 'all')
596 598 self.assertEqual(um.visitchildrenset('dir/subdir/z'), 'all')
597 599 self.assertEqual(um.visitchildrenset('dir/foo'), 'all')
598 600 self.assertEqual(um.visitchildrenset('dir/subdir/x'), 'all')
599 601 self.assertEqual(um.visitchildrenset('folder'), 'all')
600 602
601 603 def testVisitdirM2SubdirPrefix(self):
602 604 m1 = matchmod.alwaysmatcher('', '')
603 605 m2 = matchmod.match('', '', patterns=['path:dir/subdir'])
604 606 um = matchmod.unionmatcher([m1, m2])
605 607 self.assertEqual(um.visitdir('.'), 'all')
606 608 self.assertEqual(um.visitdir('dir'), 'all')
607 609 self.assertEqual(um.visitdir('dir/subdir'), 'all')
608 610 self.assertEqual(um.visitdir('dir/foo'), 'all')
609 611 self.assertEqual(um.visitdir('folder'), 'all')
610 612 self.assertEqual(um.visitdir('dir/subdir/z'), 'all')
611 613 self.assertEqual(um.visitdir('dir/subdir/x'), 'all')
612 614
613 615 def testVisitchildrensetM2SubdirPrefix(self):
614 616 m1 = matchmod.alwaysmatcher('', '')
615 617 m2 = matchmod.match('', '', include=['path:dir/subdir'])
616 618 um = matchmod.unionmatcher([m1, m2])
617 619 self.assertEqual(um.visitchildrenset('.'), 'all')
618 620 self.assertEqual(um.visitchildrenset('dir'), 'all')
619 621 self.assertEqual(um.visitchildrenset('dir/subdir'), 'all')
620 622 self.assertEqual(um.visitchildrenset('dir/foo'), 'all')
621 623 self.assertEqual(um.visitchildrenset('folder'), 'all')
622 624 self.assertEqual(um.visitchildrenset('dir/subdir/z'), 'all')
623 625 self.assertEqual(um.visitchildrenset('dir/subdir/x'), 'all')
624 626
625 627 # We're using includematcher instead of patterns because it behaves slightly
626 628 # better (giving narrower results) than patternmatcher.
627 629 def testVisitdirIncludeIncludfe(self):
628 630 m1 = matchmod.match('', '', include=['path:dir/subdir'])
629 631 m2 = matchmod.match('', '', include=['rootfilesin:dir'])
630 632 um = matchmod.unionmatcher([m1, m2])
631 633 self.assertEqual(um.visitdir('.'), True)
632 634 self.assertEqual(um.visitdir('dir'), True)
633 635 self.assertEqual(um.visitdir('dir/subdir'), 'all')
634 636 self.assertFalse(um.visitdir('dir/foo'))
635 637 self.assertFalse(um.visitdir('folder'))
636 638 # OPT: These two should probably be 'all' not True.
637 639 self.assertEqual(um.visitdir('dir/subdir/z'), True)
638 640 self.assertEqual(um.visitdir('dir/subdir/x'), True)
639 641
640 642 def testVisitchildrensetIncludeInclude(self):
641 643 m1 = matchmod.match('', '', include=['path:dir/subdir'])
642 644 m2 = matchmod.match('', '', include=['rootfilesin:dir'])
643 645 um = matchmod.unionmatcher([m1, m2])
644 646 self.assertEqual(um.visitchildrenset('.'), {'dir'})
645 647 self.assertEqual(um.visitchildrenset('dir'), 'this')
646 648 self.assertEqual(um.visitchildrenset('dir/subdir'), 'all')
647 649 self.assertEqual(um.visitchildrenset('dir/foo'), set())
648 650 self.assertEqual(um.visitchildrenset('folder'), set())
649 651 # OPT: These next two could be 'all' instead of 'this'.
650 652 self.assertEqual(um.visitchildrenset('dir/subdir/z'), 'this')
651 653 self.assertEqual(um.visitchildrenset('dir/subdir/x'), 'this')
652 654
653 655 # We're using includematcher instead of patterns because it behaves slightly
654 656 # better (giving narrower results) than patternmatcher.
655 657 def testVisitdirIncludeInclude2(self):
656 658 m1 = matchmod.match('', '', include=['path:dir/subdir'])
657 659 m2 = matchmod.match('', '', include=['path:folder'])
658 660 um = matchmod.unionmatcher([m1, m2])
659 661 self.assertEqual(um.visitdir('.'), True)
660 662 self.assertEqual(um.visitdir('dir'), True)
661 663 self.assertEqual(um.visitdir('dir/subdir'), 'all')
662 664 self.assertFalse(um.visitdir('dir/foo'))
663 665 self.assertEqual(um.visitdir('folder'), 'all')
664 666 # OPT: These should probably be 'all' not True.
665 667 self.assertEqual(um.visitdir('dir/subdir/z'), True)
666 668 self.assertEqual(um.visitdir('dir/subdir/x'), True)
667 669
668 670 def testVisitchildrensetIncludeInclude2(self):
669 671 m1 = matchmod.match('', '', include=['path:dir/subdir'])
670 672 m2 = matchmod.match('', '', include=['path:folder'])
671 673 um = matchmod.unionmatcher([m1, m2])
672 674 self.assertEqual(um.visitchildrenset('.'), {'folder', 'dir'})
673 675 self.assertEqual(um.visitchildrenset('dir'), {'subdir'})
674 676 self.assertEqual(um.visitchildrenset('dir/subdir'), 'all')
675 677 self.assertEqual(um.visitchildrenset('dir/foo'), set())
676 678 self.assertEqual(um.visitchildrenset('folder'), 'all')
677 679 # OPT: These next two could be 'all' instead of 'this'.
678 680 self.assertEqual(um.visitchildrenset('dir/subdir/z'), 'this')
679 681 self.assertEqual(um.visitchildrenset('dir/subdir/x'), 'this')
680 682
681 683 # We're using includematcher instead of patterns because it behaves slightly
682 684 # better (giving narrower results) than patternmatcher.
683 685 def testVisitdirIncludeInclude3(self):
684 686 m1 = matchmod.match('', '', include=['path:dir/subdir/x'])
685 687 m2 = matchmod.match('', '', include=['path:dir/subdir'])
686 688 um = matchmod.unionmatcher([m1, m2])
687 689 self.assertEqual(um.visitdir('.'), True)
688 690 self.assertEqual(um.visitdir('dir'), True)
689 691 self.assertEqual(um.visitdir('dir/subdir'), 'all')
690 692 self.assertFalse(um.visitdir('dir/foo'))
691 693 self.assertFalse(um.visitdir('folder'))
692 694 self.assertEqual(um.visitdir('dir/subdir/x'), 'all')
693 695 # OPT: this should probably be 'all' not True.
694 696 self.assertEqual(um.visitdir('dir/subdir/z'), True)
695 697
696 698 def testVisitchildrensetIncludeInclude3(self):
697 699 m1 = matchmod.match('', '', include=['path:dir/subdir/x'])
698 700 m2 = matchmod.match('', '', include=['path:dir/subdir'])
699 701 um = matchmod.unionmatcher([m1, m2])
700 702 self.assertEqual(um.visitchildrenset('.'), {'dir'})
701 703 self.assertEqual(um.visitchildrenset('dir'), {'subdir'})
702 704 self.assertEqual(um.visitchildrenset('dir/subdir'), 'all')
703 705 self.assertEqual(um.visitchildrenset('dir/foo'), set())
704 706 self.assertEqual(um.visitchildrenset('folder'), set())
705 707 self.assertEqual(um.visitchildrenset('dir/subdir/x'), 'all')
706 708 # OPT: this should probably be 'all' not 'this'.
707 709 self.assertEqual(um.visitchildrenset('dir/subdir/z'), 'this')
708 710
709 711 # We're using includematcher instead of patterns because it behaves slightly
710 712 # better (giving narrower results) than patternmatcher.
711 713 def testVisitdirIncludeInclude4(self):
712 714 m1 = matchmod.match('', '', include=['path:dir/subdir/x'])
713 715 m2 = matchmod.match('', '', include=['path:dir/subdir/z'])
714 716 um = matchmod.unionmatcher([m1, m2])
715 717 # OPT: these next three could probably be False as well.
716 718 self.assertEqual(um.visitdir('.'), True)
717 719 self.assertEqual(um.visitdir('dir'), True)
718 720 self.assertEqual(um.visitdir('dir/subdir'), True)
719 721 self.assertFalse(um.visitdir('dir/foo'))
720 722 self.assertFalse(um.visitdir('folder'))
721 723 self.assertEqual(um.visitdir('dir/subdir/z'), 'all')
722 724 self.assertEqual(um.visitdir('dir/subdir/x'), 'all')
723 725
724 726 def testVisitchildrensetIncludeInclude4(self):
725 727 m1 = matchmod.match('', '', include=['path:dir/subdir/x'])
726 728 m2 = matchmod.match('', '', include=['path:dir/subdir/z'])
727 729 um = matchmod.unionmatcher([m1, m2])
728 730 self.assertEqual(um.visitchildrenset('.'), {'dir'})
729 731 self.assertEqual(um.visitchildrenset('dir'), {'subdir'})
730 732 self.assertEqual(um.visitchildrenset('dir/subdir'), {'x', 'z'})
731 733 self.assertEqual(um.visitchildrenset('dir/foo'), set())
732 734 self.assertEqual(um.visitchildrenset('folder'), set())
733 735 self.assertEqual(um.visitchildrenset('dir/subdir/z'), 'all')
734 736 self.assertEqual(um.visitchildrenset('dir/subdir/x'), 'all')
735 737
736 738 class SubdirMatcherTests(unittest.TestCase):
737 739
738 740 def testVisitdir(self):
739 741 m = matchmod.match('', '', include=['path:dir/subdir'])
740 742 sm = matchmod.subdirmatcher('dir', m)
741 743
742 744 self.assertEqual(sm.visitdir('.'), True)
743 745 self.assertEqual(sm.visitdir('subdir'), 'all')
744 746 # OPT: These next two should probably be 'all' not True.
745 747 self.assertEqual(sm.visitdir('subdir/x'), True)
746 748 self.assertEqual(sm.visitdir('subdir/z'), True)
747 749 self.assertFalse(sm.visitdir('foo'))
748 750
749 751 def testVisitchildrenset(self):
750 752 m = matchmod.match('', '', include=['path:dir/subdir'])
751 753 sm = matchmod.subdirmatcher('dir', m)
752 754
753 755 self.assertEqual(sm.visitchildrenset('.'), {'subdir'})
754 756 self.assertEqual(sm.visitchildrenset('subdir'), 'all')
755 757 # OPT: These next two should probably be 'all' not 'this'.
756 758 self.assertEqual(sm.visitchildrenset('subdir/x'), 'this')
757 759 self.assertEqual(sm.visitchildrenset('subdir/z'), 'this')
758 760 self.assertEqual(sm.visitchildrenset('foo'), set())
759 761
760 762 class PrefixdirMatcherTests(unittest.TestCase):
761 763
762 764 def testVisitdir(self):
763 765 m = matchmod.match(util.localpath('root/d'), 'e/f',
764 766 ['../a.txt', 'b.txt'])
765 767 pm = matchmod.prefixdirmatcher('root', 'd/e/f', 'd', m)
766 768
767 769 # `m` elides 'd' because it's part of the root, and the rest of the
768 770 # patterns are relative.
769 771 self.assertEqual(bool(m('a.txt')), False)
770 772 self.assertEqual(bool(m('b.txt')), False)
771 773 self.assertEqual(bool(m('e/a.txt')), True)
772 774 self.assertEqual(bool(m('e/b.txt')), False)
773 775 self.assertEqual(bool(m('e/f/b.txt')), True)
774 776
775 777 # The prefix matcher re-adds 'd' to the paths, so they need to be
776 778 # specified when using the prefixdirmatcher.
777 779 self.assertEqual(bool(pm('a.txt')), False)
778 780 self.assertEqual(bool(pm('b.txt')), False)
779 781 self.assertEqual(bool(pm('d/e/a.txt')), True)
780 782 self.assertEqual(bool(pm('d/e/b.txt')), False)
781 783 self.assertEqual(bool(pm('d/e/f/b.txt')), True)
782 784
783 785 self.assertEqual(m.visitdir('.'), True)
784 786 self.assertEqual(m.visitdir('e'), True)
785 787 self.assertEqual(m.visitdir('e/f'), True)
786 788 self.assertEqual(m.visitdir('e/f/g'), False)
787 789
788 790 self.assertEqual(pm.visitdir('.'), True)
789 791 self.assertEqual(pm.visitdir('d'), True)
790 792 self.assertEqual(pm.visitdir('d/e'), True)
791 793 self.assertEqual(pm.visitdir('d/e/f'), True)
792 794 self.assertEqual(pm.visitdir('d/e/f/g'), False)
793 795
794 796 def testVisitchildrenset(self):
795 797 m = matchmod.match(util.localpath('root/d'), 'e/f',
796 798 ['../a.txt', 'b.txt'])
797 799 pm = matchmod.prefixdirmatcher('root', 'd/e/f', 'd', m)
798 800
799 801 # OPT: visitchildrenset could possibly return {'e'} and {'f'} for these
800 802 # next two, respectively; patternmatcher does not have this
801 803 # optimization.
802 804 self.assertEqual(m.visitchildrenset('.'), 'this')
803 805 self.assertEqual(m.visitchildrenset('e'), 'this')
804 806 self.assertEqual(m.visitchildrenset('e/f'), 'this')
805 807 self.assertEqual(m.visitchildrenset('e/f/g'), set())
806 808
807 809 # OPT: visitchildrenset could possibly return {'d'}, {'e'}, and {'f'}
808 810 # for these next three, respectively; patternmatcher does not have this
809 811 # optimization.
810 812 self.assertEqual(pm.visitchildrenset('.'), 'this')
811 813 self.assertEqual(pm.visitchildrenset('d'), 'this')
812 814 self.assertEqual(pm.visitchildrenset('d/e'), 'this')
813 815 self.assertEqual(pm.visitchildrenset('d/e/f'), 'this')
814 816 self.assertEqual(pm.visitchildrenset('d/e/f/g'), set())
815 817
816 818 if __name__ == '__main__':
817 819 silenttestrunner.main(__name__)
General Comments 0
You need to be logged in to leave comments. Login now