RomeoGolf

Сб 30 Январь 2016

Pelican и модуль Python-Markdown

Содержание

Собственный Markdown

Как сказано в официальной документации,

Markdown — это две вещи: (1) синтаксис форматирования простого текста, и (2) программный инструмент, написанный на Perl, который преобразует форматирование простого текста в HTML.

Однако, существует уже масса реализаций, не только на Perl. Для пеликана нас интересует, разумеется, модуль для Python. Информацию о нем можно получить из официальной документации, на сегодняшний день свежая версия — 2.6.5.

Дальнейший текст существенной частью является вольным частичным переводом упомянутой документации, примеры взяты непосредственно из нее же. Кое-что при переводе было пропущено, так как имеет весьма слабое касательство к Пеликану, зато добавлены некоторые примечания от себя, а также некоторые примеры даны не только в виде результирующего HTML-кода, но и в виде того, что, собственно, будет видно в итоге.

Особенности

В дополнение к базовому синтаксису, Python-Markdown поддерживает следующие особенности:

  • Интернациональный ввод

    Python-Markdown позволяет вводить информацию на любых языках, поддерживаемый Unicode, включая двунаправленный текст. Тестовый комплекс включает документы, написанные на русском и арабском.

  • Расширения

    Представлены различные расширения (включая extra) для изменения и/или расширения базового синтаксиса. Кроме того, доступен открытый Extension API для написания ваших собственных расширений.

  • Выходные форматы

    Python-Markdown может выводить документы в HTML4, XHTML и HTML5.

  • Интерфейс командной строки

    Кроме библиотеки Python, для вашего удобства доступен сценарий командной строки.

Отличия

Несмотря на то, что Python-Markdown пытается полностью реализовать markdown, как описано в правилах синтаксиса, правила можно интерпретировать неоднозначно, и разные реализации порой отличаются своим поведением. Известные и намеренные отличия Python-Markdown:

  • Выделение в середине слова

    Python-Markdown по умолчанию игнорирует выделение в середине слова. Другими словами, some_long_filename.txt не станет some<em>long</em>filename.txt. Это может быть при желании отключено.

  • Отступы/размер табуляции

    Синтаксические правила ясно утверждают, что «если пункт списка состоит из нескольких параграфов, каждый последующий параграф должен иметь отступ или 4 пробела, или один таб» (выделение добавлено). Однако, многие реализации не навязывают это правило и позволяют менее 4 пробелов отступа. Реализация Python-Markdown рассматривает несоблюдение этого правила, как ошибку.

    В случае, если некто предпочитает другое поведение, длину табуляции можно установить такого размера, какого пожелаете. Однако, учтите, что это повлияет на все аспекты синтаксиса (включая корневой уровень блоков кода).

  • Последовательные списки

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

Про установку модуля, наверное, неинтересно читать. Писать уж точно не интересно, она ничем не отличается от любой другой и уже не раз описана. А вот про применение с учетом особенностей и отличий рассказать стоит.

Про использование модуля в программах на языке Python переводить документацию тоже не вполне уместно. Pelican достаточно умная птица, чтобы избавить пользователя от копания в тонкостях программирования, позволяя ограничиться настройкой поведения. Грубо говоря, модуль предоставляет пару-тройку функций, например,

markdown.markdown (text [, **kwargs])

Этой функции надо обязательно передать текст для обработки и можно добавить дополнительные параметры, типа используемых расширений вместе с их конфигурацией. Эту функцию Pelican сам где-то и вызывает, не наше дело. А вот настроить ее поведение — наше дело. Для этого надо иметь представление о расширениях.

Расширения

Python-Markdown предлагает гибкий механизм расширений, который делает возможным изменение и/или расширение поведения парсера без необходимости редактирования его файлов исходного кода.

Официально поддерживаемые расширения

Перечисленные ниже расширения включены (как минимум) в большинство последних релизов и официально поддерживаются Python-Markdown. Если у вас типичная установка Python-Markdown, эти расширения уже доступны для применения с использованием их имени из второй колонки

Расширение Имя
Extra markdown.extensions.extra
   > Abbreviations markdown.extensions.abbr
   > Attribute Lists markdown.extensions.attr_list
   > Definition Lists markdown.extensions.def_list
   > Fenced Code Blocks markdown.extensions.fenced_code
   > Footnotes markdown.extensions.footnotes
   > Tables markdown.extensions.tables
   > Smart Strong markdown.extensions.smart_strong
Admonition markdown.extensions.admonition
CodeHilite markdown.extensions.codehilite
HeaderId markdown.extensions.headerid
Meta-Data markdown.extensions.meta
New Line to Break markdown.extensions.nl2br
Sane Lists markdown.extensions.sane_lists
SmartyPants markdown.extensions.smarty
Table of Contents markdown.extensions.toc
WikiLinks markdown.extensions.wikilinks

Существуют также расширения, которые разрабатывают и публикуют различные организации и одиночные разработчики. Для вашего удобства их список поддерживается на wiki. Команда Python-Markdown не предлагает официальной поддержки таких расширений, в случае чего следует обращаться к их разработчикам.

Extra

Подборка различных расширений, которые (главным образом) имитируют одноименное расширение для PHP Markdown. Поддерживаемые расширения:

  • Abbreviations
  • Attribute Lists
  • Definition Lists
  • Fenced Code Blocks
  • Footnotes
  • Tables
  • Smart Strong

Markdown внутри блоков HTML

В отличие от других особенностей Extra, эта особенность встроена в ядро markdown и включается, когда задействуют markdown.extensions.extra.

Содержимое любых блочных элементов HTML-кода может быть отформатировано при помощи Markdown простым добавлением атрибута markdown в открывающем теге. Этот атрибут будет удален из результирующего текста, но все остальные атрибуты будут сохранены.

