comparison contrib/perf.py @ 30143:2d858c771760

perf: introduce safeattrsetter to replace direct attribute assignment Referring not-existing attribute immediately causes failure, but assigning a value to such attribute doesn't. For example, perf.py has code paths below, which assign a value to not-existing attribute. This causes incorrect performance measurement, but these code paths are executed successfully. - "repo._tags = None" in perftags() recent Mercurial has tags cache information in repo._tagscache - "branchmap.write = lambda repo: None" in perfbranchmap() branchmap cache is written out by branchcache.write() in branchmap.py "util.safehasattr() before assignment" can avoid this issue, but might increase mistake at "copy & paste" attribute name or so. To centralize (1) examining existence of, (2) assigning a value to, and (3) restoring an old value to the attribute, this patch introduces safeattrsetter(). This is used to replace direct attribute assignment in subsequent patches. Encapsulation of restoring is needed to completely remove direct attribute assignment from perf.py, even though restoring isn't needed so often.
author FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
date Sun, 09 Oct 2016 01:03:16 +0900
parents 98b9846a131e
children 14031d183048
comparison
equal deleted inserted replaced
30142:3dcaf1c4e90d 30143:2d858c771760
180 fm.write('user', ' user %f', m[1]) 180 fm.write('user', ' user %f', m[1])
181 fm.write('sys', ' sys %f', m[2]) 181 fm.write('sys', ' sys %f', m[2])
182 fm.write('count', ' (best of %d)', count) 182 fm.write('count', ' (best of %d)', count)
183 fm.plain('\n') 183 fm.plain('\n')
184 184
185 # utilities for historical portability
186
187 def safeattrsetter(obj, name, ignoremissing=False):
188 """Ensure that 'obj' has 'name' attribute before subsequent setattr
189
190 This function is aborted, if 'obj' doesn't have 'name' attribute
191 at runtime. This avoids overlooking removal of an attribute, which
192 breaks assumption of performance measurement, in the future.
193
194 This function returns the object to (1) assign a new value, and
195 (2) restore an original value to the attribute.
196
197 If 'ignoremissing' is true, missing 'name' attribute doesn't cause
198 abortion, and this function returns None. This is useful to
199 examine an attribute, which isn't ensured in all Mercurial
200 versions.
201 """
202 if not util.safehasattr(obj, name):
203 if ignoremissing:
204 return None
205 raise error.Abort(("missing attribute %s of %s might break assumption"
206 " of performance measurement") % (name, obj))
207
208 origvalue = getattr(obj, name)
209 class attrutil(object):
210 def set(self, newvalue):
211 setattr(obj, name, newvalue)
212 def restore(self):
213 setattr(obj, name, origvalue)
214
215 return attrutil()
216
217 # perf commands
218
185 @command('perfwalk', formatteropts) 219 @command('perfwalk', formatteropts)
186 def perfwalk(ui, repo, *pats, **opts): 220 def perfwalk(ui, repo, *pats, **opts):
187 timer, fm = gettimer(ui, opts) 221 timer, fm = gettimer(ui, opts)
188 try: 222 try:
189 m = scmutil.match(repo[None], pats, {}) 223 m = scmutil.match(repo[None], pats, {})