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()