Если значение атрибута markdown установлено в «1» (что рекомендуется) или другое значение за исключением «span» или «block», будет выполняться поведение по умолчанию: элементы p, h[1-6], li, dd, dt, td, th, legend и address пропускают анализ блока, в то время как остальные — нет. Если поведение по умолчанию перекрыто значением span, анализ блока будет пропущен независимо от тега. Если поведение по умолчанию перекрыто значением block, анализ блока будет выполняться независимо от тега. Простой пример:

This is *true* markdown text.

<div markdown="1">
This is *true* markdown text.
</div>

будет преобразован в такое:

<p>This is <em>true</em> markdown text.</p>
<div>
<p>This is <em>true</em> markdown text.</p>
</div>

Вложенный Markdown внутри блоков HTML

Вложенные элементы более чувствительны и должны использоваться с осторожностью. Чтобы избежать неожиданных результатов:

  • Вкладывайте элементы только внутри блочных элементов.
  • После закрывающих тегов внутренних элементов ставьте пустую строку.
  • Не делайте более одного уровня вложенности.

Сложный пример:

<div markdown="1" name="Example">

The text of the `Example` element.

<div markdown="1" name="DefaultBlockMode">
This text gets wrapped in `p` tags.
</div>

The tail of the `DefaultBlockMode` subelement.

<p markdown="1" name="DefaultSpanMode">
This text *is not* wrapped in additional `p` tags.
</p>

The tail of the `DefaultSpanMode` subelement.

<div markdown="span" name="SpanModeOverride">
This `div` block is not wrapped in paragraph tags.
Note: Subelements are not required to have tail text.
</div>

<p markdown="block" name="BlockModeOverride">
This `p` block *is* foolishly wrapped in further paragraph tags.
</p>

The tail of the `BlockModeOverride` subelement.

<div name="RawHtml">
Raw HTML blocks may also be nested.
</div>

</div>

This text is after the markdown in HTML.

превратится в

<div name="Example">
<p>The text of the <code>Example</code> element.</p>
<div name="DefaultBlockMode">
<p>This text gets wrapped in <code>p</code> tags.</p>
</div>
<p>The tail of the <code>DefaultBlockMode</code> subelement.</p>
<p name="DefaultSpanMode">
This text <em>is not</em> wrapped in additional <code>p</code> tags.</p>
<p>The tail of the <code>DefaultSpanMode</code> subelement.</p>
<div name="SpanModeOverride">
This <code>div</code> block is not wrapped in paragraph tags.
Note: Subelements are not required to have tail text.</div>
<p name="BlockModeOverride">
<p>This <code>p</code> block <em>is</em> foolishly wrapped in further paragraph tags.</p>
</p>
<p>The tail of the <code>BlockModeOverride</code> subelement.</p>
<div name="RawHtml">
Raw HTML blocks may also be nested.
</div>

</div>
<p>This text is after the markdown in HTML.</p>

На самом деле после оновления Python до версии 3.7 и соответственного обновления модулей я обнаружил, что markdown внутри тегов <div> обрабатываются из рук вон плохо. Причем, совершенно не смог понять закономерность, когда разметка работает, а когда — нет.

Abbreviations

Добавляет возможность указания аббревиатур. А именно: любая добавленная аббревиатура будет заключена в тег <abbr>. Расширение включено в стандартную библиотеку Markdown.

The HTML specification 
is maintained by the W3C.

*[HTML]: Hyper Text Markup Language
*[W3C]:  World Wide Web Consortium

The HTML specification is maintained by the W3C.


Благодаря этому примеру во всем тексте статьи буквосочетание HTML будет иметь вид аббревиатуры. Так что не удивляйтесь, чего это вдруг автор решил пояснить эти буквы — это не я, это пример из официальной документации шалит, но это жертва, на которую я намерен пойти.

Расширение не требует каких-либо специальных опций настройки.

Attribute Lists

Добавляет синтаксис для задания атрибутов различных HTML-элементов на выходе обработки Markdown. Расширение включено в стандартную библиотеку Markdown.

Примерный список атрибутов выглядит примерно так:

