Source code for bpy.handlers

# Copyright (C) 2013 by Yu-Jie Lin
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

"""
Markup handlers' IDs and extensions:

==================== ========================================================
ID                   extensions
==================== ========================================================
``AsciiDoc``         ``.asciidoc``
``HTML``             ``.html``, ``.htm``, ``.raw``
``Markdown``         ``.md``, ``.mkd``, ``.mkdn``, ``.mkdown``, ``.markdown``
``reStructuredText`` ``.rst``
``Text``             ``.txt``, ``.text``
==================== ========================================================


Options
=======

The general options are supported by all handlers, defined in
:class:`bpy.handlers.base.BaseHandler`, but they have to be specified per
handler basis, the following sample code shows the options and their default
value:

.. code:: python

  handlers = {
    '<MARKUP HANDLER ID>': {
      'options': {
        # prefix string to HTML ID to avoid conflict
        'id_affix': None,

        # string to prepend to actual markup
        'markup_prefix': '',

        # string to append to actual markup
        'markup_suffix': '',

        # use smartypant to process the output of markup processor
        'smartypants': False,

        # support image embedding via data URI scheme
        'embed_images': False,
      },
    },
  }

.. _id_affix:

``id_affix``
------------

``id_affix`` is used to avoid conflict across posts' HTML element ID. It may be
a prefix or suffix, depending on handler's implementation and markup library's
support. It has three types of value:

1. ``None``: no affix to ID.
2. non-empty string: the string is the affix.
3. empty string: the affix is generated automatically.

Currently supported markup handler:

* :mod:`bpy.handlers.rst`

``markup_prefix`` and ``markup_suffix``
---------------------------------------

``markup_prefix`` and ``markup_suffix`` can be useful for adding header and
footer content for posts. Another useful case in reStructuredText is you can
use it for setting up some directives, for example ``.. sectnum::``, so you can
ensure all posts have prefixing section number if in use conjunction with
``.. contents::``.

``smartypants``
---------------

If ``smartypants`` is enabled, then all generated HTML from markup processor
will be processed by smartypants_ library.

.. _smartypants: https://pypi.python.org/pypi/smartypants

.. _embed_images:

``embed_images``
----------------

.. note::

  Only :mod:`bpy.handlers.text` does not support this option.

When this option is enabled, it looks for the ``src`` attribute of ``img`` tag
in rendered HTML, see if there is a local files, excluding ``http``, ``https``,
and ``data`` schemes, if found, it reads the file and embeds with Base64
encoded content.

For example, in reStructuredText:

.. code:: rst

  .. image:: /path/to/test.png

Instead of

.. code:: html

  <img alt="/path/to/test.png" src="/path/to/test.png" />

It could be replaced with, if ``/path/to/test.png`` exists:

.. code:: html

  <img alt="/path/to/test.png" src="data:image/png;base64,..." />

If the image file can't be found, a message will be printed out, the rendered
image tag will be kept untouched.

.. _custom-handler:

Writing a custom handler
========================

A sample handler ``sample_handler.py``:

.. code:: python

  from bpy.handlers import base

  class Handler(base.BaseHandler):
    PREFIX_HEAD = ''
    PREFIX_END = ''
    HEADER_FMT = '%s: %s'

    def _generate(self, markup=None):
      if markup is None:
        markup = self.markup

      html = do_process(markup)
      return html

And corresponding setting in ``brc.py``:

.. code:: python

  import re

  handlers = {
    'SampleHandler': {
      'match': re.compile(r'.*\.ext$'),
      'module': 'sample_handler',
    },
  }
"""

import os
import re
import sys
import traceback

handlers = {
  'AsciiDoc': {
    'match': re.compile(r'.*\.asciidoc$'),
    'module': 'bpy.handlers.asciidoc',
  },
  'HTML': {
    'match': re.compile(r'.*\.(html?|raw)$'),
    'module': 'bpy.handlers.html',
  },
  'Markdown': {
    'match': re.compile(r'.*\.(markdown|md(own)?|mkdn?)$'),
    'module': 'bpy.handlers.mkd',
  },
  'reStructuredText': {
    'match': re.compile(r'.*\.rst$'),
    'module': 'bpy.handlers.rst',
  },
  'Text': {
    'match': re.compile(r'.*\.te?xt$'),
    'module': 'bpy.handlers.text',
  },
}


[docs]def find_handler(filename): sys.path.insert(0, os.getcwd()) module = None for name, hdlr in handlers.items(): if hdlr['match'].match(filename): try: module = __import__(hdlr['module'], fromlist=['Handler']) break except Exception: print('Cannot load module %s of handler %s' % (hdlr['module'], name)) traceback.print_exc() sys.path.pop(0) if module: return module.Handler(filename, hdlr.get('options', {})) return None