PEP 359: The "make" Statement

Tim Hochberg tim.hochberg at ieee.org
Fri Apr 14 16:44:50 EDT 2006


Steven Bethard wrote:
> 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::

I think this PEP is going off the rails. It's primary virtue was that it 
was a simpler, clearer way to write:

     class Foo(args):
        __metaclass__ = some_metaclass
        #...

Once it starts calling secret magic methods behind the scenes it's 
losing that virture. And for what? Supporting some use cases that have 
reasonable solutions already?

> 
>      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')


What's the virtue of this form? Is it the indentation? If so, I suspect 
some relatively pretty solution could be manufactured using the 'with' 
syntax. Does anyone really write html 'by hand' in this manner anyway? I 
think this approach may start looking a lot less appealing when you are 
generating the HTML from some other data. Then you have loops inside the 
  body. Somehow you have to clean up all the leftover loop variables so 
they don't end up in your document. However, that would require some 
surgery and increased complexity in Element. It all looks like it would 
be messy and perhaps useless in real life.

Regards,

-tim



> 
>      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