comparison mercurial/fileset.py @ 27462:470ea34ba593

fileset: use decorator to mark a predicate as "existing caller" This can localize changes for adding (or removing) an "existing caller" predicate function in source code.
author FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
date Mon, 21 Dec 2015 22:31:16 +0900
parents afa76585c955
children a8afdc5a7885
comparison
equal deleted inserted replaced
27461:afa76585c955 27462:470ea34ba593
138 symbols = {} 138 symbols = {}
139 139
140 # filesets using matchctx.status() 140 # filesets using matchctx.status()
141 _statuscallers = [] 141 _statuscallers = []
142 142
143 def predicate(decl, callstatus=False): 143 # filesets using matchctx.existing()
144 _existingcallers = []
145
146 def predicate(decl, callstatus=False, callexisting=False):
144 """Return a decorator for fileset predicate function 147 """Return a decorator for fileset predicate function
145 148
146 'decl' argument is the declaration (including argument list like 149 'decl' argument is the declaration (including argument list like
147 'adds(pattern)') or the name (for internal use only) of predicate. 150 'adds(pattern)') or the name (for internal use only) of predicate.
148 151
149 Optional 'callstatus' argument indicates whether predicate implies 152 Optional 'callstatus' argument indicates whether predicate implies
150 'matchctx.status()' at runtime or not (False, by default). 153 'matchctx.status()' at runtime or not (False, by default).
154
155 Optional 'callexisting' argument indicates whether predicate
156 implies 'matchctx.existing()' at runtime or not (False, by
157 default).
151 """ 158 """
152 def decorator(func): 159 def decorator(func):
153 i = decl.find('(') 160 i = decl.find('(')
154 if i > 0: 161 if i > 0:
155 name = decl[:i] 162 name = decl[:i]
156 else: 163 else:
157 name = decl 164 name = decl
158 symbols[name] = func 165 symbols[name] = func
159 if callstatus: 166 if callstatus:
160 _statuscallers.append(name) 167 _statuscallers.append(name)
168 if callexisting:
169 _existingcallers.append(name)
161 if func.__doc__: 170 if func.__doc__:
162 func.__doc__ = "``%s``\n %s" % (decl, func.__doc__.strip()) 171 func.__doc__ = "``%s``\n %s" % (decl, func.__doc__.strip())
163 return func 172 return func
164 return decorator 173 return decorator
165 174
257 l = getlist(x) 266 l = getlist(x)
258 if len(l) < min or len(l) > max: 267 if len(l) < min or len(l) > max:
259 raise error.ParseError(err) 268 raise error.ParseError(err)
260 return l 269 return l
261 270
262 @predicate('binary()') 271 @predicate('binary()', callexisting=True)
263 def binary(mctx, x): 272 def binary(mctx, x):
264 """File that appears to be binary (contains NUL bytes). 273 """File that appears to be binary (contains NUL bytes).
265 """ 274 """
266 # i18n: "binary" is a keyword 275 # i18n: "binary" is a keyword
267 getargs(x, 0, 0, _("binary takes no arguments")) 276 getargs(x, 0, 0, _("binary takes no arguments"))
268 return [f for f in mctx.existing() if util.binary(mctx.ctx[f].data())] 277 return [f for f in mctx.existing() if util.binary(mctx.ctx[f].data())]
269 278
270 @predicate('exec()') 279 @predicate('exec()', callexisting=True)
271 def exec_(mctx, x): 280 def exec_(mctx, x):
272 """File that is marked as executable. 281 """File that is marked as executable.
273 """ 282 """
274 # i18n: "exec" is a keyword 283 # i18n: "exec" is a keyword
275 getargs(x, 0, 0, _("exec takes no arguments")) 284 getargs(x, 0, 0, _("exec takes no arguments"))
276 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'x'] 285 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'x']
277 286
278 @predicate('symlink()') 287 @predicate('symlink()', callexisting=True)
279 def symlink(mctx, x): 288 def symlink(mctx, x):
280 """File that is marked as a symlink. 289 """File that is marked as a symlink.
281 """ 290 """
282 # i18n: "symlink" is a keyword 291 # i18n: "symlink" is a keyword
283 getargs(x, 0, 0, _("symlink takes no arguments")) 292 getargs(x, 0, 0, _("symlink takes no arguments"))
322 # i18n: "portable" is a keyword 331 # i18n: "portable" is a keyword
323 getargs(x, 0, 0, _("portable takes no arguments")) 332 getargs(x, 0, 0, _("portable takes no arguments"))
324 checkwinfilename = util.checkwinfilename 333 checkwinfilename = util.checkwinfilename
325 return [f for f in mctx.subset if checkwinfilename(f) is None] 334 return [f for f in mctx.subset if checkwinfilename(f) is None]
326 335
327 @predicate('grep(regex)') 336 @predicate('grep(regex)', callexisting=True)
328 def grep(mctx, x): 337 def grep(mctx, x):
329 """File contains the given regular expression. 338 """File contains the given regular expression.
330 """ 339 """
331 try: 340 try:
332 # i18n: "grep" is a keyword 341 # i18n: "grep" is a keyword
349 # no extension, this is a precise value 358 # no extension, this is a precise value
350 return int(s) 359 return int(s)
351 except ValueError: 360 except ValueError:
352 raise error.ParseError(_("couldn't parse size: %s") % s) 361 raise error.ParseError(_("couldn't parse size: %s") % s)
353 362
354 @predicate('size(expression)') 363 @predicate('size(expression)', callexisting=True)
355 def size(mctx, x): 364 def size(mctx, x):
356 """File size matches the given expression. Examples: 365 """File size matches the given expression. Examples:
357 366
358 - 1k (files from 1024 to 2047 bytes) 367 - 1k (files from 1024 to 2047 bytes)
359 - < 20k (files less than 20480 bytes) 368 - < 20k (files less than 20480 bytes)
387 else: 396 else:
388 raise error.ParseError(_("couldn't parse size: %s") % expr) 397 raise error.ParseError(_("couldn't parse size: %s") % expr)
389 398
390 return [f for f in mctx.existing() if m(mctx.ctx[f].size())] 399 return [f for f in mctx.existing() if m(mctx.ctx[f].size())]
391 400
392 @predicate('encoding(name)') 401 @predicate('encoding(name)', callexisting=True)
393 def encoding(mctx, x): 402 def encoding(mctx, x):
394 """File can be successfully decoded with the given character 403 """File can be successfully decoded with the given character
395 encoding. May not be useful for encodings other than ASCII and 404 encoding. May not be useful for encodings other than ASCII and
396 UTF-8. 405 UTF-8.
397 """ 406 """
410 continue 419 continue
411 s.append(f) 420 s.append(f)
412 421
413 return s 422 return s
414 423
415 @predicate('eol(style)') 424 @predicate('eol(style)', callexisting=True)
416 def eol(mctx, x): 425 def eol(mctx, x):
417 """File contains newlines of the given style (dos, unix, mac). Binary 426 """File contains newlines of the given style (dos, unix, mac). Binary
418 files are excluded, files with mixed line endings match multiple 427 files are excluded, files with mixed line endings match multiple
419 styles. 428 styles.
420 """ 429 """
514 for s in tree[1:]: 523 for s in tree[1:]:
515 if _intree(funcs, s): 524 if _intree(funcs, s):
516 return True 525 return True
517 return False 526 return False
518 527
519 # filesets using matchctx.existing()
520 _existingcallers = [
521 'binary',
522 'encoding',
523 'eol',
524 'exec',
525 'grep',
526 'size',
527 'symlink',
528 ]
529
530 def getfileset(ctx, expr): 528 def getfileset(ctx, expr):
531 tree = parse(expr) 529 tree = parse(expr)
532 530
533 # do we need status info? 531 # do we need status info?
534 if (_intree(_statuscallers, tree) or 532 if (_intree(_statuscallers, tree) or