Skip to main content

A python-inspired templating language.

DEMO

import app.url
import request
from plywood.plugin import compress
# looks like python so far?

doctype(5)  # or doctype('strict') doctype('xhtml'), etc.
html:  # this'll start looking a lot like jade, but with quotes and colons
  # even though 'html' is a function call, the parentheses are optional.
  head:
    meta(charset="utf-8")
    title:
      if self.title:  # context variables are available on 'self'
        # docstrings are stripped of preceding whitespace and the first and
        # last newline is removed.
        """
        {{self.title}} |
        """  # string interpolation uses plywood in 'inline' mode.  Each line
             # will be joined with a space.
      'Welcome'  # string literals require quotes
    compress('css'):
      # passing values to tag attributes are escaped (html-entitized) automatically
      # if you want to escape using xml, pass {'format': 'xml'} in your options.
      link(rel='stylesheet', type='text/css', href=url.static('css/reset.css'))
      link(rel='stylesheet', type='text/css', href=url.static('css/welcome.css'))
    script(src="//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js", type="text/javascript")
    compress('js'):
      script(src=url.static("js/underscore.js"), type="text/javascript")
      script(src=url.static("js/backbone.js"), type="text/javascript")
    ieif 'lt IE 9':
      script(src="//html5shiv.googlecode.com/svn/trunk/html5.js", type="text/javascript")
      link(rel='stylesheet', type='text/css', href=url.static('css/ie.css'))
    # blocks? block inheritance?  of course!
    block('extra_head')
  body:
    div(class="wrapper", id="main-header")
    div.wrapper(id="main-header"):  # class shorthand - only HTML plugins have this trick

    # I struggled long and hard on what to do about the #id shorthands.
    # in the end, I couldn't in good conscience call this a "python
    # inspired" language if '#' was not the comment delimiter.  So the id
    # shorthand is "@" instead:
    div.wrapper@main-header:  # and yes, that '-' is part of the
         # 'main-header' token.  to support xml/html tag and attribute name,
         # I had to allow ':' and '-' in variable name.

      # for xml usage, the token parsing will accept some gnarly-looking elements in
      # argument lists, and this uses the html-plugin constructor, so that
      # you don't have to create a bunch of plugins for your XML documents.
      # (you still need commas between)
      <book xmlns='urn:loc.gov:books',
           xmlns:isbn='urn:ISBN:0-395-36341-6'>:
          <isbn:number>: 1568491379
      header:
        block('header'):
          # inlining is easy
          p(class="logo"): 'logo'
          # more complicated inlining
          p: a(href=url.reverse("login")): 'Login'
          block('header_title'):
            if self.user:
              'Welcome, '{self.user.name}'
            else:
              'Welcome'
        if not self.user:
          p(class="login"):
            a(href=url.reverse("login")): 'Log In'
            a(href=url.reverse("logout")): 'Log Out'
      nav:
        ul:
          block('nav'):

      section(class="breadcrumb"):
        block('breadcrumb')

      section(class="main"):
        block('messages'):
          if messages:
            ul(class="messages"):
              for message in self.messages:
                li(class=message.tags):  message
        script:
          # code literals, so that savvy editors can color the source code
          '''javascript
          $(document).ready(function(){
            $("ul.messages").addClass("animate");

            var fade_out = _(function() {
              this.addClass("fade-out")
            }).bind($("ul.messages"))

            setTimeout(fade_out, 5000);
            $("ul.messages").bind("click", fade_out);
          });
          '''
        block('content')

      footer:
        p:
          '&copy;{now(%Y)} colinta'

INSTALLATION

$ pip install plywood
$ ply < in.ply > out.html

SYNTAX

Each line starts with a statement, which can either be a function (div, block) a literal (', '''), or a control statement (if, else, for).

Functions get called with the arguments and a “block”:

# arguments are ((), {}), block is Block()
p
# arguments are ((), {'class': 'divvy'}), block is Block()
div(class="divvy")
# arguments are (('autofocus'), {'id': 'bio'}), block is Block(Literal('This is my bio'),)
textarea("autofocus", id="bio"): 'This is my bio'

Even if there is no “block”, you’ll get at the least at empty block object that you can call block.get_value() on. It will be “falsey”, though, so you can check for the existence of a block. The minimum “truthy” block is an empty string. That means div: '' will have a “truthy” block, but div will have a “falsey” block.

You can extend the crap out of plywood, because div, if, block, the whole lot, are all written as plywood extensions. Without the builtin extensions, the language couldn’t actually do anything, because it is at its core just a language grammar.

WHY!?!?

The main reason: I envisioned an HTML templating language that had python-like syntax, and the options that are out there now (Haml, Coffekup, Jade) don’t hit the mark.

Plain-Jane HTML? Sure, if you want. That is, I think, the best alternative to plywood! For that, use Jinja2.

The template languages that take an HTML-agnostic view (jinja2, django) is HTML made nastier by inserting additional markup. I looked at Jade and Haml as “yeah, you’re getting there”, but they didn’t nail it. Plus, have you tried writing extensions for those systems? Ooof. Nasty stuff. Writing a plugin for plywood is much easier, and since you can take some part in the parsing and runtime process, you can write some pretty hefty plugins!

I’m unapologettically a DIY-er. I think that sometimes wheels just need re-inventing!

LICENSE

Author:

Colin Thomas-Arnold

Copyright:

2012 Colin Thomas-Arnold <http://colinta.com/>

Copyright (c) 2012, Colin Thomas-Arnold All rights reserved.

See LICENSE for more details (it’s a simplified BSD license).

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

plywood-v1.2.0.tar.gz (33.9 kB view details)

Uploaded Source

File details

Details for the file plywood-v1.2.0.tar.gz.

File metadata

  • Download URL: plywood-v1.2.0.tar.gz
  • Upload date:
  • Size: 33.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No

File hashes

Hashes for plywood-v1.2.0.tar.gz
Algorithm Hash digest
SHA256 a7fbf0c98e9d66527f8d0b0fae8b529779703cdd6683fe6d901666827411290b
MD5 560bb84d70a588d2106fe15a161cc6cc
BLAKE2b-256 fc3ad4e93d001be4c691ef456ac5849288a0a5fde72b919d92a6ccda899b4df6

See more details on using hashes here.

Release history Release notifications | RSS feed

2.1.0

1 file

2.0.1

1 file

2.0.0

1 file

1.11.0

1 file

1.10.0

1 file

1.6.3

1 file

1.6.1

1 file

1.5.5

1 file

1.5.4

1 file

1.5.3

1 file

1.5.2

1 file

1.5.1

1 file

1.5.0

1 file

1.4.0

1 file

1.3.0

1 file

1.2.4

1 file

1.2.3

1 file

1.2.2

1 file

This release

v1.2.0 This release

1 file

v1.1.2

1 file

v0.0.3

1 file

v0.0.2

1 file

v0.0.1

1 file

v0.0.0

1 file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page