Blame pagure/perfrepo.py

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