{: #someid .someclass somekey='some value' }

Слово, начинающееся с решетки #, будет установлено в качестве id элемента.

Слово, начинающееся с точки ., будет добавлено в список классов, определенных для элемента.

Пара «ключ/значение» (somekey='some value') будет установлена для элемента.

Имейте в виду, что, хотя слово с точкой добавляет класс, использование пары ключ/значение будет всегда перекрывать ранее определенный атрибут. Рассмотрите следующее:

{: #id1 .class1 id=id2 class="class2 class3" .class4 }

Этот пример приведет в результате к установке таких атрибутов:

id="id2" class="class2 class3 class4"
Блочные элементы

Чтобы определить атрибуты для блочного элемента, список атрибутов необходимо разместить на последней строке данного блока.

This is a paragraph.
{: #an_id .a_class }

приведет к следующему результату:

<p id="an_id" class="a_class">This is a paragraph.</p>

Единственное исключение — заголовки. Для них допускается располагать список в той же строке.

A setext style header {: #setext}
=================================

### A hash style header ### {: #hash }

приведет к следующему:

<h1 id="setext">A setext style header</h1>
<h3 id="hash">A hash style header</h3>
Строчные элементы

Чтобы определить атрибуты для строчных элементов, список атрибутов должен быть расположен непосредственно после такого элемента без пробельных символов

[link](http://example.com){: class="foo bar" title="Some title!" }

приведет к следующему:

<p><a href="http://example.com" class="foo bar" title="Some title!">link</a></p>

Расширение не требует особых опций настройки.

Definition Lists

Расширение добавляет возможность создавать список определений в документе Markdown. Включено в стандартную библиотеку Markdown.

Apple
:   Pomaceous fruit of plants of the genus Malus in 
    the family Rosaceae.

Orange
:   The fruit of an evergreen tree of the genus Citrus.

будет обработано так:

<dl>
<dt>Apple</dt>
<dd>Pomaceous fruit of plants of the genus Malus in 
the family Rosaceae.</dd>

<dt>Orange</dt>
<dd>The fruit of an evergreen tree of the genus Citrus.</dd>
</dl>

Расширение не требует особых опций настройки.

Fenced Code Blocks

Расширение добавляет дополнительный способ указания блоков кода, который преодолевает некоторые ограничения для блоков кода, заданных отступами.

This is a paragraph introducing:

~~~~~~~~~~~~~~~~~~~~
a one-line code block
~~~~~~~~~~~~~~~~~~~~

Заборчатый блок кода может иметь пустую строку в качестве первой или последней строки кода, а также может следовать немедленно после пункта списка, не становясь частью списка.

Внимание:
Заборчатые блоки кода поддерживаются только на уровне корня документа. Таким образом, их нельзя вложить в списки или цитаты

Можно определить язык в блоке кода для использования при подсветке синтаксиса (или еще зачем-нибудь). Язык будет добавлен атрибутом класса выходному элементу <code>. То есть, вам следует определить язык, какой вам надо, в CSS-классе .language. Для совместимости с синтаксисом markdown, язык можно опционально заключить в фигурные скобки:

~~~~{.python}
# python code
~~~~

~~~~.html
<p>HTML Document</p>
~~~~

и из этого получится

<pre><code class="python"># python code
</code></pre>

<pre><code class="html">&lt;p&gt;HTML Document&lt;/p&gt;
</code></pre>

Кроме того, поддерживается синтаксис в стиле GitHub с использованием обратных апострофов:

```python
# more python code
```

Если вам бы хотелось подсветить заборчатые блоки кода с помощью расширения CodeHilite, просто подключите это расширение (помните, что его зависимостью является модуль Pygments), и синтаксис языка в блоке кода будет подсвечиваться соответствующим образом.

Подобно синтаксису с двоеточиями расширения CodeHilite, заборчатые блоки кода могут выделять определенные строки. Строки можно указать в стиле PHP Extra

~~~~{.python hl_lines="1 3"}
# This line is emphasized
# This line isn't
# This line is emphasized
~~~~

или в стиле GitHub

```python hl_lines="1 3"
# This line is emphasized
# This line isn't
# This line is emphasized
```

Расширение не требует особых опций настройки.

Footnotes

Добавляет синтаксис для указания сносок в документах Markdown. Расширение включено в стандартную библиотеку Markdown.

Синтаксис сносок Python-Markdown в основном следует правилам, принятым в сообществе Markdown в целом и почти в точности соответствует реализации PHP Markdown Extra. Отличия касаются только некоторых тонкостей на выходе.

Пример:

Footnotes[^1] have a label[^@#$%] and the footnote's content.

[^1]: This is a footnote content.
[^@#$%]: A footnote on the label: "@#$%".

Метка сноски должна начинаться со знака вставки ^ и может содержать любой строчный текст (включая пробелы) между квадратными скобками. Только первый символ знака вставки имеет специальное значение.

Контент сноски должен начинаться с метки, за которой идет двоеточие и минимум один пробел. Метка, используемая для определения контента, должна в точности соответствовать метке, используемой в теле (включая регистр символов и пробелы). Контент может содержать несколько строк, абзацев, блоков кода и большинство других элементов синтаксиса Markdown. Дополнительные строки должны иметь отступ на один уровень (четыре пробела или один таб).

При работе с несколькими блоками может быть полезно начинать контент с отдельной строки для метки, указывающей на контент. Благодаря такому способу у всего блока получаются единообразные отступы, и любые ошибки автору будет существенно проще заметить.

[^1]: 
    The first paragraph of the definition.

    Paragraph two of the definition.

    > A blockquote with
    > multiple lines.

        a code block

    A final paragraph.

Для расширения доступны следующие опции настройки:

  • PLACE_MARKER: текстовая строка, используемая для отметки позиции, где будет отображаться сноска. По умолчанию ///Footnotes Go Here///.

    Если текст маркера места не найден в документе, сноска будет размещена в конце результирующего HTML-документа.

  • UNIQUE_IDS: Следует ли избегать коллизий через многократный вызов reset() По умолчанию False.

  • BACKLINK_TEXT: Текстовая строка, которая будет ссылкой от сноски обратно к позиции в документе. По умолчанию &#8617

Tables

Добавляет возможность использования синтаксиса, установленного в PHP Markdown Extra.

То есть, следующий текст (взятый из документации к упомянутой выше PHP Markdown Extra)

First Header  | Second Header
------------- | -------------
Content Cell  | Content Cell
Content Cell  | Content Cell

будет отображен в виде

<table>
  <thead>
    <tr>
      <th>First Header</th>
      <th>Second Header</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Content Cell</td>
      <td>Content Cell</td>
    </tr>
    <tr>
      <td>Content Cell</td>
      <td>Content Cell</td>
    </tr>
  </tbody>
</table>

Расширение не требует особых опций настройки.

Примечание
Для таблиц, созданных таким образом, хотелось бы иметь возможность задать стили оформления, желательно еще и разные в разных случаях. Это особенно важно потому, что блоки кода с включенной нумерацией страниц тоже используют таблицы: одна колонка для номеров, вторая — для блока кода. Фильдиперстово стилизованные таблицы без указания класса могут попортить пример кода с нумерованными строками. Таким образом, хотелось бы разделить стили таблиц при помощи классов. Однако, использование расширения Attribute Lists с таблицами работает не так, как хотелось бы.
 
Хотя таблица выглядит, как блочный элемент, она ведет себя не как блок, да еще и у нее есть подэлементы — строки и столбцы. Поэтому {: .styled_table }, написанное сразу после таблицы, будет применено не к <table>, а к <td> — пустому и специально для этой цели созданному. Мое решение (первое, что пришлов голову): втыкаю сразу перед таблицей строку <div markdown="span" class="styled_table"></div>, а в CSS-файле использую конструкции типа .styled_table + table {..}, .styled_table + table td {..}. Можно, конечно, воспользоваться и псевдоклассом :not, но мой способ позволяет сделать разное оформление таблиц при необходимости.

Smart Strong

Расширение добавляет более «умную» обработку двойных знаков подчеркивания внутри слова. Делает для двойных подчеркиваний то же, что smart_emphasis для одинарных. Расширение включено в стандартную библиотеку Markdown.

Пример использования в консоли Python:

>>> import markdown
>>> markdown.markdown('Text with double__underscore__words.', \
                      extensions=['markdown.extensions.smart_strong'])
'<p>Text with double__underscore__words.</p>'
>>> markdown.markdown('__Strong__ still works.', \
                      extensions=['markdown.extensions.smart_strong'])
'<p><strong>Strong</strong> still works.</p>'
>>> markdown.markdown('__this__works__too__.', \
                      extensions=['markdown.extensions.smart_strong'])
'<p><strong>this__works__too</strong>.</p>'

Расширение не требует особых опций настройки.

Extra — заключение

Итак, расширение Extra — это, по сути, комплект из семи расширений. Их можно подключить оптом, указав markdown.extensions.extra в строке подключения, а можно и поштучно, указывая только имена действительно необходимых. Указать отдельные расширения в настройках может быть полезно еще и для того, чтобы передать им опции настройки, отличающиеся от опций по умолчанию.

Однако, почему же в Pelican вся эта подборка работает сразу? Да еще и вместе с подсветкой синтаксиса, которая обеспечивается расширением CodeHilite, которое в Extra не входит? (Правда, для нормальной подсветки нужен еще и соответствующий CSS-файл, и если его нет, то код визуально подсвечен не будет, но ключевые слова в коде будут правильно обрамлены тегами <span> с разными классами для возможности применения разных стилей.)

А все дело в файле конфигурации Pelican — pelicanconf.py, в котором предусмотрена опция-переменная MD_EXTENSIONS: список расширений Markdown, которые будет использовать генератор сайта.

И хотя в файле конфигурации, который создается в результате pelican-quickstart.py, эта опция не указана вообще, у нее есть значение по умолчанию, которое и применяется в таких случаях:

MD_EXTENSIONS = ['codehilite(css_class=highlight)','extra']

Как видно, расширение Extra включено изначально, а CodeHilite включено еще и с настройками. При желании их можно отключить, указав явным образом MD_EXTENSIONS с пустыми квадратными скобками. А можно добавить еще какие-нибудь расширения, но тогда надо обязательно указать и те, что включены по умолчанию, потому что опция MD_EXTENSIONS их перекрывает и заменяет.

Кроме того надо заметить, что при указании имени расширения в опции оставлена только последняя составляющая. Предполагаю, это сделано потому, что в коде Pelican уже указан import markdown.extentions. Впрочем, для блоггера причины этой удобной особенности неважны, можно просто писать имя самого расширения без префиксов.

Admonition

Расширение добавляет в Markdown-документ замечания в стиле rST. Включено в стандартную библиотеку Markdown.

Замечание создается с использованием следующего синтаксиса:

!!! type "optional explicit title within double quotes"
    Any number of other indented markdown elements.

    This is the second paragraph.

type будет использоваться в имени класса CSS и в качестве заголовка по умолчанию. Это должно быть одно слово. Так, например:

!!! note
    You should note that the title will be automatically capitalized.

будет переделано так:

<div class="admonition note">
<p class="admonition-title">Note</p>
<p>You should note that the title will be automatically capitalized.</p>
</div>

Опционально вы можете добавить заголовок. Например:

!!! danger "Don't try this at home"
    ...

сделает

<div class="admonition danger">
<p class="admonition-title">Don't try this at home</p>
<p>...</p>
</div>

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

!!! important ""
    This is a admonition box without a title.

получится

<div class="admonition important">
<p>This is a admonition box without a title.</p>
</div>

rST предполагает использование следующих типов: attention, caution, danger, error, hint, important, note, tip, warning. Однако, вы вольны использовать всё, что хотите.

В состав расширения не входит никаких CSS-файлов. Придется делать их самостоятельно, можете посмотреть тему Sphinx, если вам нужно вдохновение.

CodeHilite

Расширение добавляет подсветку синтаксиса к стандартным блокам кода Python-Markdown с использованием Pygments. Расширение включено в стандартную библиотеку Markdown.

Для его использования необходимо также скачать и установить модуль Pygments. Впрочем, при установке Pelican это произошло само собой: pip поставил модуль, так как он входит в список зависимостей. Но в документации на Markdown об этом, конечно, не сказано, ведь его можно использовать не только в Pelican.

Вам надо определить подходящие классы CSS и создать подходящие правила для них — подобрать (сделать самостоятельно, раздобыть или поручить эту работу модулю Pygments) CSS-файл и подключить его в заголовке ваших HTML-шаблонов. На эту тему есть замечательная документация, где описаны детали. Если вы не указали язык, Pygments попытается догадаться самостоятельно. Если у него это не получится, блок кода останется без подсветки.

Имейте в виду:
CSS и/или JavaScript не включены в виде части данного расширения, но должны быть обеспечены пользователем. Проект Pygments предоставляет стили CSS по умолчанию, и они могут оказаться полезными поначалу.

Расширение CodeHilite следует тем же правилам синтаксиса, что и обычные блоки кода Markdown, с одним исключением. Подсвечивателю нужно знать, какой язык используется для блока кода. Есть три способа рассказать подсвечивателю, какой язык содержит блок, и каждый имеет отличающийся результат.

Имейте в виду:
Формат идентификатора языка действует на нумерацию строк только в том случае, если linenums установлено в None (по умолчанию). Если его значение True или False, то формат идентификатора не влияет на показ номера строки — он служит только для определения языка в блоке кода
  • Знак начала сценария #! (с путем)

    Если в первой строке блок кода содержит знак начала сценария, язык извлекается из этой строки и номера строк ставятся. Такой код:

    #!/usr/bin/python
    # Code goes here ...

приведет к результату:

1
2
#!/usr/bin/python
# Code goes here ...
  • Знак начала сценария #! (без пути)

    Если первая строка блока кода содержит знак начала сценария, однако не содержит путь (одиночный / или даже пробел), эта строка будет удалена из кода перед обработкой. Номера строк ставятся. Такой код:

#!python
# Code goes here ...

приведет к результату:

1
# Code goes here ...
  • Двоеточия

    Если первая строка начинается с трех или более двоеточий, то текст, следующий за двоеточиями, идентифицирует язык. Первая строка удаляется из блока кода перед обработкой, номера строк не ставятся. Такой код:

    :::python
    # Code goes here ...

приведет к результату:

# Code goes here ...

При помощи синтаксиса с двоеточиями можно выделить определенные строки. При использовании CSS-стилей Pygments у выделяемых линий фон желтый. Это полезно для привлечения внимания читателя к отдельным линиям. Такой код:

    :::python hl_lines="1 3"
    # This line is emphasized
    # This line isn't
    # This line is emphasized

приведет к результату:

# This line is emphasized
# This line isn't
# This line is emphasized
Имейте в виду:
hl_lines называется опция Pygments “highlighted lines”

CodeHilite имеет полную обратную совместимость в том плане, что если встретившийся блок кода внезапно окажется без определения языка, такой блок просто будет обернут тегами <pre> и выдан так. Такой код:

    # Code goes here ...

приведет к результату:

# Code goes here ...

а код страницы в этом месте будет выглядеть так:

<div class="codehilite"><pre><code># Code goes here ...
</code></pre></div>
Имейте в виду:
Если язык не будет определен, движок подсветки Pygments попытается догадаться сам (если только quess_lang не установлен в False). А если у него не получится, тогда будет такое поведение, как описано выше.

Для расширения доступны следующие опции настройки:

  • linenums: Использовать нумерацию строк. Возможные значения: True (да), False (нет) и None (авто). Значение по умолчанию — None.

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

    Использование False отключает нумерацию строк, даже при использовании знака начала сценария (#!) для указания языка.

  • guess_lang: Автоматическое определение языка. По умолчанию True.

    Использование False предотвращает попытки Pygments угадать язык, таким образом, будут подсвечиваться только блоки с явным указанием языка.

  • css_class: Устанавливает имя класса CSS для оборачивающего тега <div>. По умолчанию codehilite.

  • pygments_style: Стиль форматирования Pygments HTML (ColorSheme). По умолчанию default.

    Имейте в виду:
    Эта опция полезна только в том случае, если noclasses установлена в True, иначе пользователь должен обеспечить наличие CSS-стилей.
  • noclasses: Использовать встроенные стили вместо классов CSS. По умолчанию False.

  • use_pygments: По умолчанию True. Установите в False, чтобы отключить использование Pygments. Если для блока кода определен язык, он будет присвоен в качестве класса для тега <code>, как предлагается спецификацией HTML5 (альтернативный вывод не будет учитываться) и может быть использован в библиотеке JavaScript в браузере для подсветки блока кода.

HeaderId

Расширение автоматически генерирует атрибут id для элементов заголовков (h1 – h6) в результирующем HTML-документе.

Расширение включено в стандартную библиотеку Markdown.

Внимание!
Это расширение рассматривается, как не рекомендуемое. Вместо него следует использовать расширение Table of Contents, которое предлагает большинство возможностей этого расширения, а также дополнительные.

По умолчанию все заголовки автоматически получат атрибут id, сгенерированный на основе текста заголовка (ниже описано, как это отключить). Посмотрите пример, в котором все три заголовка должны бы иметь одинаковый id:

#Header
#Header
#Header

выдаст в результате

<h1 id="header">Header</h1>
<h1 id="header_1">Header</h1>
<h1 id="header_2">Header</h1>

Для расширения доступны следующие опции настройки:

  • level: Базовый уровень для заголовков.

    По умолчанию 1.

    level позволяет вам автоматически подстроить уровни заголовков для их соответствия вашим HTML-шаблонам. Например, допустим, markdown-текст на странице не должен содержать заголовков уровня выше третьего (<h3>). Ниже показано, как этого добиться:

    >>>  text = '''
    ... #Some Header
    ... ## Next Level'''
    >>> from markdown.extensions.headerid import HeaderIdExtension
    >>> html = markdown.markdown(text, extensions=[HeaderIdExtension(level=3)])
    >>> print html
    <h3 id="some_header">Some Header</h3>
    <h4 id="next_level">Next Level</h4>
    
  • forceid: Вынуждает все заголовки получить id.

    По умолчанию True.

    forceid включает или выключает автоматическую генерацию id для заголовков, которые не имеют таковых, указанных явным образом (с использованием расширения Attribute Lists).

    >>> text = '''
    ... # Some Header
    ... # Header with ID # { #foo }'''
    >>> html = markdown.markdown(text,
                      extensions=['markdown.extensions.attr_list',
                                  HeaderIdExtension(forceid=False)])
    >>> print html
    <h1>Some Header</h1>
    <h1 id="foo">Header with ID</h1>
    
  • separator: Разделитель слов. Символ, которым будут заменены пробелы в id.

    По умолчанию -.

  • slugify: Callable (вызываемый объект) для генерации якорей (anchors).

    По умолчанию markdown.extensions.headerid.slugify

    Если вы желаете использовать иной алгоритм для определения id, можете сделать свой Callable, который принимает два аргумента:

    • value: строка для преобразования в slug
    • separator: разделитель слов

slug — труднопереводимое слово. Однозначного аналога на русском не нашел. Суть примерно в том, что из некоей строки иногда требуется сделать последовательность символов, в которой можно узнать исходную строку, но при этом нужно исключить любые символы, вызывающие трудности в обработке, начиная со служебных (типа пробелов и слэшей) и заканчивая символами сложных алфавитов, той же кириллицы. Например для строки «Привет, мир!» получится slag «privet_mir», или «hello_world_». Это может быть полезно при генерации имен, которые должны удовлетворять определенным требованиям к составу символов, например, имен файлов, и чаще всего используется при формировании частей человекопонятных интернет-адресов.

Расширение HeaderId поддерживает расширение Meta-Data. Пожалуйста, посмотрите документацию для этого расширения. Поддерживаемые ключевые слова meta-data:

  • header_level
  • header_forceid

При использовании meta-data перекроет настройки, установленные интерфейсом extension_configs.

header_level: 2
header_forceid: Off

# A Header

приведет в результате к такому выводу:

<h2>A Header</h2>

Meta-Data

Расширение добавляет синтаксис для определения метаданных документа. Написано под влиянием MultiMarkdown и следует правилам его синтаксиса. В настоящее время это расширение не использует метаданных каким-либо образом, а просто предоставляет свойство Meta экземпляра класса Markdown для использования другими расширениями или напрямую в коде на Python.

Расширение включено в стандартную библиотеку Markdown.

Метаданные состоят из набора ключевых слов и значений, указанных в начале документа Markdown примерно так:

Title:   My Document
Summary: A brief description of my document.
Authors: Waylan Limberg
         John Doe
Date:    October 2, 2007
blank-value: 
base_url: http://example.com

This is the first paragraph of the document.

Ключевые слова регистрозависимы и могут состоять из букв, цифр, символов подчеркивания и точек и должны завершаться двоеточием. Значения состоят из чего угодно, следующего за двоеточием на этой же строке, и даже может быть пустым.

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

Первая пустая строка заканчивает все метаданные документа. Таким образом, первая строка документа не должна быть пустой.

С другой стороны, вы можете использовать разделители в YAML-стиле чтобы отметить начало и конец метаданных. В этом случае, первая строка документа должна быть --. Метаданные заканчиваются на первой пустой строке или первой строке, содержащей конечный разделитель (--- или ...), смотря что встретиться раньше. Хотя YAML-разделители и поддерживаются, метаданные не анализируются, как YAML.

Все метаданные удаляются из документа перед дальнейшей обработкой программой Markdown.

Метаданные доступны в виде Python-типа данных Dict в свойстве Meta экземпляра класса Markdown. Пример использования с документом, приведенным выше:

>>> md = markdown.Markdown(extensions = ['markdown.extensions.meta'])
>>> html = md.convert(text)
>>> # Meta-data has been stripped from output
>>> print html
<p>This is the first paragraph of the document.</p>

>>> # View meta-data
>>> print md.Meta
{
'title' : ['My Document'],
'summary' : ['A brief description of my document.'],
'authors' : ['Waylan Limberg', 'John Doe'],
'date' : ['October 2, 2007'],
'blank-value' : [''],
'base_url' : ['http://example.com']

Обратите внимание, что ключевые слова все в нижнем регистре, а значения состоят из списка строк, в котором каждая строка — отдельная строка значения этого ключа в исходном документе. Таким образом, можно сохранить разрывы строк, если хотите. Либо элементы списка можно объединить, где это уместно. Никаких предположений относительно данных не делается, они просто передаются свойству Meta в том виде, в каком обнаружены.

Пожалуй, метаданные можно передать в систему шаблонов, либо использовать в различных расширениях Markdown. Эти возможности оставлены фантазии разработчика.

Следующие расширения в настоящее время могут работать с расширением Meta-Data. Также перечислены поддерживаемые ключевые слова:

  • HeaderId
    • header_level
    • header_forceid
  • WikiLinks
    • wiki_base_url
    • wikw_end_url
    • wiki_html_class

New-Line-to-Break Extension

Расширение будет обрабатывать новые строки с использованием тега переноса строки, подобно тому, как это сделано в реализациях Markdown на StackOverflow и GitHub. Пример:

>>> import markdown
>>> text = &laquo;
... Line 1
... Line 2
... &laquo;
>>> html = markdown.markdown(text, extensions=['markdown.extensions.nl2br'])
>>> print html
<p>Line 1<br />
Line 2</p>

Расширение не требует особых опций настройки.

Sane Lists

Расширение изменяет поведение синтаксиса списков Markdown так, чтобы они вызывали меньше удивления, или, скажем так, были более предсказуемыми.

Расширение включено в стандартную библиотеку Markdown.

Sane Lists не разрешает использование смешанных типов списков. Иными словами, нумерованный список не будет продолжаться, если встретится пункт маркированного списка и наоборот. Например:

1. Ordered item 1
2. Ordered item 2

* Unordered item 1
* Unordered item 2

приведет к такому результату:

<ol>
  <li>Ordered item 1</li>
  <li>Ordered item 2</li>
</ol>

<ul>
  <li>Unordered item 1</li>
  <li>Unordered item 2</li>
</ul>

в то время как поведение Markdown по умолчанию сгенерировало бы маркированный список.

Странно. В документации написано Whereas the default Markdown behavior would be to generate an unordered list., а получается вовсе даже нумерованный:

1. Ordered item 1
2. Ordered item 2

* Unordered item 1
* Unordered item 2

приводит к результату:

  1. Ordered item 1
  2. Ordered item 2

  3. Unordered item 1

  4. Unordered item 2

Имейте в виду, что в отличие от поведения Markdown по умолчанию, если между пунктами строки нет пустой строки, другой тип списка игнорируется полностью. Это соответствует поведению абзацев. Например:

A Paragraph.
* Not a list item.

1. Ordered list item.
* Not a separate list item.

При подключенном расширении получится такой результат:

<p>A Paragraph.
* Not a list item.</p>

<ol>
  <li>Ordered list item.
  * Not a separate list item.</li>
</ol>

Во всех остальных случаях Sane Lists должно вести себя так же, как нормальные списки Markdown.

Расширение не требует особых опций настройки.

SmartyPants

Расширение преобразует ASCII-символы тире, кавычек и многоточия в соответствующие HTML-объекты

ASCII-символ Замена HTML-объект Заменяющие ключи
' ‘ ’ &lsquo; &rsquo; 'left-single-quote', 'right-single-quote'
" “ ” &ldquo; &rdquo; 'left-double-quote', 'right-double-quote'
<‌< >> « » &laquo; &raquo; 'left-angle-quote', 'right-angle-quote'
... &hellip; 'ellipsis'
-- &ndash; 'ndash'
--- &mdash; 'mdash'

Используя опцию настройки substitutions, вы можете перекрыть замены по умолчанию. Просто передайте отображение (mapping) словарика ключей для заменяющих строк.

Примечание
прямые кавычки — одиночная и парная — закрываются от этого расширения обратным слэшем. Закрывающие угловые скобки — тоже, причем, хоть обе, хоть только первая. Точки многоточия можно экранировать хоть каждую. А вот открывающие угловые скобки от преобразования в текст обратным слэшем не закрываются. Похоже, чтобы не заключать их в блок кода, их можно разделить символом нулевой длины &zwnj;.

Например, можно использовать следующую конфигурацию, чтобы получить правильные кавычки по-немецки:

extension_configs = {
    'markdown.extensions.smarty': {
        'substitutions': {
            'left-single-quote': '&sbquo;', # sb is not a typo!
            'right-single-quote': '&lsquo;',
            'left-double-quote': '&bdquo;',
            'right-double-quote': '&ldquo;'
        }
    }
}
Имейте в виду:
Это расширение повторно реализует Python-библиотеку SmartyPants, включая ее в анализатор Markdown. Хотя оно не предоставляет никаких дополнительных особенностей, оно предлагает несколько преимуществ. В особенности, оно не будет пытаться работать в подсвечиваемых блоках кода (при использовании CodeHilite) подобно аналогичным библиотекам третьих сторон.

Для расширения доступны следующие опции настройки:

Option Default value Description
smart_dashes True преобразовывать тире
smart_quotes True преобразовывать прямые кавычки
smart_angled_quotes False преобразовывать угловые кавычки
smart_ellipses True преобразовывать многоточия
substitutions {} перезаписывает замены по умолчанию

Расширение SmartyPants основано не изначальной реализации SmartyPants Джона Грубера. Для получения информации о деталях почитайте документацию

Table of Contents

Расширение создает содержание документа Markdown и добавляет его в результирующий HTML-документ.

Расширение включено в стандартную библиотеку Markdown.

По умолчанию все заголовки автоматически получают уникальный атрибут id, генерируемый на основе текста заголовка. Обратите внимание, в следующем примере все заголовки должны бы иметь одинаковый id:

#Header
#Header
#Header

Получится так:

<h1 id="header">Header</h1>
<h1 id="header_1">Header</h1>
<h1 id="header_2">Header</h1>

Поместите маркер в документе в том месте, где вам бы хотелось видеть содержание. Тогда вложенный список всех заголовков документа заменит этот маркер. Маркер по умолчанию [TOC], так что следующий документ

[TOC]

# Header 1

## Header 2

будет преобразован в такое:

<div class="toc">
  <ul>
    <li><a href="#header-1">Header 1</a></li>
      <ul>
        <li><a href="#header-2">Header 2</a></li>
      </ul>
  </ul>
</div>
<h1 id="header-1">Header 1</h1>
<h1 id="header-2">Header 2</h1>

Независимо от того, где в документе обнаружен маркер (или отключен), содержание доступно в свойстве toc класса Markdown. Это позволяет вставлять содержание в любое место шаблона страницы. Например:

>>> md = markdown.Markdown(extensions=['markdown.extensions.toc'])
>>> html = md.convert(text)
>>> page = render_some_template(context={'body': html, 'toc': md.toc})

Для расширения доступны следующие опции настройки:

  • marker: Текст, который будет заменен на содержание. По умолчанию [TOC].

    Установите в пустую строку, чтобы отключить поиск маркера, что может сэкономить немного времени, особенно в больших документах.

  • title: Атрибут title, который будет добавлен блоку <div> содержания. По умолчанию None.

  • anchorlink: Установите в True, чтобы все заголовки получили ссылку сами на себя. По умолчанию False.
  • permalink: Установите в True или строку, чтобы сгенерировать постоянную ссылку в конце каждого заголовка. Полезно для страниц в стиле Sphinx.

    Если установлено в True, в качестве текста ссылки будет использован символ абзаца (¶ or &para;). Если установить строку, она будет использоваться в качестве текста ссылки.

  • baselevel: Базовый уровень для заголовков. По умолчанию 1.

    Настройка baselevel позволяет автоматически подогнать уровни заголовков к иерархии шаблонов HTML. Например, предположим, что страница текста Markdown не должна содержать заголовков уровня выше 3 (<h>). Следующий пример показывает, как это сделать:

    >>>  text = '''
    ... #Some Header
    ... ## Next Level'''
    >>> from markdown.extensions.toc import TocExtension
    >>> html = markdown.markdown(text, extensions=[TocExtension(baselevel=3)])
    >>> print html
    <h3 id="some_header">Some Header</h3>
    <h4 id="next_level">Next Level</h4>'
    
  • slugify: Вызываемый объект для генерации якорей (anchors)

    По умолчанию markdown.extensions.headerid.slugify

    Если вы желаете использовать иной алгоритм для определения id, можете сделать свой Callable, который принимает два аргумента:

    • value: строка для преобразования в slug
    • separator: разделитель слов
  • separator: Разделитель слов. Символ, который заменяет пробелы в id. По умолчанию -.

Примечание
Если перед маркером [TOC] вставить заголовок содержания, оформив его в виде заголовка, например, ## Содержание, то этот заголовок будет тоже вставлен в содержание, придавая ему вполне дурацкий вид. Поэтому обозначить TOC надо как-то иначе, либо тупо выделением **Оглавление**, либо обозначив абзацу с псевдозаголовком собственный класс (при помощи Attribute Lists), для которого можно нарисовать красивое (или такое же, как у остальных заголовков) оформление в CSS-файле.

Расширение добавляет поддержку WikiLinks. А именно, любое слово [[в скобках]] будет преобразовано в ссылку.

Расширение включено в стандартную библиотеку Markdown.

Слово [[в скобках]] — это любая комбинация букв в верхнем и нижнем регистре, цифр, дефисов, подчеркиваний и пробелов, окруженных двойными квадратными скобками. То есть,

[[Bracketed]]

будет преобразовано в

<a href="/Bracketed/" class="wikilink">Bracketed</a>

Имейте в виду, что WikiLinks автоматически присваивает class="wikilink", что упрощает применение стиля WikiLinks, отличающегося от других ссылок на странице, если это желательно. Смотрите ниже, как изменить этот класс.

Кроме того, если используются пробелы, они будут преобразованы в символ подчеркивания в ссылке, но оставлены как есть в тексте ссылки. Наверное, этот пример проиллюстрирует лучше:

[[Wiki Link]]

станет

<a href="/Wiki_Link/" class="wikilink">Wiki Link</a>

Поведением по умолчанию является указание каждой ссылки на корневой каталог документа в текущем домене и закрывать слэшем. Дополнительно, каждой ссылке присваивается HTML-класс wikilink.

Для расширения доступны следующие опции настройки:

  • base_url: Строка для добавления в начале URL. По умолчанию /.
  • end_url: Строка для добавления в конце URL. По умолчанию /.
  • html_class: CSS-класс. Оставьте пустым, чтобы не использовать никакой. По умолчанию wikilink.
  • build_url: Вызываемый объект, который формирует URL из его частей.

Например, давайте предположим, что ссылки должны всегда указывать на подкаталог /wiki/ и заканчиваться на .html

>>> from markdown.extensions.wikilinks import WikiLinkExtension
>>> html = markdown.markdown(text,
...     extensions=[WikiLinkExtension(base_url='/wiki/', end_url='.html')]
... )

Вышеприведенное в результате сделает такие ссылки из [[WikiLink]]:

<a href="/wiki/WikiLink.html" class="wikilink">WikiLink</a>

Если вы хотите нечто большее, чем просто изменить начало и/или конец URL, вы можете также сделать вызываемый объект, который должен принимать три аргумента (label, base и end). Этот объект должен возвращать URL целиком.

>>> def my_url_builder(label, base, end):
...    # do stuff
...    return url
...
>>> html = markdown.markdown(text,
...     extensions=[WikiLinkExtension(build_url=my_url_builder)],
... )

Также предоставляется опция для изменения или удаления атрибута class.

>>> html = markdown.markdown(text,
...     extensions=[WikiLinkExtension(html_class='myclass')]
... )

приведет к присвоению Wiki-ссылкам класса myclass:

<a href="/WikiLink/" class="myclass">WikiLink</a>

Данное расширение также поддерживает расширение Meta-Data. Пожалуйста, посмотрите его документацию насчет особенностей применения. Поддерживаются такие ключевые слова метаданных:

  • wiki_base_url
  • wiki_end_url
  • wiki_html_class

Если используются метаданные, они будут перекрывать установки, предоставляемые интерфейсом extension_configs.

Документ

wiki_base_url: http://example.com/
wiki_end_url:  .html
wiki_html_class:

A [[WikiLink]] in the first paragraph.

будет переделан в такой результат (обратите внимание на пустой wiki_html_class):

<p>A <a href="http://example.com/WikiLink.html">WikiLink</a> in the first paragraph.</p>

Заключение

Итак, теперь примерно понятно, что за зверь — Markdown, как с ним дружить, как его использовать и настраивать, а также выйти за рамки стандартных возможностей при помощи расширений.

Что касается расширений:

  • Extra должно быть подключено обязательно. Не зря оно включено по умолчанию, оно обеспечивает в том числе базовую функциональность.
  • CodeHilite однозначно необходимо, если есть желание поделиться на своих страницах примерами кода. Правда, подсветка синтаксиса — выбор темы, настройка отображения и все такое — тема отдельного разговора.
  • Table of Contents чрезвычайно полезно, если страница получилась довольно длинной и содержит порядочное количество глав под заголовками. Причем, содержание можно вынести даже в сайдбар или футер, если воспользоваться свойством md.toc объекта Markdown и вставить его в шаблон в нужном месте, используя синтаксис jinja2, но это тоже тема отдельного разговора.
  • SmartyPants очень удобно, если есть желание привести знаки препинания к виду, принятому в отечественной типографике — длинные тире, кавычки-«елочки». Без этого расширения придется либо сразу вбивать в тексте HTML-спецсимволы, либо делать соответствующие замены в своем редакторе после написания статьи.
  • Остальные расширения из числа стандартных, на мой взгляд, весьма условно полезны. А Meta-Data вообще полезна скорее программисту, чем пользователю, и может пригодиться разве что при разработке своих расширений.

Стало быть, в файле настройки Pelican pelicanconf.py следует добавить строку

MD_EXTENSIONS = ['codehilite(css_class=highlight)','extra', 'toc', 'smarty(smart_angled_quotes=True)']

(может быть, еще туда же admonition, sane_lists и nl2br при желании), и, применяя полученные из документации знания, наслаждаться результатом.

И еще один момент, связанный с отличиями CMS от статического сайта, собранного генератором: если для CMS можно писать контент где угодно, потом откуда угодно забросить его в базу сайта, внешний вид страниц не будет зависеть от местоположения автора. Если же написать статью и запустить сборку сайта дома, на компьютере, а потом продолжить заниматься этим, скажем, в отпуске, пользуясь ноутбуком и гостиничным вайфаем, надо сначала убедиться, что конфигурация Pelican на домашнем компьютере и ноутбуке совпадают. То есть, версии Python одинаковые, модули установлены одинаковые и одинаковых версий, темы сайта одинаковые, файлы настройки pelicanconf.py и publishconf.py совпадают. И, конечно же, все остальные материалы сайта присутствуют на обеих вычислительных машинах в полном объеме. Это не проблема, сегодня нетрудно синхронизировать информацию на разных устройствах по сети, а наполнять сайт без сети все равно не получится. Однако, об этом тоже надо не забывать.


Теги: