Mercurial > hg
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, {}) |