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 -- http://mail.python.org/mailman/listinfo/python-list