Create a logging mixin class that auto dedents log messages

Construct a metaclass that can be used to create subclasses of the
logging.Logger class which wraps all methods for logging output with a
function that initially dedents the message.

Change-Id: Ice5dd54dbdeabb086115953d30369dd933c7a374
This commit is contained in:
Darragh Bailey
2013-07-24 06:49:52 +01:00
parent ba75656a1c
commit 57bf4a6f5e
2 changed files with 89 additions and 27 deletions

View File

@@ -15,13 +15,13 @@
# limitations under the License.
from ghp.errors import HpgitError
from ghp.log import LogDedentMixin
from ghp import subcommand, log
from git import Repo, GitCommandError
import inspect
import os
import textwrap
class ImportUpstreamError(HpgitError):
@@ -29,7 +29,7 @@ class ImportUpstreamError(HpgitError):
pass
class ImportUpstream(object):
class ImportUpstream(LogDedentMixin):
"""
Import code from an upstream project and merge in additional branches
to create a new branch unto which changes that are not upstream but are
@@ -46,6 +46,10 @@ class ImportUpstream(object):
self._repo = Repo(os.environ.get('GIT_WORK_TREE', os.path.curdir))
self._git = self.repo.git
# make sure to correctly initialise inherited objects before performing
# any computation
super(ImportUpstream, self).__init__()
if self.repo.bare:
raise ImportUpstreamError("Cannot perform imports in bare repos")
@@ -142,10 +146,10 @@ class ImportUpstream(object):
"from '%s' (%s)", self.import_branch, self.upstream, commit)
self.log.info(
textwrap.dedent("""\
Checking if import branch '%s' already exists:
git branch --list %s
"""), self.import_branch, self.import_branch)
"""\
Checking if import branch '%s' already exists:
git branch --list %s
""", self.import_branch, self.import_branch)
if self.git.show_ref("refs/heads/" + self.import_branch, verify=True,
with_exceptions=False) and not force:
msg = "Import branch '%s' already exists, use force to replace"
@@ -154,11 +158,10 @@ class ImportUpstream(object):
if self.repo.active_branch == self.import_branch:
self.log.info(
textwrap.dedent(
"""\
Resetting import branch '%s' to specified commit '%s'
git reset --hard %s
"""), self.import_branch, commit, commit)
"""\
Resetting import branch '%s' to specified commit '%s'
git reset --hard %s
""", self.import_branch, commit, commit)
self.git.reset(commit, hard=True)
elif checkout:
checkout_args = dict(b=True)
@@ -166,30 +169,27 @@ class ImportUpstream(object):
checkout_args = dict(B=True)
self.log.info(
textwrap.dedent(
"""\
Checking out import branch '%s' using specified commit '%s'
git checkout %s %s %s
"""), self.import_branch, commit, checkout_args,
"""\
Checking out import branch '%s' using specified commit '%s'
git checkout %s %s %s
""", self.import_branch, commit, checkout_args,
self.import_branch, commit)
self.git.checkout(self.import_branch, commit, **checkout_args)
else:
self.log.info(
textwrap.dedent(
"""\
Creating import branch '%s' from specified commit '%s'
git branch --force %s %s
"""), self.import_branch, commit, self.import_branch, commit)
"""\
Creating import branch '%s' from specified commit '%s'
git branch --force %s %s
""", self.import_branch, commit, self.import_branch, commit)
self.git.branch(self.import_branch, commit, force=force)
if self.extra_branches:
self.log.info(
textwrap.dedent(
"""\
Merging additional branch(es) '%s' into import branch '%s'
git checkout %s
git merge %s
"""), ", ".join(self.extra_branches), self.import_branch,
"""\
Merging additional branch(es) '%s' into import branch '%s'
git checkout %s
git merge %s
""", ", ".join(self.extra_branches), self.import_branch,
self.import_branch, " ".join(self.extra_branches))
self.git.checkout(self.import_branch)
self.git.merge(*self.extra_branches)

View File

@@ -24,6 +24,8 @@ for logging output to the console.
"""
import logging
from functools import wraps
import textwrap
# Add new NOTICE logging level
@@ -87,3 +89,63 @@ class LevelFilterIgnoreBelow(logging.Filter):
def filter(self, record):
return record.levelno >= self.level
class DedentLoggerMeta(type):
"""
Meta class to wrap all level functions in logging interface with dedent
Classes created from this should be derived from the logging.Logger class
as otherwise they will not contain the correct methods to be wrapped and
trying to pass them as the default class to create Loggers from will fail.
"""
def __new__(cls, name, bases, dict):
# provide a more intelligent error instead of waiting for setattr/getattr
# adding of a wrapper function to fail
if logging.Logger not in bases:
raise TypeError("%s not derived from logging.Logger" % name)
obj = super(DedentLoggerMeta, cls).__new__(cls, name, bases, dict)
for level in _levels:
setattr(obj, level, cls.wrap_level(getattr(obj, level)))
setattr(obj, 'log', cls.wrap(getattr(obj, 'log')))
return obj
@staticmethod
def wrap(func):
def _dedent_log(self, level, msg, *args, **kwargs):
dedent = kwargs.pop('dedent', True)
if dedent:
msg = textwrap.dedent(msg)
func(self, level, msg, *args, **kwargs)
return wraps(func)(_dedent_log)
@staticmethod
def wrap_level(func):
def _dedent_log(self, msg, *args, **kwargs):
dedent = kwargs.pop('dedent', True)
if dedent:
msg = textwrap.dedent(msg)
func(self, msg, *args, **kwargs)
return wraps(func)(_dedent_log)
class DedentLogger(logging.Logger):
__metaclass__ = DedentLoggerMeta
# override default logger class for everything that imports this module
logging.setLoggerClass(DedentLogger)
class LogDedentMixin(object):
def __init__(self, *args, **kwargs):
self.__log = getLogger('%s.%s' % (__name__, self.__class__.__name__))
super(LogDedentMixin, self).__init__(*args, **kwargs)
@property
def log(self):
return self.__log