PEP 359: The "make" Statement

Tim Hochberg tim.hochberg at cox.net
Sat Apr 15 12:39:35 EDT 2006


Tim Hochberg wrote:

> Steven Bethard wrote:
>
>> Steven Bethard wrote:
>>
>>> Tim Hochberg wrote:
>>>
>>>> Steven Bethard wrote:
>>>>
>>>>> Steven Bethard wrote:
>>>>>
>>>>>> Duncan Booth wrote:
>>>>>>
>>>>>>> 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?
>>>
>>>
>>> That's why it's in the Open Issues section.  I expect most of these 
>>> open issues to be resolved by rejection.  (At least, that's my 
>>> preferred resolution.)  But since they people have brought them up, 
>>> I think they need to be addressed as completely as possible.
>>>
>>> But I think you make a good point that this particular case can be 
>>> just as easily done using a with-statement (I think).  I'll add that 
>>> to this part of the PEP (once I'm sure it works).
>>
>>
>>
>> Hmm...  Actually, while the with-statement can probably help out with 
>> the nesting, it doesn't help out with the DRY; you still have to 
>> repeat the element name (once for the call to Element, and once as 
>> the name that Element object is bound to).
>
>
>
> I don't think that's correct. I think that with a suitably designed 
> HtmlDocument object, the following should be possible:
>
> with HtmlDocument("Title") as doc:
>     with doc.element("body"):
>        doc.text("before first h1")
>        with doc.element("h1", style="first"):
>           doc.text("first h1")
>        # I don't understand the point of tail, but you could do that too
>        doc.text("after first h1")
>        with doc.element("h1", style="second"):
>            doc.text("second h1")
>        doc.text("after second h1")
>
Here's code to do this. It would be probably be better to use elment 
tree or some such instead of pushing out the HTML directly, but this 
should get the idea across (testing using 2.5a1):



    class HtmlTag(object):
        def __init__(self, document, type, attribs):
            self.document = document
            self.type = type
            self.attribs = attribs
        def __context__(self):
            return self
        def _format_attribs(self):
            if not self.attribs:
                return ''
            return  ' ' + ' '.join('%s="%s"' % (k,v) for
                        k, v in self.attribs.items())
        def __enter__(self):
            self.document.entities.append('<%s%s>' % (self.type,
                                          self._format_attribs()))
            return self
        def __exit__(self, type, value, traceback):
            self.document.entities.append('</%s>' % self.type)
        def add(self):
            self.document.entities.append('<%s%s/>' % (self.type,
                                          self._format_attribs()))

    class HtmlDocument(object):
        def __init__(self):
            self.entities = []
        def tag(self, type, **atribs):
            return HtmlTag(self, type, atribs)
        def text(self, value):
            self.entities.append(value)
        def tostring(self):
            return ''.join(self.entities)
        def __context__(self):
            return self
        def __enter__(self):
            self.entities.append('<html>')
            return self
        def __exit__(self, type, value, traceback):
            if not (type is value is traceback is None):
                raise type(value)
            self.entities.append('</html>')

And here are some simple examples:

    with HtmlDocument() as doc:
        with doc.tag("body"):
           doc.text("before first h1")
           with doc.tag("h1", style="first"):
              doc.text("first h1")
           doc.text("after first h1")
           with doc.tag("h1", style="second"):
               doc.text("second h1")
           doc.text("after second h1")

    expected = '''\
<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>\
'''
    print doc.tostring() == expected

    some_list = ["foo", "bar", 'bazz', "froodle"]

    with HtmlDocument() as doc:
        with doc.tag("body"):
           with doc.tag("h1"):
               doc.text("An ordered list")
           with doc.tag("ol"):
               for value in some_list:
                    with doc.tag('li'):
                        doc.text(value)

    print doc.tostring()




Regards,

-tim

> That seems reasonably DRY compliant. Doc would simply stack and 
> unstack the attributes on the way in and out of the with blocks. This 
> arguably maps better to the underlying HTML as well.
>
> I'd like to reiterate my point that, as far as I can tell, the make 
> statement won't actually work for creating HTML in all but the most 
> trivial of cases. Consider:
>
> with HtmlDocument("Title Here") as doc:
>     with doc.element("body"):
>        with doc.element("h1"):
>            doc.text("An ordered list")
>        with doc.element("ol"): # Ordered list
>            for value in some_list:
>           doc.listitem(value)
>
> If I try to translate this to make syntax, I get into trouble. I end 
> up with 'value' getting set as an element over and over again. Not at 
> all what I want! I suppose you could filter your expression so that 
> only things that subtype some marker class get added, but that's 
> piling weirdness on top of confusion; and good sign that the whole 
> idea is better off abandoned.
>
> -tim
>
>
>
>
>
>
>




More information about the Python-list mailing list