Skip to content

Commit

Permalink
py3k support
Browse files Browse the repository at this point in the history
  • Loading branch information
barbuza committed Nov 6, 2013
1 parent 572073b commit ef20bd0
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 38 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/venv/
/venv3/
/build/
/hamly/*.so
*.pyc
/dist/
/hamly.egg-info/
/hamly.egg-info/
5 changes: 5 additions & 0 deletions benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,8 @@

table = [dict(a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10)
for x in range(1000)]

# print "-" * 80
# print(hamly_template.template_source)

# print(hamly_template(table=[dict(a=1,b=2) for x in range(2)]))
14 changes: 7 additions & 7 deletions hamly/ast_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,23 @@ def scalar_to_ast(value):
return ast.Name("False", ast.Load())
elif value is None:
return ast.Name("None", ast.Load())
elif isinstance(value, basestring):
elif isinstance(value, str):
return ast.Str(value)
elif isinstance(value, int) or isinstance(value, float):
return ast.Num(value)
elif isinstance(value, list):
return ast.List(map(scalar_to_ast, value), ast.Load())
return ast.List([scalar_to_ast(x) for x in value], ast.Load())
elif isinstance(value, tuple):
return ast.Tuple(map(scalar_to_ast, value), ast.Load())
return ast.Tuple([scalar_to_ast(x) for x in value], ast.Load())
elif isinstance(value, dict):
return ast.Dict(map(scalar_to_ast, value.keys()),
map(scalar_to_ast, value.values()))
return ast.Dict([scalar_to_ast(x) for x in value.keys()],
[scalar_to_ast(x) for x in value.values()])
else:
return value


def make_call(name, *args):
ast_args = map(scalar_to_ast, args)
ast_args = [scalar_to_ast(x) for x in args]
return ast.Call(ast.Name(name, ast.Load()), ast_args, [], None, None)


Expand All @@ -35,7 +35,7 @@ def make_expr(node):


def make_tuple(*elts):
ast_elts = map(scalar_to_ast, elts)
ast_elts = [scalar_to_ast(x) for x in elts]
return ast.Tuple(ast_elts, ast.Load())


Expand Down
6 changes: 3 additions & 3 deletions hamly/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def tagnode_to_ast(node):
if node.attrs:
for key, value in node.attrs.items():
callargs.append(make_tuple(key, value))
block = sum(map(node_to_ast, node.children), [make_expr(make_call(OPEN_TAG, *callargs))])
block = sum([node_to_ast(x) for x in node.children], [make_expr(make_call(OPEN_TAG, *callargs))])
block.append(make_expr(make_call(WRITE, "</%s>\n" % node.tagname)))
return block

Expand All @@ -35,8 +35,8 @@ def controlnode_to_ast(node):
return [ast.Break()]
mod = ast.parse("%s\n pass" % node.code)
ctrl = mod.body[0]
ctrl.body = sum(map(node_to_ast, node.children), [])
ctrl.orelse = sum(map(node_to_ast, node.orelse), [])
ctrl.body = sum([node_to_ast(x) for x in node.children], [])
ctrl.orelse = sum([node_to_ast(x) for x in node.orelse], [])
return [ctrl]


Expand Down
1 change: 1 addition & 0 deletions hamly/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
TO_STRING = "_h_to_string"
QUOTEATTR = "_h_quoteattr"
WRITE_ATTRS = "_h_write_attrs"
MAIN = "_h_main"
49 changes: 34 additions & 15 deletions hamly/loader.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
# -*- coding: utf-8 -*-

import sys
import ast

from six import exec_

from .escape import escape, quoteattr, soft_unicode
from .html import write_attrs
from .parser import parse
from .compiler import compile_tree
from .optimizer import optimize
from .const import WRITE, TO_STRING, ESCAPE, WRITE_MULTI, QUOTEATTR, WRITE_ATTRS
from .const import (WRITE, TO_STRING, ESCAPE, WRITE_MULTI,
QUOTEATTR, WRITE_ATTRS, MAIN)

cache = {}

Expand All @@ -13,33 +20,45 @@ def get_template(filename):

if not cached:
with open(filename) as fp:
source = fp.read().decode("utf-8")
source = fp.read()#.decode("utf-8")

tree = parse(source)
compiled = compile_tree(tree)
module = ast.Module(compiled)
optimized = optimize(module)


optimized.body.insert(0, ast.ImportFrom(module='hamly.escape',
names=[ast.alias(name='escape', asname=ESCAPE),
ast.alias(name='quoteattr', asname=QUOTEATTR),
ast.alias(name='soft_unicode', asname=TO_STRING)], level=0))
optimized.body.insert(0, ast.ImportFrom(module='hamly.html', names=[ast.alias(name='write_attrs', asname=WRITE_ATTRS)], level=0))

template_source = ""
try:
from astmonkey import visitors
template_source = visitors.to_source(optimized)
except ImportError:
pass
try:
import codegen
template_source = codegen.to_source(optimized)
except ImportError:
template_source = ""

# if sys.version_info[0] < 3:
# optimized.body.insert(0, ast.ImportFrom("hamly.escape",
# [ast.alias("escape", ESCAPE),
# ast.alias("quoteattr", QUOTEATTR),
# ast.alias("soft_unicode", TO_STRING)],
# 0))
# optimized.body.insert(0, ast.ImportFrom("hamly.html", [ast.alias("write_attrs", WRITE_ATTRS)], 0))

code = compile(ast.fix_missing_locations(optimized), filename, "exec")

code = compile(ast.fix_missing_locations(optimized), filename, 'exec')
globs = {
ESCAPE: escape,
QUOTEATTR: quoteattr,
TO_STRING: soft_unicode,
WRITE_ATTRS: write_attrs
}

scope = {}
exec code in scope
main_fun = scope["main"]
concat = ''.join
exec_(code, globs, scope)
main_fun = scope[MAIN]
concat = "".join

def render(**kwargs):
output = []
Expand All @@ -51,6 +70,6 @@ def render(**kwargs):
setattr(render, "template_source", template_source)

cached = render
cache[filename] = cached
# cache[filename] = cached

return cached
28 changes: 18 additions & 10 deletions hamly/optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@

import re
import ast
import sys
import copy

from .const import OPEN_TAG, WRITE, ESCAPE, TO_STRING, WRITE_MULTI, QUOTEATTR, WRITE_ATTRS
from six import exec_

from .const import (OPEN_TAG, WRITE, ESCAPE, TO_STRING,
WRITE_MULTI, QUOTEATTR, WRITE_ATTRS, MAIN)
from .ast_utils import (make_call, make_expr, make_tuple, ast_True,
make_cond, copy_loc, scalar_to_ast, defines_functions, )
from .escape import quoteattr, escape, soft_unicode
Expand Down Expand Up @@ -127,7 +131,7 @@ def visit_Expr(self, node):

if not dynamic_args:
static_attrs_data = []
write_attrs(map(self.evaluate, static_args), static_attrs_data.append)
write_attrs([self.evaluate(x) for x in static_args], static_attrs_data.append)
block.append(self._write(''.join(static_attrs_data)))
else:
static_names = False
Expand Down Expand Up @@ -201,7 +205,7 @@ def _flush():
_flush()
result.append(item)
_flush()
return map(self.visit, result)
return [self.visit(x) for x in result]

def visit_For(self, node):
return copy_loc(ast.For(node.target, node.iter,
Expand Down Expand Up @@ -256,8 +260,8 @@ def visit_For(self, node):
iter_assign = ast.Assign([node.target], scalar_to_ast(value))
code = compile(ast.fix_missing_locations(ast.Module([iter_assign])), '', 'exec')
scope = {}
body = map(copy.deepcopy, node.body)
exec code in scope
body = [copy.deepcopy(x) for x in node.body]
exec_(code, {}, scope)
for st in body:
for name in names:
st = SubstituteVisitor(name, scalar_to_ast(scope[name])).visit(st)
Expand Down Expand Up @@ -324,7 +328,7 @@ def visit_Expr(self, node):
values = node.value.args[:]
values += impl.args.defaults[len(values) - max_args:]
for arg, value in zip(impl.args.args, values):
body = map(SubstituteVisitor(arg.id, value).visit, body)
body = [SubstituteVisitor(arg.id, value).visit(x) for x in body]
self._inline = True
return body
return node
Expand Down Expand Up @@ -403,7 +407,7 @@ def visit_Call(self, node):
and isinstance(node.args[0], ast.Call)\
and isinstance(node.args[0].func, ast.Name)\
and node.args[0].func.id == ESCAPE:
node.args = map(InterpolateStrings().visit, node.args)
node.args = [InterpolateStrings().visit(x) for x in node.args]
return node


Expand Down Expand Up @@ -437,6 +441,10 @@ def optimize(node):
names.visit(node)
names = list(set(names.names))
names.extend((WRITE, WRITE_MULTI))
arguments = ast.arguments(args=[ast.Name(name, ast.Param()) for name in names], vararg=None,
kwarg="__kw", defaults=[])
return ast.Module([ast.FunctionDef(name="main", args=arguments, body=node.body, decorator_list=[])])
if sys.version_info[0] < 3:
arguments = ast.arguments(args=[ast.Name(name, ast.Param()) for name in names], vararg=None,
kwarg="__kw", defaults=[])
else:
arguments = ast.arguments([ast.arg(name, None) for name in names],
None, None, [], '__kw', None, [], [])
return ast.Module([ast.FunctionDef(name=MAIN, args=arguments, body=node.body, decorator_list=[])])
10 changes: 8 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
# -*- coding: utf-8 -*-

import sys
from setuptools import setup, Extension

if sys.version_info[0] < 3:
requires = ['pyparsing==1.5.7', 'pydot', 'astmonkey', 'six']
else:
requires = ['codegen', 'six']

setup(
name='hamly',
description='fast haml for python',
keywords='web template haml',
version='0.1',
version='0.1.1',
author='Victor Kotseruba',
author_email='[email protected]',
license='MIT',
packages=['hamly'],
install_requires=['pyparsing==1.5.7', 'pydot', 'astmonkey'],
install_requires=requires,
include_package_data=True,
zip_safe=False,
ext_modules=[Extension('hamly.escape_fast', ['hamly/escape_fast.c'])]
Expand Down

0 comments on commit ef20bd0

Please sign in to comment.