Re: GSoc : Templates Compilation , draft 2

2011-04-08 Thread Andrey Zubko
Hello, Russell Keith-Magee
Thanks for your advices.
*1. Detailed Timeline :*

   - Designing concept : this step requires fully design writing for
   achieving BACKWARD compatibility for all tags, and filters. Providing
   backward-compatibility is a simple task. I've described it earlier by using
   FilterExpression class.
   - Coding "template compilation" subsystem :
  - 4 days term - django.template.loader, django.template.loaders.* -
  adding support for importing already compiled templates.
  - 1 week term - implementing 'compile' method to
  django.template.Template class, in this method will be processed
calling of
  child nodelist compiling and writing to compiled template file.
  - 2 weeks term - implementing 'compile' method to django.templates
  - 1 weeks term - implementing 'compile' method to
  django.template.loader_tags Node-derived classes.
  - 3 days term - implementing support in
  django.template.FilterExpression and django.template.Variable.In 5
  weeks term will be produced such possibilities :
 1. compiling, writing, importing compiled tempaltes files
 2. support for compilation of default filters from
 django.template.defaultfilters, and default tags from
 django.template.defaulttags : , Node, Node-derived classes : TextNode,
 VariableNode, BlockNode, ExtendsNode, ConstantIncludeNode,
IncludeNode,
 AutoEscapeNode, CommentNode, CsrfTokenNode, CycleNode, FilterNode,
 FirstOfNode, ForNode, IfChangedNode, IfEqualNode,
RegroupNode, SsiNode,
 LoadNode, NowNode, SpacelessNode, URLNode, WidthRatioNode, WithNode.
  - 5 days term - implementing 'compile' method for
  django.template.defaulttags.IfNode and implementing compilation
support for
  django.template.smartif.
   - Writing unit-tests :
  - 1 week term - writing unit-tests for compilation process for
  django.template.defaultfilters.
  - 2 week term - writing unit-tests for django.template.defaulttags.
  - 4 days term - writing unit-tests for django.template.loader_tags.
  - 1 week term - writing unit-tests for compilation process for
  django.template.Template, django.template.loader,
django.template.loaders.*.
  Testing compiled tempates writing,import'ing and raw template compiling.
   - 1-2 weeks term - resolving all errors founded by using unit-tests.
   By this step I will produce fully tested template compilation code.
   - 3-5 weeks term - optimization of written code

*2. Armin Ronacher has good template engine that supports template
compilation, and other features. I can not argue against his template engine
solution. But, society should be pointed on :*

   - The default syntax of Jinja2 matches Django syntax in many ways.
   However this similarity doesn’t mean that you can use a Django template
   unmodified in Jinja2. For example filter arguments use a function call
   syntax rather than a colon to separate filter name and arguments.
   Additionally the extension interface in Jinja is fundamentally different
   from the Django one which means that your custom tags won’t work any
   longer.
   - Armin Ronacher wrote about backward-compatibilty: > Are you planning on
   keeping the API for registering template tags > compatible?
   (django.template.Library.register.tag) It will be supported because
   otherwise upgrading won't be possible, but how well this works in detail
   would have to be investigated and what a cleaner implementation looks like.
   Jinja2 has an expression parser and encourages people to create a AST that
   does what they want in extensions. However in Jinja2 I never encouraged
   people to write custom tags so the API is very limited there.  As for
   me, he haven't made decision how it would be looked like in Django. I have
   already proposed solution of template subsystem's improvement.
   - My solution will come with django 'out of the box' against Armin
   Ronacher solution.


On Fri, Apr 8, 2011 at 4:23 AM, Russell Keith-Magee  wrote:

> On Fri, Apr 8, 2011 at 9:15 AM, Andrey Zubko  wrote:
> > Hello,
> > I'm Andrey Zubko, student faculty of Computer Science in Zaporozhye
> > State Engineering Academy. I'm senior chief developer of a local
> > Internet Service Provider "Telza" which provides Internet and
> > telephony services. My responsibilities includes enhancing, improving
> > existent Billing system that is written by myself.
> >
> > In GSOC program I want to improve template subsystem by integrating
> > template compilation into python bytecode.
> >
> > Goals and challenges
> >  Integrating of template compilation has 2 goals. The first goal and
> > the chief goal is to provide backward-compatibility to  templates,
> > custom tags, custom filters  that are already  written.  The second
> > goal is to minimize modifications of Django sources to provide more
> > stability and faster 

