Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/fileset.py @ 27464:c39ecb2b86b3
fileset: detect unintentional existing() invocation at runtime
A fileset predicate can invoke 'matchctx.existing()' successfully,
even if it isn't marked as "existing caller". It is aborted only in
some corner cases: e.g. there were one deleted file in the working
directory (see 8a0513bf030a for detail).
This patch makes 'matchctx.existing()' invocation abort if not
'_existingenabled', which is true only while "existing caller"
running.
After this changes, non-"existing caller" predicate function is
aborted immediately, whenever it invokes 'matchctx.existing()'. This
prevent developer from forgetting to mark a predicate as "existing
caller".
BTW, unintentional 'matchctx.status()' invocation can be detected
easily without any additional trick like this patch, because it
returns 'None' if a predicate isn't marked as "status caller", and
referring field (e.g. '.modified') of it is always aborted.
author | FUJIWARA Katsunori <foozy@lares.dti.ne.jp> |
---|---|
date | Mon, 21 Dec 2015 22:31:16 +0900 |
parents | a8afdc5a7885 |
children | 737ffdabbde9 |
comparison
equal
deleted
inserted
replaced
27463:a8afdc5a7885 | 27464:c39ecb2b86b3 |
---|---|
246 s = mctx.status().clean | 246 s = mctx.status().clean |
247 return [f for f in mctx.subset if f in s] | 247 return [f for f in mctx.subset if f in s] |
248 | 248 |
249 def func(mctx, a, b): | 249 def func(mctx, a, b): |
250 if a[0] == 'symbol' and a[1] in symbols: | 250 if a[0] == 'symbol' and a[1] in symbols: |
251 return symbols[a[1]](mctx, b) | 251 funcname = a[1] |
252 enabled = mctx._existingenabled | |
253 mctx._existingenabled = funcname in _existingcallers | |
254 try: | |
255 return symbols[funcname](mctx, b) | |
256 finally: | |
257 mctx._existingenabled = enabled | |
252 | 258 |
253 keep = lambda fn: getattr(fn, '__doc__', None) is not None | 259 keep = lambda fn: getattr(fn, '__doc__', None) is not None |
254 | 260 |
255 syms = [s for (s, fn) in symbols.items() if keep(fn)] | 261 syms = [s for (s, fn) in symbols.items() if keep(fn)] |
256 raise error.UnknownIdentifier(a[1], syms) | 262 raise error.UnknownIdentifier(a[1], syms) |
495 class matchctx(object): | 501 class matchctx(object): |
496 def __init__(self, ctx, subset=None, status=None): | 502 def __init__(self, ctx, subset=None, status=None): |
497 self.ctx = ctx | 503 self.ctx = ctx |
498 self.subset = subset | 504 self.subset = subset |
499 self._status = status | 505 self._status = status |
506 self._existingenabled = False | |
500 def status(self): | 507 def status(self): |
501 return self._status | 508 return self._status |
502 def matcher(self, patterns): | 509 def matcher(self, patterns): |
503 return self.ctx.match(patterns) | 510 return self.ctx.match(patterns) |
504 def filter(self, files): | 511 def filter(self, files): |
505 return [f for f in files if f in self.subset] | 512 return [f for f in files if f in self.subset] |
506 def existing(self): | 513 def existing(self): |
514 assert self._existingenabled, 'unexpected existing() invocation' | |
507 if self._status is not None: | 515 if self._status is not None: |
508 removed = set(self._status[3]) | 516 removed = set(self._status[3]) |
509 unknown = set(self._status[4] + self._status[5]) | 517 unknown = set(self._status[4] + self._status[5]) |
510 else: | 518 else: |
511 removed = set() | 519 removed = set() |