Plim — наиболее полный порт шаблонизатора Slim на Python

в 23:19, , рубрики: haml, python, slim, template engine, метки: , , ,

Определённо, первые реализации Haml и Slim должны были появиться на Python. Или, по крайней мере, так было бы справедливее.
Cложно представить себе более «питоничный» подход к написанию HTML-разметки, чем тот, который в своё время был предложен этими языками. Синтаксис, основанный на отступах, отсутствие закрывающих символов, общая лаконичность — не эти ли слова можно услышать из уст среднестатистического программиста, рассказывающего кому-то в первый раз о Python?

Но реальность оказалась другой и моё первое знакомство со Slim случилось в тот момент, когда в одном из своих проектов я начал использовать Chef и по этому поводу взялся изучать Ruby. С тех самых пор и до самого последнего времени я ждал, когда в мире Python появится хоть что-то, что по своему качеству приближалось бы к текущей реализации Slim. Но так и не дождавшись, принялся писать своё.

Мотивация

Для Python существует уже достаточно большое количество пакетов, реализующих Haml-подобные языков разметки. Вот лишь некоторые из них:

Так или иначе, все эти проекты уступают оригинальному Slim в степени своей проработанности:

— одни имеют врождённые недостатки синтаксиса, которые уже не исправить, так как проекты имеют сложившуюся аудиторию с большой кодовой базой;
— другие пытаются писать кросс-компиляторы для всех существующих «больших» python-шаблонизаторов, в результате чего одинаково плохо поддерживают синтаксис каждого из них;
— у всех остальных — слишком наивные реализации. Даже slimish-jinja2 (который, по идее, должен поддерживать весь синтаксис Slim) реализован весьма скромно. Обратите внимание, что для парсинга атрибутов тега используется очень наивное регулярное выражение, которое сработает неверно практически с любой python-строкой, содержащей символ "=". Это явно не тот проект, который сегодня можно внедрять в свой отлаженный процесс разработки.

Выходило, что даже при таком объёмном списке пакетов, нам не из чего выбирать, если речь заходит о production-системе. В результате такого неутешительного заключения, было решено написать собственный, production-ready порт оригинального шаблонизатора Slim.

Реализация

Plim — это наиболее полная реализация синтаксиса Slim на Python, созданная поверх шаблонного движка Mako. По своей сути, Plim — это простой препроцессор, который ничего не знает о контексте выполнения (runtime), но который умеет правильно транслировать slim-разметку в корректный mako-шаблон, который затем может быть скомпилирован в Python-модуль и закеширован движком Mako.

Под «наиболее полной реализацией» подразумевается поддержка большинства управляющих конструкций Slim. Однако, следует сразу же уточнить, что Plim — не точный порт языка! Некоторые элементы синтаксиса были убраны, другие были значительно дополнены, а в некоторые конструкции была внесена ясность и однозначность. Но, тем не менее, бОльшая часть синтаксиса не претерпела никаких изменений, поэтому при работе с Plim вы можете использовать существующие плагины для подсветки slim-синтаксиса.