Re: GSoc : Templates Compilation , draft 2

2011-04-07 Thread Russell Keith-Magee
On Fri, Apr 8, 2011 at 9:15 AM, Andrey Zubko  wrote:
> Hello,
> I'm Andrey Zubko, student faculty of Computer Science in Zaporozhye
> State Engineering Academy. I'm senior chief developer of a local
> Internet Service Provider "Telza" which provides Internet and
> telephony services. My responsibilities includes enhancing, improving
> existent Billing system that is written by myself.
>
> In GSOC program I want to improve template subsystem by integrating
> template compilation into python bytecode.
>
> Goals and challenges
>  Integrating of template compilation has 2 goals. The first goal and
> the chief goal is to provide backward-compatibility to  templates,
> custom tags, custom filters  that are already  written.  The second
> goal is to minimize modifications of Django sources to provide more
> stability and faster integrating in trunk.
> Implementation
> Support of template compilation can be achieved by adding method
> 'compile' to every Node-derived class, and some other classes,
> functions, such as django.template.Variable,
> django.template.FilterExpression, django.template.NodeList, and others
> described further.
>
> 1. Node and Node-derived classes modifications
> Class Node and derived from it classes should have optional method –
> compile, it returns list of python bytecode commands for write in
> compiled template. It receives parameter parent_name that will
> contains name of uppermost tag/block. Parent_name is a name of
> uppermost tag/block that identifies entry in hash blocks_output that
> is used for caching all outputs. Only one case can provide parent_name
> to be overridden – meet of the '{% block %}' tag in a parent tag, this
> tag should override parent_name to its unique name. Approach for
> caching all output is used for providing template inheritance ability.
> Use of template Inheritance provides following cases :
> child template has one sequence of blocks, but parent template has
> another sequence of blocks
> child template has blocks that haven't specified in parent's template
>
> For solving this cases I provided list blocks_sequence_output that
> keeps sequence of blocks outputting. Implementation of 'compile'
> method in Node class :
>    def compile(self,parent_name):
>        self.generate_name(parent_name)
>        compiled_code = ["t = Template('%s')" % self.template,
>                         "blocks_output['%s'] = blocks_output['%s'] +
> t._render(context)" % (self.parent_name,self.parent_name)]
>        return compiled_code
>
> As you saw, in purposes of providing backward-compatibility all Node-
> derived classes, that haven't implemented 'compile' method, will use
> current rendering approach.
> Node-derived classes with implemented 'compile' method will be looked
> like :
> class TextNode(Node):
>    def compile(self,parent_name):
>        return ["blocks_output['%s'] = blocks_output['%s'] + \"\"\"%s
> \"\"\"" % (self.parent_name,
>
> self.parent_name,
>
> self.s)]
>
> As shown before 'compile' method is not so difficult to implement.
> To recursively compilation of Node-derived classes is used method
> 'compile' in the class NodeList :
> class NodeList(list):
>      def generate_name(self,node):
>        return id(node)
>
>    def compile(self, parent_name=None):
>        bits = []
>        for node in self:
>            if isinstance(node, Node):
>                if parent_name is None or isinstance(node,
> BlockNode) :
>                    if isinstance(node, BlockNode) :
>                        parent_name_new = node.name
>                    else :
>                        parent_name_new = self.generate_name(node)
>
>                    bits.append("blocks_output['%s']=''" %
> parent_name_new)
>                else :
>                    parent_name_new = parent_name
>
>                bits.append(self.compile_node(node, parent_name_new))
>            else:
>                bits.append(node)
>        return mark_safe(''.join([force_unicode(b) for b in bits]))
>
> Also this function is used for initialising blocks_output hashes for
> uppermost nodes.
>
> 2. Blocks and dynamical inheritance
> I have described before Node-derived classes improvements, but
> django.template.loader_tags.BlockNode, django.template.loader_tags,
> django.template.loader_tages.ExtendsNode and others have big influence
> on compiled template architecture design, that's why I have allocated
> whole section for them. Internal variables  blocks_output,
> blocks_output_sequence  are used for providing support of dynamic
> inheritance. They specify the way how template should be rendered, in
> what sequence, what blocks should be outputted to user.
> 'blocks_output' hash collects blocks or tags outputs, and
> blocks_output_sequence saves sequence of block outputting. Because
> parent template can contain its own sequence of blocks that is saved
> in blocks_output_sequence, we must update the child
> blocks_output_sequence without right of overriding it. 

GSoc : Templates Compilation , draft 2

2011-04-07 Thread Andrey Zubko
Hello,
I'm Andrey Zubko, student faculty of Computer Science in Zaporozhye
State Engineering Academy. I'm senior chief developer of a local
Internet Service Provider "Telza" which provides Internet and
telephony services. My responsibilities includes enhancing, improving
existent Billing system that is written by myself.

In GSOC program I want to improve template subsystem by integrating
template compilation into python bytecode.

Goals and challenges
 Integrating of template compilation has 2 goals. The first goal and
the chief goal is to provide backward-compatibility to  templates,
custom tags, custom filters  that are already  written.  The second
goal is to minimize modifications of Django sources to provide more
stability and faster integrating in trunk.
Implementation
Support of template compilation can be achieved by adding method
'compile' to every Node-derived class, and some other classes,
functions, such as django.template.Variable,
django.template.FilterExpression, django.template.NodeList, and others
described further.

1. Node and Node-derived classes modifications
Class Node and derived from it classes should have optional method –
compile, it returns list of python bytecode commands for write in
compiled template. It receives parameter parent_name that will
contains name of uppermost tag/block. Parent_name is a name of
uppermost tag/block that identifies entry in hash blocks_output that
is used for caching all outputs. Only one case can provide parent_name
to be overridden – meet of the '{% block %}' tag in a parent tag, this
tag should override parent_name to its unique name. Approach for
caching all output is used for providing template inheritance ability.
Use of template Inheritance provides following cases :
child template has one sequence of blocks, but parent template has
another sequence of blocks
child template has blocks that haven't specified in parent's template

For solving this cases I provided list blocks_sequence_output that
keeps sequence of blocks outputting. Implementation of 'compile'
method in Node class :
def compile(self,parent_name):
self.generate_name(parent_name)
compiled_code = ["t = Template('%s')" % self.template,
 "blocks_output['%s'] = blocks_output['%s'] +
t._render(context)" % (self.parent_name,self.parent_name)]
return compiled_code

As you saw, in purposes of providing backward-compatibility all Node-
derived classes, that haven't implemented 'compile' method, will use
current rendering approach.
Node-derived classes with implemented 'compile' method will be looked
like :
class TextNode(Node):
def compile(self,parent_name):
return ["blocks_output['%s'] = blocks_output['%s'] + \"\"\"%s
\"\"\"" % (self.parent_name,
 
self.parent_name,
 
self.s)]

As shown before 'compile' method is not so difficult to implement.
To recursively compilation of Node-derived classes is used method
'compile' in the class NodeList :
class NodeList(list):
  def generate_name(self,node):
return id(node)

def compile(self, parent_name=None):
bits = []
for node in self:
if isinstance(node, Node):
if parent_name is None or isinstance(node,
BlockNode) :
if isinstance(node, BlockNode) :
parent_name_new = node.name
else :
parent_name_new = self.generate_name(node)

bits.append("blocks_output['%s']=''" %
parent_name_new)
else :
parent_name_new = parent_name

bits.append(self.compile_node(node, parent_name_new))
else:
bits.append(node)
return mark_safe(''.join([force_unicode(b) for b in bits]))

Also this function is used for initialising blocks_output hashes for
uppermost nodes.

2. Blocks and dynamical inheritance
I have described before Node-derived classes improvements, but
django.template.loader_tags.BlockNode, django.template.loader_tags,
django.template.loader_tages.ExtendsNode and others have big influence
on compiled template architecture design, that's why I have allocated
whole section for them. Internal variables  blocks_output,
blocks_output_sequence  are used for providing support of dynamic
inheritance. They specify the way how template should be rendered, in
what sequence, what blocks should be outputted to user.
'blocks_output' hash collects blocks or tags outputs, and
blocks_output_sequence saves sequence of block outputting. Because
parent template can contain its own sequence of blocks that is saved
in blocks_output_sequence, we must update the child
blocks_output_sequence without right of overriding it. Because parent
template can contain own blocks that is not present in child template,
we must update child blocks_output hash with not existing items in it.
By solving this requirements class BlockNode will be looked like :

class BlockNode(Node):
   

Re: GSoc : Templates Compilation

2011-04-01 Thread Andrey Zubko
Hi, Jonathan
I was considering two approaches : template compilation with static
inheritance and template compilation with static and dynamic
inheritance.
The first one approach has simple way of compilation :
1.Reading template in string
2.Tokenizing
3.Parsing — creating NodeList structure
4.Recuirsive NodeList tree compiling. Note: parent templatea already
in this tree. And my task is to repeat this structure in python
bytecode.
E.g. Template ('TextNodePart\n{% block some_block %}BlockNode
'some_block'{% if True %}IfNode{% endif %}{%endblock%}') :

from ... import ...

def render(context):
print "TextNodePart"
print "BlockNode 'some_block'"
if True :
print "IfNode"

or, e.g. With static inheritance Template('{% extends 'base.html' %}
SomeTextNode{% block some_block %}{% if True %}ifNode{% endif %}{%
endblock%}'), where base.html — Template('{% block some_block %}{%
endblock %}{% block other_block %}TextNode{% endblock %}') will be
looked :

from … import …

def render(context):
if True :
print "ifNode"
print "TextNode"

But template compilation with dynamic inheritance is a bit more
complicated. Real template structure isn't known at compilation time
of template, because it isn't known what template it will be. At
runtime i tried to find template, compile it, or just import already
compiled template. Structure of final template will be known also at
runtime - thats why I'm collecting blocks output sequence - to output
in this sequence.

E.g. :
child.html :
{% extends parent %}
{% block first_block %}
{% if True %}
some_info
{% endif %}
{% endblock %}
{% block second_block %}
{% endblock %}

parent.html :
TextNode
{% block first_block %}
{% endblock %}

In this example child template has 2 block : first_block and
second_block, but parent template has only 1 block : first_block. And
in this situation I have to print only first_block output...

Compiled child template :
from ... import ...

blocks_output = Template_BlockOutput()
blocks_output_sequence = list()

def output():
for block_name in blocks_output_sequence :
if blocks_output.has_key(block_name) :
print blocks_output[block_name]

def render(context,no_output=False):
blocks_output['first_block'] = '' # entering block tag
blocks_sequence_output.append('first_block')
if True :
blocks_output['first_block'] = blocks_output['first_block'] +
"some_info"
blocks_output['second_block'] = ''  # entering block tag

extenal_blocks_output, external_blocks_sequence_output =
get_compiled_template_or_create(context.parent,no_output=True)
blocks_output.append(external_blocks_output)
blocks_sequence_output = external_blocks_sequence_output

""" function footer """
if no_output :
return blocks_output, blocks_output_sequence
else :
output()

Compiled parent template :
from ... import ...

blocks_output = Template_BlockOutput()
blocks_output_sequence = list()

def output():
for block_name in blocks_output_sequence :
if blocks_output.has_key(block_name) :
print blocks_output[block_name]

def render(context,no_output=False):
blocks_output['first_block'] = '' # entering block tag
blocks_sequence_output.append('first_block')
""" function footer """
if no_output :
return blocks_output, blocks_output_sequence
else :
output()


This is draft version of compilation, it has number of defects. I'm
solving them now.

On Apr 1, 12:50 am, Jonathan Slenders 
wrote:
> Another one:
>
> Instead of:
>     def has_key(self,key):
>         for level in xrange(0,len(self._levels)) :
>             if self._levels[level].has_key(key) :
>                 return True
>         return False
>
> do:
>     def has_key(self,key):
>         return any((key in l) for l in self._levels)
>
> Ony one "self" reference, and any-through-iterator is very fast, as
> it's native python code (probably implemented in C). And lazy
> evaluating.
>
> It's challenging to understand your code. Maybe it's easier if you
> explain in words-only how you want to implement inheritance.
>
> -
>
> My approach would be like this
>
> For a template called 'x', create a dictionary 'x' with maps the block
> names for this template to a func with it's matching implementation
> code.
> Somehow, a method render_block(name) should be made available. So when
> a blocks are nested, one block should be able to call
> render_block(other_name). The render_block method should do a lookup
> in the dictionary for a block with this name, and call it.
>
> Dynamic inheritance can easily be realized by replacing key/value
> pairs in this dict by other implementations. Pass the original
> implementation to the new implementation as an additional function
> parameter. (As a curry.)
>
> I guess it's fast, and can be cached in memory. For each rendering,
> only a 

Re: GSoc : Templates Compilation

2011-03-31 Thread Jonathan Slenders
Another one:

Instead of:
def has_key(self,key):
for level in xrange(0,len(self._levels)) :
if self._levels[level].has_key(key) :
return True
return False

do:
def has_key(self,key):
return any((key in l) for l in self._levels)

Ony one "self" reference, and any-through-iterator is very fast, as
it's native python code (probably implemented in C). And lazy
evaluating.

It's challenging to understand your code. Maybe it's easier if you
explain in words-only how you want to implement inheritance.

-

My approach would be like this


For a template called 'x', create a dictionary 'x' with maps the block
names for this template to a func with it's matching implementation
code.
Somehow, a method render_block(name) should be made available. So when
a blocks are nested, one block should be able to call
render_block(other_name). The render_block method should do a lookup
in the dictionary for a block with this name, and call it.

Dynamic inheritance can easily be realized by replacing key/value
pairs in this dict by other implementations. Pass the original
implementation to the new implementation as an additional function
parameter. (As a curry.)

I guess it's fast, and can be cached in memory. For each rendering,
only a copy of the required template dictionaries have to be made.
(Not even a deep copy.)

---

Cheers,
Jonathan

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-developers@googlegroups.com.
To unsubscribe from this group, send email to 
django-developers+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en.



Re: GSoc : Templates Compilation

2011-03-31 Thread Jonathan Slenders
Hi Andrey

I haven't yet looked through all of your code, but already a little
feedback.

Remember that every dot operator requires a dictionary lookup or
getattr call. Also the [ ]-operator is another level of redirection. I
think using 'self' inside a for-loop is not a good idea. If you're
going to compile, you really want to optimize every possible detail.


On Mar 31, 6:00 pm, Andrey Zubko  wrote:
> ...
>     def super(self,key):
>         if len(self._levels) > 1 :
>             for level in xrange(1,len(self._levels)) :
>                 if self._levels[level].has_key(key) :
>                     return self._levels[level][key]
>         else :
>             if self._levels[0].has_key(key) :
>                 return self._levels[key]   ###  You mean:  return 
> self._levels[0][key]
>             else :
>                 return ""


Maybe turn it into this:

>     def super(self,key):
> levels = self._levels
>         if len(levels) > 1 :
> iter = levels.__iter__()
> iter.next() # Ignore first item
> for level in iter:
>                 if level.has_key(key) :
>                     return level[key]
>         else :
>             return levels[0].get(key, "")


Now you have only one lookup in 'self', half the amount of index
operators (has_key counts as index operator), no additional xrange
iterator, and only one call of "len". Gaining probably twice the
speed.


But is it really necessary to use classes and instance methods instead
of just plain methods in a module? A template should be stateless. (we
have render_context for the state when required.) Classes always have
overhead compared to methods.




-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-developers@googlegroups.com.
To unsubscribe from this group, send email to 
django-developers+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en.



Re: GSoc : Templates Compilation

2011-03-31 Thread Andrey Zubko
Hello, Jonathan
 > How exactly do you want to solve dynamic inheritance? It seems that
 > some people are still interested in dynamic inheritance, by using
{%
 > extends variable %}. (Which is still dirty in my opinion, but
anyway.)
 > Block information has to be kept somehow.

For supporting dynamic inheritance compiled template will be looked
more complicated. To resolve this i should :
1.Create class Template_BlockOutput similiar to ContextRender  :

class Template_BlockOutput(object):
def __init__(self):
self._levels = [{}]

def __getitem__(self,key):
for level in xrange(0,len(self._levels)) :
if self._levels[level].has_key(key) :
return self._levels[level][key]
raise AttributeError()

def super(self,key):
if len(self._levels) > 1 :
for level in xrange(1,len(self._levels)) :
if self._levels[level].has_key(key) :
return self._levels[level][key]
else :
if self._levels[0].has_key(key) :
return self._levels[key]
else :
return ""

def has_key(self,key):
for level in xrange(0,len(self._levels)) :
if self._levels[level].has_key(key) :
return True
return False

def __setitem__(self,key,value):
self._levels[0][key] = value

def append(self,d):
if isinstance(d, Template_BlockOutput) :
for level in d._levels :
self._levels.append(level)
elif isinstance(d,list) :
for level in d :
self._levels.append(level)
elif isinstance(d,dict) :
self._levels.append(d)

The instance of this class will be named as «blocks_output». Saving
all blocks output is used
for block.super expression.

2.Add list named blocks_sequence_output, that will contain blocks
output sequence. Every time parser will find «extends» tag —  the list
will be overridden by parent blocks output sequence.
3.Add get_compiled_template_or_create function to
django.template.loader that will be imported in compiled template for
compiling and importing external template.
To support all that requirements classes BlockNode, ExtendsNode should
contain compile functions like :

class BlockNode(Node):
   def super_compile(self):
compiled_code = ["blocks_output.super('%s')" % self.name]
return compiled_code

def compile(self,parent_name):
compiled_childs = self.nodelist.compile(self.name)
compiled_code = ["blocks_output['%s'] = %s"  %
(self.name,compiled_childs),
 "blocks_sequence_output.append('%s')" %
self.name]
return compiled_code

class ExtendsNode(Node):
def get_parent_compiled(self):
if self.parent_name_expr:
parent = self.parent_name_expr.compile()
else :
parent = self.parent_name
if not parent:
error_msg = "Invalid template name in 'extends' tag:
%r." % parent
if self.parent_name_expr:
error_msg += " Got this from the '%s' variable." %
self.parent_name_expr.token
raise TemplateSyntaxError(error_msg)
if hasattr(parent, 'compile'):
return parent.compile() # parent is a Template object

code = ["extenal_blocks_output,
external_blocks_sequence_output =
get_compiled_template_or_create(%s,no_output=True)" %
self.parent_name,
"blocks_output.append(external_blocks_output)",
"blocks_sequence_output =
external_blocks_sequence_output"]

return code

def compile(self):
compiled_parent = self.get_compiled_parent()
return compiled_parent


And compiled template structure for supporting dynamic template
extending :

from ... import ...

def resolve(context,var_name):
""" should be looked like method _resolve_lookup in
django.template.Variable """

blocks_output = Template_BlockOutput()
blocks_output_sequence = list()

def output():
for block_name in blocks_output_sequence :
if blocks_output.has_key(block_name) :
print blocks_output[block_name]

def render(context,no_output=False):
""" compiled template will be here """

""" function footer """
if no_output :
return blocks_output, blocks_output_sequence
else :
output()

Yours Suggestion  :
 > Maybe one small improvement. Isn't the following
 >  > date(context.variable or "Default value"),"Y-m-d") faster than
this?
 >  > date(default(resolve(context,variable),"Default value"),"Y-m-d")
Yes, it will be good to add to every filter function additional
«compile» function that will generate optimesed code. E.g. :
def default_compiled(value, arg):
"""If value is unavailable, use given default."""
return "context.%s or '%s'" % (value,arg)
default_compiled.is_safe = False

register_compiled = Library()


Re: GSoc : Templates Compilation

2011-03-30 Thread Jonathan Slenders
How exactly do you want to solve dynamic inheritance? It seems that
some people are still interested in dynamic inheritance, by using {%
extends variable %}. (Which is still dirty in my opinion, but anyway.)
Block information has to be kept somehow.


Maybe one small improvement. Isn't the following
  > date(context.variable or "Default value"),"Y-m-d")
faster than this?
  > date(default(resolve(context,variable),"Default value"),"Y-m-d")


P.S. I like the cleanness of adding an (optional?) compile method to
every Node-derived class.
But on the other hand, I would like to see Django move to a more end-
user-friendly way of defining custom template tags. A way which does
not involve any knowledge of the template parser/interpreter/compiler.
Of course, that could also be build upon the first.



-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-developers@googlegroups.com.
To unsubscribe from this group, send email to 
django-developers+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en.



GSoc : Templates Compilation

2011-03-30 Thread Andrey Zubko
I'm interested in participating  in your mentors program in Google
Summer Code.
Looking at the template implementation I've seen that I can make some
solutions  for implementation compiling in bytecode templates. Just
adding function «compile» to every Node-derived class and modifying/
adding new functionality to internal functions,classes, such as
django.template.Variable, django.template.FilterExpression, and
others. E.g. Node-derived classes :

class TextNode(Node):
def compile(self):
return "print \"\"\"%s\"\"\";\n"  % self.s

class Variable(object):
def compile(self):
print "resolve(context,%s)" % self.lookups

class FilterExpression(object):
def compile(self, ignore_failures=False):
if isinstance(self.var, Variable):
try:
obj = self.var.compile()
except VariableDoesNotExist:
if ignore_failures:
obj = None
else:
if settings.COMPILED_TEMPLATE_STRING_IF_INVALID:
global invalid_var_format_string
if invalid_var_format_string is None:
invalid_var_format_string = '%s' in
settings.COMPILED_TEMPLATE_STRING_IF_INVALID
if invalid_var_format_string:
return
settings.COMPILED_TEMPLATE_STRING_IF_INVALID % self.var
return
settings.COMPILED_TEMPLATE_STRING_IF_INVALID
else:
obj =
settings.COMPILED_TEMPLATE_STRING_IF_INVALID
else:
obj = self.var

for func, args in self.filters:
arg_vals = ""
i = 0
for lookup, arg in args:
if i > 0 :
arg_vals = arg_vals + ","
if not lookup:
arg_vals = arg_vals + "\"%s\"" % mark_safe(arg)
else:
arg_vals = arg_vals + "%s" % arg.compile(context)

if getattr(func, 'needs_autoescape', False):
new_obj = "%s(%s,autoescape=context.autoescape,%s)" %
(func.__name__,obj,arg_vals)
#new_obj = func(obj, autoescape=context.autoescape,
*arg_vals)
else:
new_obj = "%s(%s,%s)" % (func.__name__,obj,arg_vals)
#new_obj = func(obj, *arg_vals)
if getattr(func, 'is_safe', False) and isinstance(obj,
SafeData):
obj = "mark_safe(%s)" % new_obj
elif isinstance(obj, EscapeData):
obj = "mark_for_escaping(%s)" % new_obj
else:
obj = new_obj
return obj

Simple example of used modifications :

from django.template import Parser, FilterExpression

token = 'variable|default:"Default value"|date:"Y-m-d"'
p = Parser('')
fe = FilterExpression(token, p)
fe.compile()

will produce :
'date(default(resolve(context,variable),"Default value"),"Y-m-d")'

Structure of compiled template will consists of 2 parts :
Dependencies part. Section consists of importing modules. Planned that
dependencies should be added by using {% load %} tag for developer-
written tags.
Code part. Section that will consist of internal template functions,
such as «resolve» that will get variable name and resolve it using
current context, and «render» that will consist of compiled template.
E.g. :

# default imports
from django.template.defaulttags import *
from django.template.defaultfilters import *
# importing external modules, section created by using {% load %} tag
from ... import ...

def resolve(context,var_name):
""" should be looked like method _resolve_lookup in
django.template.Variable """

def render(context):
""" compiled template will be here """

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-developers@googlegroups.com.
To unsubscribe from this group, send email to 
django-developers+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en.