Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
language: python
python:
- "2.6"
- "2.7"
- "3.3"
- "3.4"
- "3.5"
- "pypy"

install: "pip install -e .[test]"
script: py.test
4 changes: 2 additions & 2 deletions examples/app1.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from werkzeug.wrappers import Request, Response

from katla.renderer import HTML5Renderer
from katla.renderer import HTMLRenderer

from app1_root import Root

Expand All @@ -18,7 +18,7 @@ def __init__(self):
self.root = Root()

def dispatch_request(self, request):
res = HTML5Renderer().render(self.root)
res = HTMLRenderer().render(self.root)
return Response(res.to_html_string(), mimetype='text/html')

def wsgi_app(self, environ, start_response):
Expand Down
146 changes: 139 additions & 7 deletions katla/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from lxml import etree

from . import attributes
from . import transforms
from .tag import tag


Expand All @@ -25,8 +25,8 @@ def to_html_string(self):
doctype=None)


base_attributes = {'style': attributes.style,
'className' : attributes.className}
base_transforms = {'style': transforms.style,
'className' : transforms.className}


class BaseRenderer(object):
Expand All @@ -37,8 +37,140 @@ def render(self, comp):
return RenderedTree(tree)


class HTML5Renderer(BaseRenderer):
class HTMLRenderer(BaseRenderer):

# div = partial(etree.Element, 'div')
span = tag('span', base_attributes)
div = tag('div', base_attributes)
# From list @ https://developer.mozilla.org/fr/docs/Web/HTML/Element

html = tag('html')

# Meta
base = tag('base', base_transforms)
head = tag('head', base_transforms)
link = tag('link', base_transforms)
meta = tag('meta', base_transforms)
style = tag('style', base_transforms)
title = tag('title', base_transforms)

# Content
address = tag('address', base_transforms)
article = tag('article', base_transforms)
body = tag('body', base_transforms)
footer = tag('footer', base_transforms)
header = tag('header', base_transforms)
h1 = tag('h1', base_transforms)
h2 = tag('h2', base_transforms)
h3 = tag('h3', base_transforms)
h4 = tag('h4', base_transforms)
h5 = tag('h5', base_transforms)
h6 = tag('h6', base_transforms)
hgroup = tag('hgroup', base_transforms)
nav = tag('nav', base_transforms)
section = tag('section', base_transforms)

# Text content
blockquote = tag('blockquote', base_transforms)
dd = tag('dd', base_transforms)
div = tag('div', base_transforms)
dl = tag('dl', base_transforms)
dt = tag('dt', base_transforms)
figcaption = tag('figcaption', base_transforms)
figure = tag('figure', base_transforms)
hr = tag('hr', base_transforms)
li = tag('li', base_transforms)
main = tag('main', base_transforms)
ol = tag('ol', base_transforms)
p = tag('p', base_transforms)
pre = tag('pre', base_transforms)
ul = tag('ul', base_transforms)

# Text semantics
a = tag('a', base_transforms)
abbr = tag('abbr', base_transforms)
b = tag('b', base_transforms)
bdi = tag('bdi', base_transforms)
bdo = tag('bdo', base_transforms)
br = tag('br', base_transforms)
cite = tag('cite', base_transforms)
code = tag('code', base_transforms)
data = tag('data', base_transforms)
dfn = tag('dfn', base_transforms)
em = tag('em', base_transforms)
i = tag('i', base_transforms)
kbd = tag('kbd', base_transforms)
mark = tag('mark', base_transforms)
q = tag('q', base_transforms)
rp = tag('rp', base_transforms)
rt = tag('rt', base_transforms)
ruby = tag('ruby', base_transforms)
s = tag('s', base_transforms)
samp = tag('samp', base_transforms)
small = tag('small', base_transforms)
span = tag('span', base_transforms)
strong = tag('strong', base_transforms)
sub = tag('sub', base_transforms)
sup = tag('sup', base_transforms)
time = tag('time', base_transforms)
u = tag('u', base_transforms)
var = tag('var', base_transforms)
wbr = tag('wbr', base_transforms)

# Image and multimedia
area = tag('area', base_transforms)
audio = tag('audio', base_transforms)
img = tag('img', base_transforms)
map_ = tag('map', base_transforms)
track = tag('track', base_transforms)
video = tag('video', base_transforms)

# Embedded content
embed = tag('embed', base_transforms)
iframe = tag('iframe', base_transforms)
object = tag('object', base_transforms)
param = tag('param', base_transforms)
source = tag('source', base_transforms)

# Scripts
canvas = tag('canvas', base_transforms)
script = tag('script', base_transforms)
noscript = tag('noscript', base_transforms)

# Edition
del_ = tag('del', base_transforms)
ins = tag('ins', base_transforms)

# Tables
caption = tag('caption', base_transforms)
col = tag('col', base_transforms)
colgroup = tag('colgroup', base_transforms)
table = tag('table', base_transforms)
tbody = tag('tbody', base_transforms)
td = tag('td', base_transforms)
tfoot = tag('tfoot', base_transforms)
th = tag('th', base_transforms)
thead = tag('thead', base_transforms)
tr = tag('tr', base_transforms)

