Skip to content

Commit

Permalink
Merge pull request #58 from moggers87/24-external
Browse files Browse the repository at this point in the history
External command filter
  • Loading branch information
moggers87 committed Dec 29, 2018
2 parents 0b20d9f + 8f46fba commit 30baab8
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 0 deletions.
7 changes: 7 additions & 0 deletions docs/exhibition.filters.external.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
exhibition.filters.external module
==================================

.. automodule:: exhibition.filters.external
:members:
:undoc-members:
:show-inheritance:
1 change: 1 addition & 0 deletions docs/exhibition.filters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Submodules

.. toctree::

exhibition.filters.external
exhibition.filters.jinja2

Module contents
Expand Down
13 changes: 13 additions & 0 deletions docs/meta.rst
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,19 @@ If specified, this will wrap the file content in ``{% block %}``.
Markdown options as specified in the `Markdown documentation
<https://python-markdown.github.io/reference/#markdown>`_.

External Command
^^^^^^^^^^^^^^^^

external_cmd
~~~~~~~~~~~~

The command to run. This should use the placeholders ``{INPUT}`` and
``{OUTPUT}`` for the input and output files respectively. For example:

.. code-block: yaml
external_cmd: "cat {INPUT} | sort > {OUTPUT}"
Cache busting
-------------

Expand Down
74 changes: 74 additions & 0 deletions exhibition/filters/external.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
##
#
# Copyright (C) 2018 Matt Molyneaux
#
# This file is part of Exhibition.
#
# Exhibition is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Exhibition is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Exhibition. If not, see <https://www.gnu.org/licenses/>.
#
##

"""
External command filter
Use an external command to process a file, like so:
.. code-block:: yaml
filter: exhibition.filters.external
external_cmd: sed 's/this/that/g' {INPUT} > {OUTPUT}
"""

from tempfile import TemporaryDirectory
import pathlib
import subprocess

DEFAULT_GLOB = "*.*"

INPUT_NAME = "input"
OUTPUT_NAME = "output"

INPUT_KEY = "INPUT"
OUTPUT_KEY = "OUTPUT"


def content_filter(node, content):
"""
This is the actual content filter called by :class:`exhibition.main.Node`
on appropiate nodes.
:param node:
The node being rendered
:param content:
The content of the node, stripped of any YAML frontmatter
"""
tmp_dir = TemporaryDirectory()
input_file = pathlib.Path(tmp_dir.name, INPUT_NAME)
output_file = pathlib.Path(tmp_dir.name, OUTPUT_NAME)

cmd = node.meta["external_cmd"]
cmd = cmd.format(**{
INPUT_KEY: input_file,
OUTPUT_KEY: output_file,
})

with input_file.open("w") as f:
f.write(content)

subprocess.run(cmd, shell=True)

with output_file.open("r") as f:
output = f.read()

return output
34 changes: 34 additions & 0 deletions exhibition/tests/test_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@

from tempfile import TemporaryDirectory
from unittest import TestCase, mock
import base64
import pathlib

from jinja2.exceptions import TemplateRuntimeError
from jinja2 import Markup

from exhibition.filters.external import content_filter as external_filter
from exhibition.filters.jinja2 import content_filter as jinja_filter
from exhibition.node import Node

Expand Down Expand Up @@ -92,6 +94,38 @@
""".strip()


class ExternalCommandTestCase(TestCase):
def test_input_output(self):
content = "hello, how are you?"
node = Node(mock.Mock(), None, meta={"external_cmd": "cat {INPUT} | base64 > {OUTPUT}"})
node._content_start = 0
output = external_filter(node, content)

expected_output = base64.b64encode(content.encode()).decode() + "\n"
self.assertEqual(output, expected_output)

def test_filter_glob(self):
with TemporaryDirectory() as content_path, TemporaryDirectory() as deploy_path:
# default glob is *.*
for filename in ["blog.html", "mytext.txt"]:
content = "hello {}, how are you?".format(filename)
path = pathlib.Path(content_path, "blog.htm")
with path.open("w") as f:
f.write(content)

node = Node(path, Node(path.parent, None,
{"content_path": content_path,
"deploy_path": deploy_path,
"filter": "exhibition.filters.external",
"external_cmd": "cat {INPUT} | base64 > {OUTPUT}"}))
node.render()
with pathlib.Path(deploy_path, "blog.htm").open("r") as f:
output = f.read()

expected_output = base64.b64encode(content.encode()).decode() + "\n"
self.assertEqual(output, expected_output)


class Jinja2TestCase(TestCase):
def test_template(self):
node = Node(mock.Mock(), None, meta={"templates": []})
Expand Down

0 comments on commit 30baab8

Please sign in to comment.