datetime compatibility option for callbacks

This commit is contained in:
Mike McLean 2017-08-02 16:45:45 -04:00
parent 658953af54
commit 842754c2d3
2 changed files with 65 additions and 1 deletions

View file

@ -25,6 +25,7 @@ import logging
import sys
import traceback
import six
from koji.util import encode_datetime_recurse
# the available callback hooks and a list
# of functions to be called for each event
@ -165,6 +166,13 @@ def ignore_error(f):
setattr(f, 'failure_is_an_option', True)
return f
def convert_datetime(f):
"""Indicate that callback needs to receive datetime objects as strings"""
setattr(f, 'convert_datetime', True)
return f
def register_callback(cbtype, func):
if not cbtype in callbacks:
raise koji.PluginError('"%s" is not a valid callback type' % cbtype)
@ -172,12 +180,15 @@ def register_callback(cbtype, func):
raise koji.PluginError('%s is not callable' % getattr(func, '__name__', 'function'))
callbacks[cbtype].append(func)
def run_callbacks(cbtype, *args, **kws):
if not cbtype in callbacks:
raise koji.PluginError('"%s" is not a valid callback type' % cbtype)
cache = {}
for func in callbacks[cbtype]:
cb_args, cb_kwargs = _fix_cb_args(func, args, kws, cache)
try:
func(cbtype, *args, **kws)
func(cbtype, *cb_args, **cb_kwargs)
except:
msg = 'Error running %s callback from %s' % (cbtype, func.__module__)
if getattr(func, 'failure_is_an_option', False):
@ -185,3 +196,10 @@ def run_callbacks(cbtype, *args, **kws):
else:
tb = ''.join(traceback.format_exception(*sys.exc_info()))
raise koji.CallbackError('%s:\n%s' % (msg, tb))
def _fix_cb_args(func, args, kwargs, cache):
if getattr(func, 'convert_datetime', False):
args = encode_datetime_recurse(args)
kwargs = encode_datetime_recurse(kwargs)
return args, kwargs

View file

@ -20,6 +20,7 @@
from __future__ import absolute_import
import calendar
import datetime
from fnmatch import fnmatch
import koji
import logging
@ -151,6 +152,51 @@ def dslice_ex(dict, keys, strict=True):
del ret[key]
return ret
class DataWalker(object):
def __init__(self, data, callback, kwargs=None):
self.data = data
self.callback = callback
if kwargs is None:
kwargs = {}
self.kwargs = kwargs
def walk(self):
return self._walk(self.data)
def _walk(self, value):
# first let callback filter the value
value = self.callback(value, **self.kwargs)
# then recurse if needed
if isinstance(value, tuple):
return tuple([self._walk(x) for x in value])
elif isinstance(value, list):
return list([self._walk(x) for x in value])
elif isinstance(value, dict):
ret = {}
for k in value:
k = self._walk(k)
v = self._walk(value[k])
ret[k] = v
return ret
else:
return value
def encode_datetime(value):
"""Convert datetime objects to strings"""
if isinstance(value, datetime.datetime):
return value.isoformat(' ')
else:
return value
def encode_datetime_recurse(value):
walker = DataWalker(value, encode_datetime)
return walker.walk()
def call_with_argcheck(func, args, kwargs=None):
"""Call function, raising ParameterError if args do not match"""
if kwargs is None: