PEP 359: The "make" Statement
Steven Bethard
steven.bethard at gmail.com
Fri Apr 14 11:31:28 EDT 2006
Steven Bethard wrote:
> Duncan Booth wrote:
>> Steven Bethard wrote:
>>
>>> Should users of the make statement be able to determine in which dict
>>> object the code is executed? The make statement could look for a
>>> ``__make_dict__`` attribute and call it to allow things like::
>>>
>>> make Element html:
>>> make Element body:
>>> make Element h1:
>>> '''First heading text'''
>>> make Element h1:
>>> '''Second heading text'''
>>
> [snip]
>> There is another effect which should be considered here. If you allow
>> Element to create an object to be used as the namespace, then as well
>> as doing special tracking when values are set in the namespace it can
>> also pre-seed it with names which magically appear in scope within the
>> make.
>>
>> e.g.
>>
>> make Element html:
>> make Element body:
>> make Element p:
>> text('But this ')
>> make Element strong:
>> text('could')
>> text(' be made to work')
>
> This is nice. I'll have to play around with it a bit to see how hard it
> would be to make it work.
Okay, I think it'll work[1]. I'm going to update this section to
something more like:
Open Issues
===========
...
Should users of the make statement be able to determine in which dict
object the code is executed? This would allow the make statement to
be used in situations where a normal dict object would not suffice,
e.g. if order and repeated names must be allowed. Allowing this sort
of customization could allow XML to be written like::
make Element html:
make Element body:
text('before first h1')
make Element h1:
attrib(style='first')
text('first h1')
tail('after first h1')
make Element h1:
attrib(style='second')
text('second h1')
tail('after second h1')
assert etree.ElementTree.tostring(body) == '''\
<html>\
<body>\
before first h1\
<h1 style="first">first h1</h1>\
after first h1\
<h1 style="second">second h1</h1>\
after second h1\
</body>\
</html>\
'''
Assuming that the make statement calls the callable's
``__make_dict__`` to get the dict in which to execute the code, the
following should make the above make statements work::
class Element(object):
class __make_dict__(dict):
def __init__(self, *args, **kwargs):
self._super = super(Element.__make_dict__, self)
self._super.__init__(*args, **kwargs)
self.elements = []
self.text = None
self.tail = None
self.attrib = {}
def __getitem__(self, name):
try:
return self._super.__getitem__(name)
except KeyError:
if name in ['attrib', 'text', 'tail']:
return getattr(self, 'set_%s' % name)
else:
return globals()[name]
def __setitem__(self, name, value):
self._super.__setitem__(name, value)
self.elements.append(value)
def set_attrib(self, **kwargs):
self.attrib = kwargs
def set_text(self, text):
self.text = text
def set_tail(self, text):
self.tail = text
def __new__(cls, name, args, edict):
get_element = etree.ElementTree.Element
result = get_element(name, attrib=edict.attrib)
result.text = edict.text
result.tail = edict.tail
for element in edict.elements:
result.append(element)
return result
[1] Here's the code I used to test it.
>>> def make(callable, name, args, block_string):
... try:
... make_dict = callable.__make_dict__
... except AttributeError:
... make_dict = dict
... block_dict = make_dict()
... exec block_string in block_dict
... return callable(name, args, block_dict)
...
>>> class Element(object):
... class __make_dict__(dict):
... def __init__(self, *args, **kwargs):
... self._super = super(Element.__make_dict__, self)
... self._super.__init__(*args, **kwargs)
... self.elements = []
... self.text = None
... self.tail = None
... self.attrib = {}
... def __getitem__(self, name):
... try:
... return self._super.__getitem__(name)
... except KeyError:
... if name in ['attrib', 'text', 'tail']:
... return getattr(self, 'set_%s' % name)
... else:
... return globals()[name]
... def __setitem__(self, name, value):
... self._super.__setitem__(name, value)
... self.elements.append(value)
... def set_attrib(self, **kwargs):
... self.attrib = kwargs
... def set_text(self, text):
... self.text = text
... def set_tail(self, text):
... self.tail = text
... def __new__(cls, name, args, edict):
... get_element = etree.ElementTree.Element
... result = get_element(name, attrib=edict.attrib)
... result.text = edict.text
... result.tail = edict.tail
... for element in edict.elements:
... result.append(element)
... return result
...
>>> body = make(Element, 'body', (), textwrap.dedent("""\
... text('before first h1')
... h1 = make(Element, 'h1', (), textwrap.dedent('''
... attrib(style='first')
... text('first h1')
... tail('after first h1')
... '''))
... h1 = make(Element, 'h1', (), textwrap.dedent('''
... attrib(style='second')
... text('second h1')
... tail('after second h1')
... '''))
... """))
>>> assert etree.ElementTree.tostring(body) == '''\
... <body>\
... before first h1\
... <h1 style="first">first h1</h1>\
... after first h1\
... <h1 style="second">second h1</h1>\
... after second h1\
... </body>\
... '''
STeVe
More information about the Python-list
mailing list