Mercurial > hg-stable
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 |