123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- # -*- coding: utf-8 -*-
- """
- (c) 2017 - Copyright Red Hat Inc
- Authors:
- Patrick Uiterwijk <puiterwijk@redhat.com>
- """
- import pprint
- import os
- import traceback
- import types
- import pygit2
- import _pygit2
- real_pygit2_repository = pygit2.Repository
- TOTALS = {'walks': 0,
- 'steps': 0}
- REQUESTS = []
- STATS = {}
- class PerfRepoMeta(type):
- def __new__(cls, name, parents, dct):
- # create a class_id if it's not specified
- if 'class_id' not in dct:
- dct['class_id'] = name.lower()
- # we need to call type.__new__ to complete the initialization
- return super(PerfRepoMeta, cls).__new__(cls, name, parents, dct)
- def __getattr__(cls, attr):
- real = getattr(real_pygit2_repository, attr)
- if type(real).__name__ in ['function', 'builtin_function_or_method']:
- def fake(*args, **kwargs):
- return real(*args, **kwargs)
- return fake
- else:
- return real
- class FakeWalker(object):
- def __init__(self, parent):
- self.parent = parent
- self.wid = STATS['counters']['walks']
- STATS['counters']['walks'] += 1
- STATS['walks'][self.wid] = {
- 'steps': 0,
- 'type': 'walker',
- 'init': traceback.extract_stack(limit=3)[0],
- 'iter': None}
- TOTALS['walks'] += 1
- def __getattr__(self, attr):
- return getattr(self.parent, attr)
- def __iter__(self):
- STATS['walks'][self.wid]['iter'] = traceback.extract_stack(limit=2)[0]
- return self
- def next(self):
- STATS['walks'][self.wid]['steps'] += 1
- TOTALS['steps'] += 1
- resp = self.parent.next()
- return resp
- class FakeDiffHunk(object):
- def __init__(self, parent):
- self.parent = parent
- def __getattr__(self, attr):
- print 'Getting Fake Hunk %s' % attr
- resp = getattr(self.parent, attr)
- print 'Response: %s' % resp
- return resp
- class FakeDiffPatch(object):
- def __init__(self, parent):
- self.parent = parent
- def __getattr__(self, attr):
- if attr == 'hunks':
- return [FakeDiffHunk(h) for h in self.parent.hunks]
- return getattr(self.parent, attr)
- class FakeDiffer(object):
- def __init__(self, parent):
- self.parent = parent
- self.iter = None
- self.did = STATS['counters']['diffs']
- STATS['counters']['diffs'] += 1
- STATS['diffs'][self.did] = {
- 'init': traceback.extract_stack(limit=3)[0],
- 'steps': 0,
- 'iter': None}
- def __getattr__(self, attr):
- return getattr(self.parent, attr)
- def __dir__(self):
- return dir(self.parent)
- def __iter__(self):
- STATS['diffs'][self.did]['iter'] = traceback.extract_stack(limit=2)[0]
- self.iter = self.parent.__iter__()
- return self
- def next(self):
- STATS['diffs'][self.did]['steps'] += 1
- resp = self.iter.next()
- if isinstance(resp, _pygit2.Patch):
- resp = FakeDiffPatch(resp)
- else:
- raise Exception('Unexpected %s returned from differ' % resp)
- return resp
- def __len__(self):
- return len(self.parent)
- class PerfRepo(object):
- """ An utility class allowing to go around pygit2's inability to be
- stable.
- """
- __metaclass__ = PerfRepoMeta
- def __init__(self, path):
- STATS['repo_inits'].append((path, traceback.extract_stack(limit=2)[0]))
- STATS['counters']['inits'] += 1
- self.repo = real_pygit2_repository(path)
- self.iter = None
- def __getattr__(self, attr):
- real = getattr(self.repo, attr)
- if type(real) in [types.FunctionType,
- types.BuiltinFunctionType,
- types.BuiltinMethodType]:
- def fake(*args, **kwargs):
- resp = real(*args, **kwargs)
- if isinstance(resp, _pygit2.Walker):
- resp = FakeWalker(resp)
- elif isinstance(resp, _pygit2.Diff):
- resp = FakeDiffer(resp)
- return resp
- return fake
- elif isinstance(real, dict):
- real_getitem = real.__getitem__
- def fake_getitem(self, item):
- return real_getitem(item)
- real.__getitem__ = fake_getitem
- return real
- else:
- return real
- def __getitem__(self, item):
- return self.repo.__getitem__(item)
- def __contains__(self, item):
- return self.repo.__contains__(item)
- def __iter__(self):
- self.wid = STATS['counters']['walks']
- STATS['counters']['walks'] += 1
- STATS['walks'][self.wid] = {
- 'steps': 0,
- 'type': 'iter',
- 'iter': traceback.extract_stack(limit=3)[0]}
- TOTALS['walks'] += 1
- self.iter = self.repo.__iter__()
- return self
- def next(self):
- STATS['walks'][self.wid]['steps'] += 1
- TOTALS['steps'] += 1
- return self.iter.next()
- pygit2.Repository = PerfRepo
- def reset_stats():
- """Resets STATS to be clear for the next request."""
- global STATS
- STATS = {'walks': {},
- 'diffs': {},
- 'repo_inits': [],
- 'counters': {'walks': 0,
- 'diffs': 0,
- 'inits': 0}}
- # Make sure we start blank
- reset_stats()
- def print_stats(response):
- """Finalizes stats for the current request, and prints them possibly."""
- REQUESTS.append(STATS)
- if not os.environ.get('PAGURE_PERFREPO_VERBOSE'):
- return response
- print 'Statistics:'
- pprint.pprint(STATS)
- return response
|