linkchecker/linkcheck/decorators.py
2009-01-08 14:18:03 +00:00

171 lines
5.3 KiB
Python

# -*- coding: iso-8859-1 -*-
# Copyright (C) 2005-2009 Bastian Kleineidam
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
"""
Simple decorators (usable in Python >= 2.4).
Example:
@synchronized(thread.allocate_lock())
def f ():
"Synchronized function"
print "i am synchronized:", f, f.__doc__
@deprecated
def g ():
"this function is deprecated"
pass
@notimplemented
def h ():
"todo"
pass
"""
from __future__ import with_statement
import warnings
import signal
import os
import sys
import time
def update_func_meta (fake_func, real_func):
"""Set meta information (eg. __doc__) of fake function to that
of the real function.
@return fake_func
"""
fake_func.__module__ = real_func.__module__
fake_func.__name__ = real_func.__name__
fake_func.__doc__ = real_func.__doc__
fake_func.__dict__.update(real_func.__dict__)
return fake_func
def deprecated (func):
"""A decorator which can be used to mark functions as deprecated.
It emits a warning when the function is called."""
def newfunc (*args, **kwargs):
"""Print deprecated warning and execute original function."""
warnings.warn("Call to deprecated function %s." % func.__name__,
category=DeprecationWarning)
return func(*args, **kwargs)
return update_func_meta(newfunc, func)
def signal_handler (signal_number):
"""From http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/410666
A decorator to set the specified function as handler for a signal.
This function is the 'outer' decorator, called with only the
(non-function) arguments.
If signal_number is not a valid signal (for example signal.SIGN),
no handler is set.
"""
# create the 'real' decorator which takes only a function as an argument
def newfunc (function):
"""Register function as signal handler."""
# note: actually the kill(2) function uses the signal number of 0
# for a special case, but for signal(2) only positive integers
# are allowed
is_valid_signal = 0 < signal_number < signal.NSIG
if is_valid_signal and os.name == 'posix':
signal.signal(signal_number, function)
return function
return newfunc
def synchronize (lock, func):
"""Return synchronized function acquiring the given lock."""
def newfunc (*args, **kwargs):
"""Execute function synchronized."""
with lock:
return func(*args, **kwargs)
return update_func_meta(newfunc, func)
def synchronized (lock):
"""A decorator calling a function with aqcuired lock."""
return lambda func: synchronize(lock, func)
def notimplemented (func):
"""Raises a NotImplementedError if the function is called."""
def newfunc (*args, **kwargs):
"""Raise NotImplementedError"""
raise NotImplementedError("%s not implemented" % func.__name__)
return update_func_meta(newfunc, func)
def timeit (func, log, limit):
"""Print execution time of the function. For quick'n'dirty profiling."""
def newfunc (*args, **kwargs):
"""Execute function and print execution time."""
t = time.time()
res = func(*args, **kwargs)
duration = time.time() - t
if duration > limit:
print >> log, func.__name__, "took %0.2f seconds" % duration
print >> log, args
print >> log, kwargs
return res
return update_func_meta(newfunc, func)
def timed (log=sys.stderr, limit=2.0):
return lambda func: timeit(func, log, limit)
class memoized (object):
"""Decorator that caches a function's return value each time it is called.
If called later with the same arguments, the cached value is returned, and
not re-evaluated."""
def __init__(self, func):
self.func = func
self.cache = {}
def __call__(self, *args):
try:
return self.cache[args]
except KeyError:
self.cache[args] = value = self.func(*args)
return value
except TypeError:
# uncachable -- for instance, passing a list as an argument.
# Better to not cache than to blow up entirely.
return self.func(*args)
def __repr__(self):
"""Return the function's docstring."""
return self.func.__doc__
class curried (object):
"""Decorator that returns a function that keeps returning functions
until all arguments are supplied; then the original function is
evaluated."""
def __init__(self, func, *a):
self.func = func
self.args = a
def __call__(self, *a):
args = self.args + a
if len(args) < self.func.func_code.co_argcount:
return curried(self.func, *args)
else:
return self.func(*args)