"""Utility for calling pandoc""" #----------------------------------------------------------------------------- # Copyright (c) 2013 the IPython Development Team. # # 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 import re import warnings from io import TextIOWrapper, BytesIO # IPython imports from IPython.utils.py3compat import cast_bytes from .exceptions import ConversionException #---------------------------------------------------------------------------- # Preliminary checks. # Not finding Pandoc is not always fatal so only a warning is issued at the # module root level so that the import of this module is not fatal. #---------------------------------------------------------------------------- class PandocMissing(ConversionException): """Exception raised when Pandoc is missing. """ def __init__(self, cmd, exc, *args, **kwargs): super(PandocMissing, self).__init__( "The command '%s' returned an error: %s.\n" %(" ".join(cmd), exc) + "Please check that pandoc is installed:\n" + "http://johnmacfarlane.net/pandoc/installing.html" ) def pandoc_available(failmode="return", warn=False, alt=None): """Is pandoc available. Only tries to call Pandoc and inform you that it succeeded or failed. Parameters ---------- - failmode : string either "return" or "raise". If "return" and pandoc is not available, will return (False, e) where e is the exception returned by subprocess.check_call. - warn : bool issue a user warning if pandoc is not available. - alt: list of strings command to print in the error (not used as actual call) Return ------ out : (Bool, Exception) On success will return (True, None). On failure and failmode=="return" will return (False, OSError instance) """ cmd = ["pandoc", "-v"] try: out = subprocess.check_output(cmd, universal_newlines=True) return True, None except OSError as e: if warn: warnings.warn( "Pandoc cannot be found (calling %s failed).\n" % " ".join(alt or cmd) + "Please check that pandoc is installed:\n" + "http://johnmacfarlane.net/pandoc/installing.html" ) if failmode == "return": return False, e else: raise PandocMissing(alt or cmd, e) #----------------------------------------------------------------------------- # Classes and functions #----------------------------------------------------------------------------- minimal_version = "1.12.1" def pandoc(source, fmt, to, extra_args=None, encoding='utf-8'): """Convert an input string in format `from` to format `to` via pandoc. This function will raise an error if pandoc is not installed. Any error messages generated by pandoc are printed to stderr. 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. """ cmd = ['pandoc', '-f', fmt, '-t', to] if extra_args: cmd.extend(extra_args) # if pandoc is missing let the exception bubble us out of here pandoc_available(failmode="raise", alt=cmd) # we can safely continue p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) out, _ = p.communicate(cast_bytes(source, encoding)) out = TextIOWrapper(BytesIO(out), encoding, 'replace').read() return out.rstrip('\n') def get_pandoc_version(): """Gets the Pandoc version if Pandoc is installed.""" try: return pandoc.version except AttributeError: out = pandoc("None", "None", "None", ["-v"]) pv_re = re.compile(r'(\d{0,3}\.\d{0,3}\.\d{0,3})') pandoc.version = pv_re.search(out).group(0) return pandoc.version def check_pandoc_version(): """Returns True if minimal pandoc version is met""" return get_pandoc_version() >= minimal_version if pandoc_available(warn=True)[0]: if(not check_pandoc_version()): warnings.warn( "You are using an old version of pandoc (%s)\n" % pandoc.version + "Recommended version is %s.\nTry updating." % minimal_version + "http://johnmacfarlane.net/pandoc/installing.html.\nContinuing with doubts...", RuntimeWarning, stacklevel=2)