pandoc.py
141 lines
| 4.7 KiB
| text/x-python
|
PythonLexer
MinRK
|
r11267 | """Utility for calling pandoc""" | ||
#----------------------------------------------------------------------------- | ||||
Jonathan Frederic
|
r14827 | # Copyright (c) 2014 the IPython Development Team. | ||
MinRK
|
r11267 | # | ||
# Distributed under the terms of the Modified BSD License. | ||||
# | ||||
# The full license is in the file COPYING.txt, distributed with this software. | ||||
#----------------------------------------------------------------------------- | ||||
#----------------------------------------------------------------------------- | ||||
# Imports | ||||
#----------------------------------------------------------------------------- | ||||
from __future__ import print_function | ||||
# Stdlib imports | ||||
import subprocess | ||||
Daniel B. Vasquez
|
r14759 | import warnings | ||
cel
|
r17549 | import re | ||
MinRK
|
r12523 | from io import TextIOWrapper, BytesIO | ||
MinRK
|
r11267 | |||
# IPython imports | ||||
from IPython.utils.py3compat import cast_bytes | ||||
Daniel B. Vasquez
|
r14763 | from IPython.utils.version import check_version | ||
Daniel B. Vasquez
|
r14768 | from IPython.utils.process import is_cmd_found, FindCmdError | ||
Daniel B. Vasquez
|
r14763 | |||
David Wolever
|
r11703 | from .exceptions import ConversionException | ||
MinRK
|
r11267 | #----------------------------------------------------------------------------- | ||
# Classes and functions | ||||
#----------------------------------------------------------------------------- | ||||
Daniel B. Vasquez
|
r14768 | _minimal_version = "1.12.1" | ||
David Wolever
|
r11702 | |||
MinRK
|
r11293 | def pandoc(source, fmt, to, extra_args=None, encoding='utf-8'): | ||
MinRK
|
r11267 | """Convert an input string in format `from` to format `to` via pandoc. | ||
Parameters | ||||
---------- | ||||
source : string | ||||
Input string, assumed to be valid format `from`. | ||||
fmt : string | ||||
The name of the input format (markdown, etc.) | ||||
to : string | ||||
The name of the output format (html, etc.) | ||||
Returns | ||||
------- | ||||
out : unicode | ||||
Output as returned by pandoc. | ||||
Daniel B. Vasquez
|
r14766 | |||
Thomas Kluyver
|
r14913 | Raises | ||
------ | ||||
PandocMissing | ||||
If pandoc is not installed. | ||||
Daniel B. Vasquez
|
r14766 | Any error messages generated by pandoc are printed to stderr. | ||
MinRK
|
r11267 | """ | ||
Daniel B. Vasquez
|
r14760 | cmd = ['pandoc', '-f', fmt, '-t', to] | ||
MinRK
|
r11293 | if extra_args: | ||
Daniel B. Vasquez
|
r14760 | cmd.extend(extra_args) | ||
Daniel B. Vasquez
|
r14765 | # this will raise an exception that will pop us out of here | ||
Daniel B. Vasquez
|
r14763 | check_pandoc_version() | ||
Daniel B. Vasquez
|
r14760 | |||
# we can safely continue | ||||
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) | ||||
MinRK
|
r11267 | out, _ = p.communicate(cast_bytes(source, encoding)) | ||
MinRK
|
r12523 | out = TextIOWrapper(BytesIO(out), encoding, 'replace').read() | ||
return out.rstrip('\n') | ||||
MinRK
|
r11267 | |||
Daniel B. Vasquez
|
r14759 | |||
def get_pandoc_version(): | ||||
Daniel B. Vasquez
|
r14763 | """Gets the Pandoc version if Pandoc is installed. | ||
Daniel B. Vasquez
|
r14766 | |||
If the minimal version is not met, it will probe Pandoc for its version, cache it and return that value. | ||||
If the minimal version is met, it will return the cached version and stop probing Pandoc | ||||
Thomas Kluyver
|
r14918 | (unless :func:`clean_cache()` is called). | ||
Daniel B. Vasquez
|
r14766 | |||
Thomas Kluyver
|
r14913 | Raises | ||
------ | ||||
PandocMissing | ||||
If pandoc is unavailable. | ||||
Daniel B. Vasquez
|
r14763 | """ | ||
Daniel B. Vasquez
|
r14768 | global __version | ||
Daniel B. Vasquez
|
r14766 | |||
Jonathan Frederic
|
r14827 | if __version is None: | ||
Daniel B. Vasquez
|
r14768 | if not is_cmd_found('pandoc'): | ||
raise PandocMissing() | ||||
cel
|
r17468 | out = subprocess.check_output(['pandoc', '-v'], | ||
universal_newlines=True) | ||||
out_lines = out.splitlines() | ||||
cel
|
r17549 | version_pattern = re.compile(r"^\d+(\.\d+){1,}$") | ||
for tok in out_lines[0].split(): | ||||
if version_pattern.match(tok): | ||||
__version = tok | ||||
break | ||||
Jonathan Frederic
|
r14827 | return __version | ||
Daniel B. Vasquez
|
r14766 | |||
Daniel B. Vasquez
|
r14759 | |||
def check_pandoc_version(): | ||||
Daniel B. Vasquez
|
r14763 | """Returns True if minimal pandoc version is met. | ||
Daniel B. Vasquez
|
r14766 | |||
Thomas Kluyver
|
r14913 | Raises | ||
------ | ||||
PandocMissing | ||||
If pandoc is unavailable. | ||||
Daniel B. Vasquez
|
r14763 | """ | ||
Daniel B. Vasquez
|
r14768 | v = get_pandoc_version() | ||
cel
|
r17549 | if v is None: | ||
warnings.warn("Sorry, we cannot determine the version of pandoc.\n" | ||||
"Please consider reporting this issue and include the" | ||||
"output of pandoc --version.\nContinuing...", | ||||
RuntimeWarning, stacklevel=2) | ||||
cel
|
r17550 | return False | ||
Daniel B. Vasquez
|
r14768 | ok = check_version(v , _minimal_version ) | ||
Daniel B. Vasquez
|
r14766 | if not ok: | ||
Daniel B. Vasquez
|
r14768 | warnings.warn( "You are using an old version of pandoc (%s)\n" % v + | ||
"Recommended version is %s.\nTry updating." % _minimal_version + | ||||
"http://johnmacfarlane.net/pandoc/installing.html.\nContinuing with doubts...", | ||||
RuntimeWarning, stacklevel=2) | ||||
return ok | ||||
Daniel B. Vasquez
|
r14766 | |||
Daniel B. Vasquez
|
r14767 | #----------------------------------------------------------------------------- | ||
# Exception handling | ||||
#----------------------------------------------------------------------------- | ||||
class PandocMissing(ConversionException): | ||||
"""Exception raised when Pandoc is missing. """ | ||||
Daniel B. Vasquez
|
r14768 | def __init__(self, *args, **kwargs): | ||
super(PandocMissing, self).__init__( "Pandoc wasn't found.\n" + | ||||
Daniel B. Vasquez
|
r14767 | "Please check that pandoc is installed:\n" + | ||
"http://johnmacfarlane.net/pandoc/installing.html" ) | ||||
#----------------------------------------------------------------------------- | ||||
# Internal state management | ||||
#----------------------------------------------------------------------------- | ||||
Daniel B. Vasquez
|
r14768 | def clean_cache(): | ||
global __version | ||||
__version = None | ||||
Daniel B. Vasquez
|
r14767 | |||
Daniel B. Vasquez
|
r14768 | __version = None | ||