Perhaps I doubt this is a good option for decorators, but here:
import string SelfClosing = object() def escapeAttr(attr): # WARNING: example only, security not guaranteed for any of these functions return attr.replace('"', '\\"') def tag(name, content='', **attributes): # prepare attributes for attr,value in attributes.items(): assert all(c.isalnum() for c in attr) # probably want to check xml spec attrString = ' '.join('{}="{}"'.format(k,escapeAttr(v)) for k,v in attributes.items()) if not content==SelfClosing: return '<{name} {attrs}>{content}</{name}>'.format( name = name, attrs = attrString, content = content ) else: # self-closing tag return '<{name} {attrs}/>'
Example:
def makeBoldWrapper(**attributes): def wrapWithBold(origFunc): def composed(*args, **kw): result = origFunc(*args, **kw) postprocessed = tag('b', content=result, **attributes) return postprocessed return composed return wrapWithBold
Demo:
@makeBoldWrapper(attr1='1', attr2='2') def helloWorld(text): return text >>> print( helloWorld('Hello, world!') ) <b attr2="2" attr1="1">Hello, world!</b>
A common misconception with decorators is that the parameters (attr1=...) are the parameters of the @myDecorator decorator; This is not relevant. Rather, the result of calling the myDecoratorFactory(attr1=...) function is evaluated as someresult and becomes an anonymous @someresult decorator. Therefore, โdecorators with argumentsโ are actually decorator factories that should return a decorator as a value.
source share