summaryrefslogtreecommitdiff
path: root/decorator
diff options
context:
space:
mode:
authormichele.simionato <devnull@localhost>2008-12-11 06:58:37 +0000
committermichele.simionato <devnull@localhost>2008-12-11 06:58:37 +0000
commit80bf8764d75e332f51fba758beb8ec494bcabd8e (patch)
treef7b96d4c6be3a911804454d61fc49d564580e3f2 /decorator
parent46b4f268432eca18e30171f1ee0d353d1feae8c9 (diff)
downloadmicheles-80bf8764d75e332f51fba758beb8ec494bcabd8e.tar.gz
Various improvements to the management of the source code
Diffstat (limited to 'decorator')
-rwxr-xr-xdecorator/decorator.py49
-rw-r--r--decorator/documentation.py62
2 files changed, 56 insertions, 55 deletions
diff --git a/decorator/decorator.py b/decorator/decorator.py
index 0253ad6..b2c3304 100755
--- a/decorator/decorator.py
+++ b/decorator/decorator.py
@@ -27,14 +27,22 @@ for the documentation.
__all__ = ["decorator", "FunctionMaker", "getinfo", "new_wrapper"]
-import os, sys, re, inspect, warnings, tempfile
+import os, sys, re, inspect, warnings
DEF = re.compile('\s*def\s*([_\w][_\w\d]*)\s*\(')
-# helper
-def _callermodule(level=2):
- return sys._getframe(level).f_globals.get('__name__', '?')
+# patch inspect.getsource to recognize the __source__ attribute
+inspect_getsource = inspect.getsource
+def getsource(obj):
+ "A replacement for inspect.getsource honoring the __source__ attribute"
+ try:
+ return obj.__source__
+ except AttributeError:
+ return inspect_getsource(obj)
+
+inspect.getsource = getsource
+
# basic functionality
class FunctionMaker(object):
"""
@@ -67,8 +75,8 @@ class FunctionMaker(object):
self.module = module
if funcdict:
self.dict = funcdict
- assert self.name and hasattr(self, 'signature')
# check existence required attributes
+ assert self.name and hasattr(self, 'signature')
def update(self, func, **kw):
"Update the signature of func with the data in self"
@@ -76,10 +84,11 @@ class FunctionMaker(object):
func.__doc__ = getattr(self, 'doc', None)
func.__dict__ = getattr(self, 'dict', {})
func.func_defaults = getattr(self, 'defaults', None)
- func.__module__ = getattr(self, 'module', _callermodule())
+ callermodule = sys._getframe(3).f_globals.get('__name__', '?')
+ func.__module__ = getattr(self, 'module', callermodule)
func.__dict__.update(kw)
- def make(self, src_templ, save_source=False, **evaldict):
+ def make(self, src_templ, **evaldict):
"Make a new function from a given template and update the signature"
src = src_templ % vars(self) # expand name and signature
mo = DEF.match(src)
@@ -89,22 +98,26 @@ class FunctionMaker(object):
reserved_names = set([name] + [
arg.strip(' *') for arg in self.signature.split(',')])
s = ''
- for n, v in evaldict.items():
+ for n, v in evaldict.iteritems():
if inspect.isfunction(v):
s += '# %s=<function %s.%s>\n' % (n, v.__module__, v.__name__)
+ elif isinstance(v, basestring):
+ s += '# %s=\n%s\n' % (name, '\n'.join(
+ '## ' + line for line in v.splitlines()))
+ else:
+ s += '# %s=%r\n' % (n, v)
if n in reserved_names:
raise NameError('%s is overridden in\n%s' % (n, src))
source = s + src
if not source.endswith('\n'): # add a newline just for safety
source += '\n'
- if save_source:
- fhandle, fname = tempfile.mkstemp()
- os.write(fhandle, source)
- os.close(fhandle)
- else:
- fname = '?'
- code = compile(source, fname, 'single')
- exec code in evaldict
+ try:
+ code = compile(source, '<string>', 'single')
+ exec code in evaldict
+ except:
+ print >> sys.stderr, 'Error in generated code:'
+ print >> sys.stderr, source
+ raise
func = evaldict[name]
self.update(func, __source__=source)
return func
@@ -124,7 +137,9 @@ def decorator(caller, func=None):
fun = FunctionMaker(func)
src = """def %(name)s(%(signature)s):
return _call_(_func_, %(signature)s)"""
- return fun.make(src, _func_=func, _call_=caller)
+ decorated = fun.make(src, _func_=func, _call_=caller)
+ decorated.__source__ = inspect_getsource(func)
+ return decorated
###################### deprecated functionality #########################
diff --git a/decorator/documentation.py b/decorator/documentation.py
index be2a3a2..6e92c22 100644
--- a/decorator/documentation.py
+++ b/decorator/documentation.py
@@ -265,7 +265,7 @@ calling func with args (), {}
For the rest of this document, I will discuss examples of useful
decorators built on top of ``decorator``.
-``delayed`` and ``threaded``
+``delayed`` and ``async``
--------------------------------------------
Often, one wants to define families of decorators, i.e. decorators depending
@@ -306,40 +306,34 @@ to deserve a name:
.. code-block:: python
- threaded = delayed(0)
+ async = delayed(0, name='async') # no-delay decorator
-Threaded procedures will be executed in a separated thread as soon
-as they are called. Here is an example.
+Asynchronous procedures will be executed in a parallel thread.
Suppose one wants to write some data to
an external resource which can be accessed by a single user at once
(for instance a printer). Then the access to the writing function must
-be locked:
+be locked. Here is a minimalistic example:
-.. code-block:: python
-
- import time
-
- datalist = [] # for simplicity the written data are stored into a list.
-
-$$write
-
-Since the writing function is locked, we are guaranteed that at any given time
-there is at most one writer. Here is an example.
+>>> datalist = [] # for simplicity the written data are stored into a list.
->>> @threaded
-... def writedata(data):
-... write(data)
+>>> @async
+... def write(data):
+... # append data to the datalist by locking
+... with threading.Lock():
+... time.sleep(1) # emulate some long running operation
+... datalist.append(data)
+... # other operations not requiring a lock here
-Each call to ``writedata`` will create a new writer thread, but there will
+Each call to ``write`` will create a new writer thread, but there will
be no synchronization problems since ``write`` is locked.
->>> writedata("data1")
+>>> write("data1")
<_Timer(Thread-1, started)>
>>> time.sleep(.1) # wait a bit, so we are sure data2 is written after data1
->>> writedata("data2")
+>>> write("data2")
<_Timer(Thread-2, started)>
>>> time.sleep(2) # wait for the writers to complete
@@ -756,18 +750,21 @@ def decorator_apply(dec, func):
return fun.make(src, decorated=dec(func))
def _trace(f, *args, **kw):
- print "calling %s with args %s, %s" % (f.func_name, args, kw)
+ print "calling %s with args %s, %s" % (f.__name__, args, kw)
return f(*args, **kw)
def trace(f):
return decorator(_trace, f)
-def delayed(nsec):
- def _delayed(proc, *args, **kw):
+def delayed(nsec, name='delayed'):
+ def caller(proc, *args, **kw):
thread = threading.Timer(nsec, proc, args, kw)
thread.start()
return thread
- return decorator(_delayed)
+ caller.__name__ = name
+ return decorator(caller)
+
+async = delayed(0, name='async') # no-delay decorator
def identity_dec(func):
def wrapper(*args, **kw):
@@ -781,7 +778,7 @@ def memoize25(func):
func.cache = {}
def memoize(*args, **kw):
if kw:
- key = args, frozenset(kw.items())
+ key = args, frozenset(kw.iteritems())
else:
key = args
cache = func.cache
@@ -795,7 +792,7 @@ def memoize25(func):
def _memoize(func, *args, **kw):
# args and kw must be hashable
if kw:
- key = args, frozenset(kw.items())
+ key = args, frozenset(kw.iteritems())
else:
key = args
cache = func.cache
@@ -809,8 +806,6 @@ def memoize(f):
f.cache = {}
return decorator(_memoize, f)
-threaded = delayed(0) # no-delay decorator
-
def blocking(not_avail="Not Available"):
def _blocking(f, *args, **kw):
if not hasattr(f, "thread"): # no thread running
@@ -921,14 +916,5 @@ def fact(n): # this is not tail-recursive
if n == 0: return 1
return n * fact(n-1)
-datalist = []
-
-def write(data):
- "Writing to a sigle-access resource"
- with threading.Lock():
- time.sleep(1)
- datalist.append(data)
-
-
if __name__ == '__main__':
import doctest; doctest.testmod()