diff options
author | michele.simionato <devnull@localhost> | 2008-12-11 06:58:37 +0000 |
---|---|---|
committer | michele.simionato <devnull@localhost> | 2008-12-11 06:58:37 +0000 |
commit | 80bf8764d75e332f51fba758beb8ec494bcabd8e (patch) | |
tree | f7b96d4c6be3a911804454d61fc49d564580e3f2 /decorator | |
parent | 46b4f268432eca18e30171f1ee0d353d1feae8c9 (diff) | |
download | micheles-80bf8764d75e332f51fba758beb8ec494bcabd8e.tar.gz |
Various improvements to the management of the source code
Diffstat (limited to 'decorator')
-rwxr-xr-x | decorator/decorator.py | 49 | ||||
-rw-r--r-- | decorator/documentation.py | 62 |
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() |