123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- """Utility for calling pandoc"""
- # Copyright (c) IPython Development Team.
- # Distributed under the terms of the Modified BSD License.
- from __future__ import print_function, absolute_import
- import subprocess
- import warnings
- import re
- from io import TextIOWrapper, BytesIO
- from nbconvert.utils.version import check_version
- from ipython_genutils.py3compat import cast_bytes, which
- from .exceptions import ConversionException
- _minimal_version = "1.12.1"
- _maximal_version = "3.0.0"
- def pandoc(source, fmt, to, extra_args=None, encoding='utf-8'):
- """Convert an input string using pandoc.
- Pandoc converts an input string `from` a format `to` a target format.
- 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.
- Raises
- ------
- PandocMissing
- If pandoc is not installed.
-
- Any error messages generated by pandoc are printed to stderr.
- """
- cmd = ['pandoc', '-f', fmt, '-t', to]
- if extra_args:
- cmd.extend(extra_args)
- # this will raise an exception that will pop us out of here
- check_pandoc_version()
-
- # 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.
-
- 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
- (unless :func:`clean_cache()` is called).
- Raises
- ------
- PandocMissing
- If pandoc is unavailable.
- """
- global __version
- if __version is None:
- if not which('pandoc'):
- raise PandocMissing()
- out = subprocess.check_output(['pandoc', '-v'])
- out_lines = out.splitlines()
- version_pattern = re.compile(r"^\d+(\.\d+){1,}$")
- for tok in out_lines[0].decode('ascii', 'replace').split():
- if version_pattern.match(tok):
- __version = tok
- break
- return __version
- def check_pandoc_version():
- """Returns True if pandoc's version meets at least minimal version.
- Raises
- ------
- PandocMissing
- If pandoc is unavailable.
- """
- if check_pandoc_version._cached is not None:
- return check_pandoc_version._cached
- v = get_pandoc_version()
- 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)
- return False
- ok = check_version(v, _minimal_version, max_v=_maximal_version)
- check_pandoc_version._cached = ok
- if not ok:
- warnings.warn( "You are using an unsupported version of pandoc (%s).\n" % v +
- "Your version must be at least (%s) " % _minimal_version +
- "but less than (%s).\n" % _maximal_version +
- "Refer to http://pandoc.org/installing.html.\nContinuing with doubts...",
- RuntimeWarning, stacklevel=2)
- return ok
- check_pandoc_version._cached = None
- #-----------------------------------------------------------------------------
- # Exception handling
- #-----------------------------------------------------------------------------
- class PandocMissing(ConversionException):
- """Exception raised when Pandoc is missing."""
- def __init__(self, *args, **kwargs):
- super(PandocMissing, self).__init__( "Pandoc wasn't found.\n" +
- "Please check that pandoc is installed:\n" +
- "http://pandoc.org/installing.html" )
- #-----------------------------------------------------------------------------
- # Internal state management
- #-----------------------------------------------------------------------------
- def clean_cache():
- global __version
- __version = None
- __version = None
|