Чтобы окончательно прояснить ситуацию, ниже представлен список всех внесённых изменений в синтаксис:

  1. Slim поддерживает следующие индикаторы строк — " ' ", " =' " и " ==' ". В Plim, апостроф (одинарная кавычка) была заменена на запятую:
    , value
    =, value
    ==, value
    

    Данное изменение было продиктовано тем фактом, что одинарная кавычка является допустимым символом в начале произвольного Python-выражения. Поэтому, следующие plim-выражения будут иметь неоднозначную трактовку (что всегда плохо, когда речь идёт об автоматической трансляции кода):

    / Является ли следующее выражение пустой python-строкой, 
      или оно должно трактоваться как синтаксическая ошибка, 
      связанная с незакрытой одинарной кавычкой?
    
    =''
    
    
    / Является ли следующее выражение python-строкой из одного 
      символа ('u''' - это корректное python-выражение) или оно должно 
      трактоваться как синтаксическая ошибка, связанная с незакрытой 
      тройной кавычкой юникодной строки?
    
    ='u'''
    

    В то же время, символ запятой недопустим в начале любой корректной python-строки, поэтому при его использовании мы получаем однозначную трактовку выражений:

    / Синтаксическая ошибка в runtime, связанная с некорректным 
      mako-выражением ${'} - незакрытая одинарная кавычка.
      Явная ошибка верстальщика.
    
    =,'
    
    
    / Корректная и однозначная конструкция. Генерирует mako-выражение с 
      пустой юникод-строкой, за которым следует один символ пробела.
    
    =,u''
    

  2. В отличие от Slim, Plim не поддерживает квадратные и фигурные скобки для выделения атрибутов тегов. Вы можете использовать только круглые скобки.
    Данное ограничение упрощает логику алгоритмов некоторых компонентов парсера и вносит в синтаксис некоторый уровень стандартизации, при этом абсолютно не уменьшая его функциональность:

    / Для выделения атрибутов вы можете использовать только
      круглые скобки.
    
    p(title="Link Title")
      h1 class=(item.id == 1 and 'one' or 'unknown') Title
    
      / Квадратные и фигурные скобки разрешены только как часть mako- или python-кода
      a#idx-${item.id} href=item.get_link(
                   **{'argument': 'value'}) = item.attrs['title']
    

  3. Как вы могли заметить в прошлом примере, Plim поддерживает динамические выражения
    в кратких формах тегов (#id.class). Вы также можете переносить выражения в скобках на другую строчку:

    / Внутри скобок отступы не имеют значения
    
    #m${
            c.message.id
    }.msg${
        (
            "test"
        )
    } = do_something_real(
    	'with', 'the', 
    	real, 
    arguments)
    

  4. В Plim, все теги должны использовать нижний регистр букв. Это ограничение было введено для того, чтобы иметь возможность включить в язык поддержку «неявных» литеральных строк, о которых — в следующем пункте списка. Но вы можете догадаться что это такое по следующему примеру:
    doctype 5
    html
      head
        title Page Title
      body
        p
          | Привет! Я - явный текстовый контент, который не нужно парсить.
        p
          Привет! Я - неявный текстовый контент, и меня тоже не нужно парсить.
    

  5. В отличие от Slim, Plim имеет поддержку неявных текстовых блоков. Текстовый блок будет считаться «неявным литералом», если его первая строка начинается с одной из следующих последовательностей символов:
    • заглавная латинская буква;
    • любая буква из диапазона, не входящего в таблицу ASCII-символов;
    • любая цифра, перед которой не символов "+" или "-", которые являются зарезервированными;
    • любой закодированный в &-форму HTML-символ. Например —  
    • mako-выражение, начинающееся с последовательности символов "${";
    • открывающая квадратная скобка "[";
    • открывающая круглая скобка "(";
    • любой юникод-символ, не входящий в диапазон кодов U0021 — U007E (т.е. вне диапазона ASCII 33 — 126, который является зарезервированным парсером).

    Вот пример применения неявных текстовых блоков:

    p
      | pipe is the explicit literal indicator. It is required if your line starts with
        the non-literal character.
    
    p
      I'm the implicit literal, because my first letter is in uppercase.
    
    p
      1. Digits
      2. are
      3. the
      4. implicit
      5. literals
      6. too.
    
    p
      ${raw_mako_expression} indicates the implicit literal line.
    
    p
      If subsequent lines do not start with implicit literal indicator,
        you must indent them
      | or you can use the "explicit" pipe.
    
    p
      если ваш блок текста написан на русском, или любом другом языке, не
      использующим символы из ASCII-диапазона, то вам даже не обязательно
      использовать заглавную букву в начале блока.
    

  6. Вам не нужно использовать символ "|" (пайп — индикатор явного литерала) в тегах style и script;
  7. Plim не делает различий между управляющими конструкциями и фильтрами.
    Например, в Slim вы должны писать "-if", "-for" и так далее — для любой управляющей конструкции, но «coffee:» (без минуса в начале, но с двуеточием в конце) — для любого из доступных фильтров. В Plim, вы используете один и тот же синтаксис: "-if", "-for" и "-coffee".
  8. Plim поддерживает HTML-теги! Вам следует только не забывать закрывать их. Внутри тегов работают те же правила парсинга, что и для «чистой» plim-разметки. Эта функциональность бывает особенно полезной, когда вы пытаетесь реализовать что-то похожее на следующий пример:
    - if edit_profile
      / обернуть весь интерфейс в редактируемый блок
      <div id="edit-profile">
    
        - include new_or_edit_interface.html
    
    - if edit_profile
      / не забываем закрыть тег
      </div>
    

    Вследствие наличия поддержки HTML-тегов, в Plim не был реализован эквивалент инструкции "/!", с помощью которой Slim генерировал HTML-комментарии. Решение о бесполезности такой конструкции в Plim было принято по причине того, что HTML-комментарии на сегодняшний день используются практически только для conditional-хаков Internet Explorer. Такая незначительная область применения не оправдывает внедрения дополнительных синтаксичечских конструкций в язык разметки, который имеет встроенную поддерживает «нативных» HTML-комментариев.

Как видите, изменения вносились максимально бережно по отношению к оригинальному языку и только в тех местах, где как я считал, присутствовала ненужная «строгость» правил.

Бонусы

Помимо основного синтаксиса, в Plim реализованы:

  • Встроенная поддержка CoffeeScript, SCSS/SASS, Markdown и reStructuredText;
  • поддержка операторов -unless и -until;
  • поддержка булевских атрибутов в виде attr=(bool_dynamic_expr)?;
  • краткие формы для всех mako-тегов и управляющих конструкций.

Мета-информация

Подробную и актуальную документацию по всем управляющим конструкциям языка вы можете найти на платформе ReadTheDocs.org (английская версия, есть pdf и epub).

Все предложения и сообщения об ошибках приветствуются в трекере на GitHub.

P.S. Текущая версия тестировалась только на ветке 2.7. Я не уверен (нет необходимого окружения для тестов), но всё должно прекрасно работать и в 2.6. А вот 3.x на данный момент не поддерживается точно. Но это связано лишь с тем, что некоторые пакеты из стандартной библиотеки в Py3K сменили своё местоположение.

Автор: Ghostwriter

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js