Make sense? It uses Python's string.Template to do the substitution (specifically safe_substitute). It's not going to be super fast because it has to generate a bunch of new tags (turns one tag into many - and that includes multiplying children), then traverse that tree of tags and apply substitution to the attributes and any children that happen to be strings, but I don't expect it to be horrifically slow either.
Of course, it has no effect on speed if you don't use it. And it's only 16 lines of code in one class, so it's not really "bloat".
Some people dislike overloading operators, so another potential way to achieve this same effect (not implemented yet) would be via a new directive:
ul [ foreach ( menu_items ) [ li ( class_ = '$class' ) [ a ( href='$href', class_ = '${class}-link' ) [ '$data' ] ] ] ]
And yet another way might be to use a custom attribute:
ul [ li ( class_ = '$class', foreach = menu_items ) [ a ( href='$href', class_ = '${class}-link' ) [ '$data' ] ] ]
I have no real preference (although the multiplication method is already working).
I know this list is quiet, but I'd really be interested in hearing feedback from people, pro or con, or alternate suggestions for achieving a similar goal.
This feature is now in SVN for anyone who wants to test it out, however, *please* do not use it in production code as the API is still subject to change (or complete removal).
> Make sense? It uses Python's string.Template to do the substitution > (specifically safe_substitute). It's not going to be super fast because > it has to generate a bunch of new tags (turns one tag into many - and > that includes multiplying children), then traverse that tree of tags and > apply substitution to the attributes and any children that happen to be > strings, but I don't expect it to be horrifically slow either.
> Of course, it has no effect on speed if you don't use it. And it's only > 16 lines of code in one class, so it's not really "bloat".
> Some people dislike overloading operators, so another potential way to > achieve this same effect (not implemented yet) would be via a new > directive:
> And yet another way might be to use a custom attribute:
> ul [ > li ( class_ = '$class', foreach = menu_items ) [ > a ( href='$href', class_ = '${class}-link' ) [ '$data' ] > ] > ]
> I have no real preference (although the multiplication method is already > working).
> I know this list is quiet, but I'd really be interested in hearing > feedback from people, pro or con, or alternate suggestions for achieving > a similar goal.
On Sat, 2008-04-05 at 02:39 -0700, Cliff Wells wrote: > It's new, it's controversial... it's multiplication.
> Make sense? It uses Python's string.Template to do the substitution > (specifically safe_substitute). It's not going to be super fast > because it has to generate a bunch of new tags (turns one tag into > many - and that includes multiplying children), then traverse that > tree of tags and apply substitution to the attributes and any children > that happen to be strings, but I don't expect it to be horrifically > slow either.
Actually, my concerns about speed were completely unfounded:
from breve.tags.html import tags from breve.flatten import flatten globals ( ).update ( tags ) from time import time
template1 = html [ body [ ul [ li ( class_ = '$class' ) [ span [ ' - ', a ( href='$href', class_ = '${class}-link' ) [ '$data' ] ], ] * menu_items ] ] ]
template2 = html [ body [ ul [ [ li ( class_ = _m [ 'class' ] ) [ span [ ' - ', a ( href = _m [ 'href' ], class_ = '%(class)s-link' % _m ) [ _m [ 'data' ] ] ], ] for _m in menu_items ] ] ] ]
t = time ( ) flatten ( template1 ) total1 = time ( ) - t
t = time ( ) flatten ( template2 ) total2 = time ( ) - t
print total1, total2
>>> 1.83087301254 1.83428692818
This isn't a scientific test, but it demonstrates that the difference is negligible, even for a list with 10,000 items. Tests were run on a 1.7GHz Centrino laptop w/2GB of RAM.
> This isn't a scientific test, but it demonstrates that the difference is
> negligible, even for a list with 10,000 items.
> Tests were run on a 1.7GHz Centrino laptop w/2GB of RAM.
On Sat, 2008-04-05 at 12:59 -0700, joshl...@gmail.com wrote: > You know that "dual core" of mine that you seems to think is the > computing equivalent of an Escalade SUV?
That's kinda (really) weird... what version of Python? I mean, if it's correct, that's outstanding, but I'm suspicious. Maybe run it again? Or better yet, run each test in a loop:
t = time ( ) for i in range ( 1000 ): flatten ( template1 ) total1 = time ( ) - t
t = time ( ) for i in range ( 1000 ): flatten ( template2 ) total2 = time ( ) - t
On Sat, 2008-04-05 at 11:07 -0700, Cliff Wells wrote: > Actually, my concerns about speed were completely unfounded:
> >>> 1.83087301254 1.83428692818
Bah. Talk about measuring the wrong thing...
Here's the correct test:
import sys from time import time from breve.tags import render_pattern as pattern from breve.tags.html import tags from breve.flatten import flatten globals ( ).update ( tags )
if test == '1': template = html [ body [ ul [ li ( class_ = '$class' ) [ span [ ' - ', a ( href='$href', class_ = '${class}-link' ) [ '$data' ] ], ] * menu_items ] ] ]
if test == '2': template = html [ body [ ul [ [ li ( class_ = _m [ 'class' ] ) [ span [ ' - ', a ( href = _m [ 'href' ], class_ = '%(class)s-link' % _m ) [ _m [ 'data' ] ] ], ] for _m in menu_items ] ] ] ]
total = time ( ) - t print "time to run", total
t = time ( ) flatten ( template ) total = time ( ) - t print "time to flatten", total
[root@portableevil trunk]# python test.py 1 time to run 21.2399451733 time to flatten 2.14032316208
[root@portableevil trunk]# python test.py 2 time to run 1.14334893227 time to flatten 3.54391717911
[root@portableevil trunk]# python test.py 1 time to run 20.7446119785 time to flatten 1.91219210625
[root@portableevil trunk]# python test.py 2 time to run 1.6469810009 time to flatten 2.26762390137
So the new method is *considerably* slower. They just take about the same amount of time to flatten (as they should, since at the time of flattening the DOMs are equivalent).
I'll see if I can't do some optimization, but ultimately this might be the cost of convenience in this case.
I might test with plain Python string interpolation as well since it's around 10x faster than string.Template.
On Sat, 2008-04-05 at 13:52 -0700, Cliff Wells wrote:
> On Sat, 2008-04-05 at 11:07 -0700, Cliff Wells wrote: > > Actually, my concerns about speed were completely unfounded:
> > >>> 1.83087301254 1.83428692818
> Bah. Talk about measuring the wrong thing... > [root@portableevil trunk]# python test.py 1 > time to run 20.7446119785 > time to flatten 1.91219210625
> [root@portableevil trunk]# python test.py 2 > time to run 1.6469810009 > time to flatten 2.26762390137
> So the new method is *considerably* slower. They just take about the > same amount of time to flatten (as they should, since at the time of > flattening the DOMs are equivalent).
> I'll see if I can't do some optimization, but ultimately this might be > the cost of convenience in this case.
Or maybe not so much. By eliminating deepcopy and using copy on demand I avoid traversing the tree twice (once for the deepcopy and once to modify elements). This gave a nice boost (still 10k elements in list):
[root@portableevil trunk]# python test.py 1 time to run 5.17018699646 time to flatten 2.11776804924
20.7s to 5.2s ain't too bad for a few minutes of work =)
Switching from string.Template shaved off another 0.5s, but I don't feel the loss of robustness and simplicity is worth it (string.Template won't throw an error on missing dict keys or stray $'s).
I may try to flatten the tree recursion into iteration to see what effect that has (besides code bloat =).
> Or maybe not so much. By eliminating deepcopy and using copy on demand
> I avoid traversing the tree twice (once for the deepcopy and once to
> modify elements). This gave a nice boost (still 10k elements in list):
> [root@portableevil trunk]# python test.py 1
> time to run 5.17018699646
> time to flatten 2.11776804924
> 20.7s to 5.2s ain't too bad for a few minutes of work =)
> Switching from string.Template shaved off another 0.5s, but I don't feel
> the loss of robustness and simplicity is worth it (string.Template won't
> throw an error on missing dict keys or stray $'s).
> I may try to flatten the tree recursion into iteration to see what
> effect that has (besides code bloat =).
I think overall .5s is no reason to ignore good syntax. I found the
problem with my first test - I don't seem to actually _have_ the
correct stuff in Breve. At least, it's no appearing. Tell me, have
you in fact commited properly to SVN?
And I get this:
Tulkas:~ joshua$ python test1.py 1
Traceback (most recent call last):
File "test1.py", line 3, in <module>
from breve.tags import render_pattern as pattern
ImportError: cannot import name render_pattern
On Sat, 2008-04-05 at 15:11 -0700, joshl...@gmail.com wrote: > I think overall .5s is no reason to ignore good syntax. I found the > problem with my first test - I don't seem to actually _have_ the > correct stuff in Breve. At least, it's no appearing. Tell me, have > you in fact commited properly to SVN?
> And I get this: > Tulkas:~ joshua$ python test1.py 1 > Traceback (most recent call last): > File "test1.py", line 3, in <module> > from breve.tags import render_pattern as pattern > ImportError: cannot import name render_pattern
> Any advice?
Yes, try again. That was an abortive attempt at a custom render tag.
> Yes, try again. That was an abortive attempt at a custom render tag.
> Cliff
Tulkas:~ joshua$ python test1.py 1
time to run 1.52949619293
time to flatten 0.584090948105
Tulkas:~ joshua$ python test1.py 2
time to run 0.327229976654
time to flatten 0.586398124695
Tulkas:~ joshua$
In view of my posting in the "Brevé 2.0 API" thread, I would actually
prefer to write it like this, which will also be a lot faster because
string.Template does not have to be used here and you can actually
just write the following into a Brevé .b file:
html [
body [
macro("menu_item", lambda params: [
li ( class_=param.class_ ) [
a ( href=params.href, class_=params.class_ + '-link' )
[ params.data ]
]),
ul [
[menu_item(args) for args in menu_items]
]
]
]
This seems like a much more natural way to do it, in my opinion.
Nothing special here. No string.Template, no dollar constructions, the
ability to use Python expressions everywhere, etc. No special
multiplication operator either.
--
Sven
On 5 apr, 11:39, Cliff Wells <cl...@twisty-industries.com> wrote:
> Make sense? It uses Python's string.Template to do the substitution
> (specifically safe_substitute). It's not going to be super fast because
> it has to generate a bunch of new tags (turns one tag into many - and
> that includes multiplying children), then traverse that tree of tags and
> apply substitution to the attributes and any children that happen to be
> strings, but I don't expect it to be horrifically slow either.
> Of course, it has no effect on speed if you don't use it. And it's only
> 16 lines of code in one class, so it's not really "bloat".
> Some people dislike overloading operators, so another potential way to
> achieve this same effect (not implemented yet) would be via a new
> directive:
> And yet another way might be to use a custom attribute:
> ul [
> li ( class_ = '$class', foreach = menu_items ) [
> a ( href='$href', class_ = '${class}-link' ) [ '$data' ]
> ]
> ]
> I have no real preference (although the multiplication method is already
> working).
> I know this list is quiet, but I'd really be interested in hearing
> feedback from people, pro or con, or alternate suggestions for achieving
> a similar goal.
instead of without the asterisk. The object "menu_item" that is
created in the variable namespace because of the macro() call should
have a __call__() method that calls its given function with all the
named parameters in a Brevé Namespace object, so that you can easily
access the contents of the dictionary with the attribute accessors
instead of having to write the square brackets to access them.
--
Sven
On 10 apr, 23:35, cbrain <s...@google.berkvens.net> wrote:
> In view of my posting in the "Brevé 2.0 API" thread, I would actually
> prefer to write it like this, which will also be a lot faster because
> string.Template does not have to be used here and you can actually
> just write the following into a Brevé .b file:
> html [
> body [
> macro("menu_item", lambda params: [
> li ( class_=param.class_ ) [
> a ( href=params.href, class_=params.class_ + '-link' )
> [ params.data ]
> ]),
> ul [
> [menu_item(args) for args in menu_items]
> ]
> ]
> ]
> This seems like a much more natural way to do it, in my opinion.
> Nothing special here. No string.Template, no dollar constructions, the
> ability to use Python expressions everywhere, etc. No special
> multiplication operator either.
> --
> Sven
> On 5 apr, 11:39, Cliff Wells <cl...@twisty-industries.com> wrote:
> > Make sense? It uses Python's string.Template to do the substitution
> > (specifically safe_substitute). It's not going to be super fast because
> > it has to generate a bunch of new tags (turns one tag into many - and
> > that includes multiplying children), then traverse that tree of tags and
> > apply substitution to the attributes and any children that happen to be
> > strings, but I don't expect it to be horrifically slow either.
> > Of course, it has no effect on speed if you don't use it. And it's only
> > 16 lines of code in one class, so it's not really "bloat".
> > Some people dislike overloading operators, so another potential way to
> > achieve this same effect (not implemented yet) would be via a new
> > directive:
> > I have no real preference (although the multiplication method is already
> > working).
> > I know this list is quiet, but I'd really be interested in hearing
> > feedback from people, pro or con, or alternate suggestions for achieving
> > a similar goal.
On Thu, 2008-04-10 at 14:35 -0700, cbrain wrote: > Hello Cliff,
> In view of my posting in the "Brevé 2.0 API" thread, I would actually > prefer to write it like this, which will also be a lot faster because > string.Template does not have to be used here and you can actually > just write the following into a Brevé .b file:
> html [ > body [ > macro("menu_item", lambda params: [ > li ( class_=param.class_ ) [ > a ( href=params.href, class_=params.class_ + '-link' ) > [ params.data ] > ]), > ul [ > [menu_item(args) for args in menu_items] > ] > ] > ]
> This seems like a much more natural way to do it, in my opinion. > Nothing special here. No string.Template, no dollar constructions, the > ability to use Python expressions everywhere, etc. No special > multiplication operator either.
I like this idea. I'll see about getting it into SVN for testing.
On Thu, 2008-04-10 at 14:35 -0700, cbrain wrote: > Hello Cliff,
> In view of my posting in the "Brevé 2.0 API" thread, I would actually > prefer to write it like this, which will also be a lot faster because > string.Template does not have to be used here and you can actually > just write the following into a Brevé .b file:
> html [ > body [ > macro("menu_item", lambda params: [ > li ( class_=param.class_ ) [ > a ( href=params.href, class_=params.class_ + '-link' ) > [ params.data ] > ]), > ul [ > [menu_item(args) for args in menu_items] > ] > ] > ]
> This seems like a much more natural way to do it, in my opinion. > Nothing special here. No string.Template, no dollar constructions, the > ability to use Python expressions everywhere, etc. No special > multiplication operator either.
Does this seem close enough?
from breve.tags.html import tags from breve import Namespace globals ( ).update ( tags )
class Macro ( object ): def __init__ ( self, name, function ): self.name = name self.function = function
def macro ( name, function ): # we'd probably do this in real life: register_global ( name, Macro ( name, function ) ) globals ( )[ name ] = Macro ( name, function )
> def macro ( name, function ): > # we'd probably do this in real life: register_global ( name, Macro ( name, function ) ) > globals ( )[ name ] = Macro ( name, function )
Okay, I've committed the change to support the macro feature to svn. However, getting it to work required a bit of hack that I feel a bit dubious about:
# breve.tags.__init__.py
def caller ( ): return sys._getframe ( 2 )
class Macro ( object ): def __init__ ( self, name, function ): self.name = name self.function = function
def macro ( name, function ): frame = caller ( ) frame.f_globals [ name ] = Macro ( name, function ) return ''
Messing with internals isn't something I like to do (although I believe this to be a well-supported method). On the plus side, macros can work both inside a template and standalone:
from breve.tags import macro from breve.tags.html import tags globals ( ).update ( tags )
On Tue, 2008-04-15 at 12:39 -0700, Cliff Wells wrote: > Okay, I've committed the change to support the macro feature to svn. > However, getting it to work required a bit of hack that I feel a bit > dubious about:
> # breve.tags.__init__.py
> def caller ( ): > return sys._getframe ( 2 )
> class Macro ( object ): > def __init__ ( self, name, function ): > self.name = name > self.function = function
> def macro ( name, function ): > frame = caller ( ) > frame.f_globals [ name ] = Macro ( name, function ) > return ''
> Messing with internals isn't something I like to do (although I believe > this to be a well-supported method). On the plus side, macros can work > both inside a template and standalone:
> from breve.tags import macro > from breve.tags.html import tags > globals ( ).update ( tags )