├── .gitignore ├── .travis.yml ├── CHANGELOG ├── MANIFEST.in ├── README.rst ├── lys ├── __init__.py └── tests │ └── test_lys.py ├── setup.py └── tox.ini /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | /dist/ 3 | /*.egg-info 4 | /*.egg 5 | /.eggs/ 6 | /.tox/ 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: python 3 | python: 4 | - "2.7" 5 | - "3.5" 6 | - "3.6" 7 | - "pypy" 8 | install: pip install tox-travis 9 | script: tox 10 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | 0.2 2 | 3 | - python 2 support 4 | - LysException instead of asserts 5 | - block people from doing a / b / c instead of a / (b / c) 6 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | 3 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Lys 2 | === 3 | .. image:: https://img.shields.io/pypi/v/lys.svg 4 | :target: https://pypi.python.org/pypi/lys 5 | .. image:: https://travis-ci.org/mdamien/lys.svg?branch=master 6 | :target: https://travis-ci.org/mdamien/lys 7 | 8 | *Simple HTML templating for Python* 9 | 10 | .. code:: python 11 | 12 | from lys import L 13 | 14 | print(L.body / ( 15 | L.h1 / 'What is love ?', 16 | L.ul / ( 17 | L.li / 'Something in the air', 18 | L.li / 'You can\'t catch it', 19 | L.li / ( 20 | L.a(href="https://en.wikipedia.org/wiki/Love") / 'Keep trying' 21 | ), 22 | ), 23 | )) 24 | 25 | To install, :code:`pip3 install lys` 26 | 27 | A few more tricks: 28 | 29 | .. code:: python 30 | 31 | # raw() to mark the content as already escaped 32 | from lys import raw 33 | L.p / raw('') 34 | 35 | # attributes '_' are replaced with '-' 36 | L.button(data_id="123") / 'click me' 37 | # => 38 | 39 | # shortcut to add classes and ids easily 40 | L.button('#magic-button.very-big', onclick='add_it()') / 'Magic !' 41 | 42 | # one easy way to do loops and ifs 43 | ( 44 | L.h1 / 'Welcome', 45 | (L.ul / ( 46 | 'Try one of our recipes:', 47 | (L.li / ( 48 | L.a(href=recipe.link) / recipe.name 49 | ) for recipe in recipes) 50 | ) if len(recipes) > 0 else ''), 51 | ) 52 | 53 | **Inspiration** : `pyxl `_, `React `_ 54 | -------------------------------------------------------------------------------- /lys/__init__.py: -------------------------------------------------------------------------------- 1 | """HTML templating with no shame 2 | 3 | >>> from lys import L 4 | >>> str(L.h1 / 'hello world') 5 | '

hello world

' 6 | >>> str(L.hr('.thick'))) 7 | '
' 8 | >>> str(L.button(onclick='reverse_entropy()')) 9 | '') 28 | 29 | def test_children(self): 30 | self.assertEqual(str(L.body / ( 31 | L.ul / ( 32 | L.li / 'One', 33 | None, 34 | L.li / 'Two', 35 | L.li / 'Three', 36 | '' 37 | ) 38 | )), '') 39 | 40 | def test_shortcut(self): 41 | self.assertEqual(str(L.span('.hello')), 42 | '') 43 | self.assertEqual(str(L.span('.hello.world')), 44 | '') 45 | self.assertEqual(str(L.span('#world.hello')), 46 | '') 47 | self.assertEqual(str(L.span('#what')), 48 | '') 49 | 50 | def test_double_division(self): 51 | self.assertEqual( 52 | str(L.a / L.b / L.c / L.d), 53 | '' 54 | ) 55 | self.assertEqual( 56 | str(L.a / L.b / 'C'), 57 | 'C' 58 | ) 59 | 60 | def test_pretty_print(self): 61 | self.assertEqual( 62 | render(L.a / L.b / 'C', pretty=True), 63 | '\n \n \n \n C\n \n \n \n' 64 | ) 65 | 66 | def test_raise(self): 67 | # no children for void tags 68 | with self.assertRaises(LysException): 69 | L.br / L.p 70 | 71 | # only str or raw() attributes values 72 | with self.assertRaises(LysException): 73 | L.button(data_id=123) 74 | 75 | # invalid shortcuts 76 | with self.assertRaises(LysException): 77 | L.span('.foo.hello world') 78 | with self.assertRaises(LysException): 79 | L.span(',hello') 80 | 81 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | from setuptools import setup 4 | 5 | setup( 6 | name='lys', 7 | version='1.0', 8 | description='Simple HTML templating for Python', 9 | long_description=open('README.rst').read(), 10 | url='http://github.com/mdamien/lys', 11 | author='Damien MARIÉ', 12 | author_email='damien@dam.io', 13 | test_suite='nose.collector', 14 | tests_require=['nose'], 15 | license='MIT', 16 | install_requires=["beautifulsoup4", 'lxml'] + ( 17 | ["future"] if sys.version_info < (3,) else [] 18 | ), 19 | classifiers=[ 20 | 'Development Status :: 5 - Production/Stable', 21 | 'Intended Audience :: Developers', 22 | 'Natural Language :: English', 23 | 'License :: OSI Approved :: MIT License', 24 | 'Programming Language :: Python :: 2', 25 | 'Programming Language :: Python :: 3', 26 | ], 27 | packages=['lys'], 28 | zip_safe=False 29 | ) 30 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py27,py35,py36,pypy 3 | [testenv] 4 | commands=python setup.py test 5 | --------------------------------------------------------------------------------