# Forms
button = tag('button', base_transforms)
datalist = tag('datalist', base_transforms)
colgroup = tag('colgroup', base_transforms)
fieldset = tag('fieldset', base_transforms)
form = tag('form', base_transforms)
input_ = tag('input', base_transforms)
keygen = tag('keygen', base_transforms)
label = tag('label', base_transforms)
legend = tag('legend', base_transforms)
meter = tag('meter', base_transforms)
optgroup = tag('optgroup', base_transforms)
option = tag('option', base_transforms)
output = tag('output', base_transforms)
progress = tag('progress', base_transforms)
select = tag('select', base_transforms)
textarea = tag('textarea', base_transforms)

# Interactive
details = tag('details', base_transforms)
dialog = tag('dialog', base_transforms)
menu = tag('menu', base_transforms)
menuitem = tag('menuitem', base_transforms)
summary = tag('summary', base_transforms)
24 changes: 24 additions & 0 deletions katla/styles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
"""
katla
:copyright: (c) 2016 by Yann Gravrand.
:license: BSD, see LICENSE for more details.
"""


def from_string(s):
"""Returns a dictionary of styles from a styles string
:param s: Input string, eg. "display:none; text-align: right"
:return: A dictionary of styles {declaration: value}
"""
declarations = [decl.strip().split(':') for decl in s.split(';')]
return {name.strip(): value.strip() for (name, value) in declarations}


def to_string(d):
"""Returns a string from a dictionary of styles
:param s: Styles dictionary, eg. {"display": "none", "text-align": "right"}
:return: A styles string
"""
return '; '.join(['%s: %s' %
(name, value) for name, value in d.items()])
26 changes: 23 additions & 3 deletions katla/tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,35 @@
:copyright: (c) 2016 by Yann Gravrand.
:license: BSD, see LICENSE for more details.
"""

from lxml import etree

from component import Component
from .component import Component


# Python 3 handling
try:
unicode = unicode
except NameError:
str, unicode, bytes, basestring = str, str, bytes, (str, bytes)
else:
str, unicode, bytes, basestring = str, unicode, str, basestring


class Tag(object):
"""``Tag`` is the base class to render HTML tags"""

def __init__(self, name, attrs_transforms):
"""Init a tag.
:param name: Name of the tag to use in output
:param attrs_transforms: either:
- a dictionary of {``attribute_name``:``attribute_transform``},
each ``attribute_transform`` being a function:
:param name: Original attribute name
:param value: Original attribute value
:return: A (``name``, ``value``) tuple with the new attribute name and value
- a list of allowed attribute names
- None to apply no transform and disable restriction on attribute names
"""
self._name = name
self._attrs_transforms = None
if attrs_transforms is None:
Expand Down Expand Up @@ -44,7 +64,6 @@ def _extend(self, tree, children):
self._set_text(tree, child)
# TODO comment case
elif isinstance(child, Component):
print 'COMP CASE'
res = child.render(self._renderer)
if res is not None:
tree.append(res)
Expand All @@ -66,4 +85,5 @@ def _handle_attrs(self, attrs):


def tag(name, attrs_transforms=None):
"""Creates a ``Tag``, see ``Tag:__init__`` for documentation"""
return Tag(name, attrs_transforms)
9 changes: 6 additions & 3 deletions katla/attributes.py → katla/transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@
katla
:copyright: (c) 2016 by Yann Gravrand.
:license: BSD, see LICENSE for more details.

The ``transforms`` module defines usual transforms on attributes,
e.g. "className" --> "class"
"""

from . import styles

def className(name, val):
return 'class', val


def style(name, val):
if isinstance(val, dict):
return (name, '; '.join(['%s: %s' %
(attr_name, attr_value) for \
attr_name, attr_value in val.items()]))
return name, styles.to_string(val)
return name, val
3 changes: 1 addition & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@
packages=find_packages(),
zip_safe=True,
install_requires=(
'werkzeug',
'lxml',
),
extras_require={'test': ('pytest',)}
extras_require={'test': ('pytest', 'werkzeug')}
)
10 changes: 5 additions & 5 deletions tests/test_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

import unittest

from katla import attributes
from katla import transforms
from katla.component import Component
from katla.renderer import BaseRenderer, base_attributes
from katla.renderer import BaseRenderer, base_transforms
from katla.tag import tag

from .utils import render_tag
Expand All @@ -29,7 +29,7 @@ def render(self, r):

class Renderer(BaseRenderer):

div = tag('div', base_attributes)
div = tag('div', base_transforms)


class TestComponent(unittest.TestCase):
Expand All @@ -38,12 +38,12 @@ def test_simple(self):
a = A()
r = Renderer()
self.assertEquals(render_tag(a.render(r)),
'<div class="greetings">Hello World </div>',
b'<div class="greetings">Hello World </div>',
'Simple component rendering')

def test_composition(self):
b = B()
r = Renderer()
self.assertEquals(render_tag(b.render(r)),
'<div class="wrapper">Wrapped: <div class="greetings">Hello World </div><div class="greetings">Hello World </div></div>',
b'<div class="wrapper">Wrapped: <div class="greetings">Hello World </div><div class="greetings">Hello World </div></div>',
'Simple component rendering')
Loading