##// END OF EJS Templates
code shuffling to present the API functions first, and the innards at the end of the file
Daniel B. Vasquez -
Show More
@@ -1,193 +1,192 b''
1 """Utility for calling pandoc"""
1 """Utility for calling pandoc"""
2 #-----------------------------------------------------------------------------
2 #-----------------------------------------------------------------------------
3 # Copyright (c) 2013 the IPython Development Team.
3 # Copyright (c) 2013 the IPython Development Team.
4 #
4 #
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6 #
6 #
7 # The full license is in the file COPYING.txt, distributed with this software.
7 # The full license is in the file COPYING.txt, distributed with this software.
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 from __future__ import print_function
14 from __future__ import print_function
15
15
16 # Stdlib imports
16 # Stdlib imports
17 import subprocess
17 import subprocess
18 import re
18 import re
19 import warnings
19 import warnings
20 from io import TextIOWrapper, BytesIO
20 from io import TextIOWrapper, BytesIO
21
21
22 # IPython imports
22 # IPython imports
23 from IPython.utils.py3compat import cast_bytes
23 from IPython.utils.py3compat import cast_bytes
24 from IPython.utils.version import check_version
24 from IPython.utils.version import check_version
25 from IPython.utils.process import find_cmd, FindCmdError
25 from IPython.utils.process import find_cmd, FindCmdError
26
26
27
27
28 from .exceptions import ConversionException
28 from .exceptions import ConversionException
29
29
30
31 #----------------------------------------------------------------------------
32 # Preliminary checks.
33 # Not finding Pandoc is not always fatal so only a warning is issued at the
34 # module root level so that the import of this module is not fatal.
35 #----------------------------------------------------------------------------
36
37 # command line to make pandoc print it's version. It is also the
38 # easiest way to make pandoc return at all.
39 __pandoc_version_call = ['pandoc', '-v']
40
41 class PandocMissing(ConversionException):
42 """Exception raised when Pandoc is missing. """
43 def __init__(self, cmd, exc, *args, **kwargs):
44 super(PandocMissing, self).__init__( "The command '%s' returned an error: %s.\n" %(" ".join(cmd), exc) +
45 "Please check that pandoc is installed:\n" +
46 "http://johnmacfarlane.net/pandoc/installing.html" )
47 self.exc = exc
48
49 def __bool__(self):
50 return False
51
52 __nonzero__ = __bool__
53
54
55 def pandoc_available(failmode="return", warn=False):
56 """Is pandoc available. Only tries to call Pandoc
57 and inform you that it succeeded or failed.
58
59 Parameters
60 ----------
61 - failmode : string
62 either "return" or "raise". If "return" and pandoc
63 is not available, will return (False, e) where e is
64 the exception returned by subprocess.check_call.
65 - warn : bool
66 issue a user warning if pandoc is not available.
67
68 Return
69 ------
70 out : (Bool, Exception)
71 On success will return True. On failure and failmode=="return"
72 will return False-valued PandocMissing instance
73 """
74
75 try:
76 find_cmd("pandoc")
77 return True
78 except FindCmdError as e:
79 if warn:
80 warnings.warn(
81 "Pandoc cannot be found (calling %s failed).\n" % " ".join(alt or __pandoc_version_call) +
82 "Please check that pandoc is installed:\n" +
83 "http://johnmacfarlane.net/pandoc/installing.html"
84 )
85
86 exc = PandocMissing("pandoc", e)
87 if failmode == "return":
88 return exc
89 else:
90 raise exc
91
92
93 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
94 # Classes and functions
31 # Classes and functions
95 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
96 minimal_version = "1.12.1"
33 minimal_version = "1.12.1"
97
34
98 # The following holds cached values about the pandoc executable.
35 # command line to make pandoc print it's version. It is also the
99 def clean_cache(new=False):
36 # easiest way to make pandoc return at all.
100 if new:
37 __pandoc_version_call = ['pandoc', '-v']
101 global __cache
102 cache = {}
103 __cache = cache
104 else:
105 cache = __cache
106 cache.clear()
107
108 cache['version_ok'] = False
109 cache['version'] = None
110 return cache
111
112 __cache = clean_cache(new=True)
113
38
114
39
115 def pandoc(source, fmt, to, extra_args=None, encoding='utf-8'):
40 def pandoc(source, fmt, to, extra_args=None, encoding='utf-8'):
116 """Convert an input string in format `from` to format `to` via pandoc.
41 """Convert an input string in format `from` to format `to` via pandoc.
117
42
118 Parameters
43 Parameters
119 ----------
44 ----------
120 source : string
45 source : string
121 Input string, assumed to be valid format `from`.
46 Input string, assumed to be valid format `from`.
122 fmt : string
47 fmt : string
123 The name of the input format (markdown, etc.)
48 The name of the input format (markdown, etc.)
124 to : string
49 to : string
125 The name of the output format (html, etc.)
50 The name of the output format (html, etc.)
126
51
127 Returns
52 Returns
128 -------
53 -------
129 out : unicode
54 out : unicode
130 Output as returned by pandoc.
55 Output as returned by pandoc.
131
56
132 Exceptions
57 Exceptions
133 ----------
58 ----------
134 This function will raise PandocMissing if pandoc is not installed.
59 This function will raise PandocMissing if pandoc is not installed.
135 Any error messages generated by pandoc are printed to stderr.
60 Any error messages generated by pandoc are printed to stderr.
136
61
137 """
62 """
138 cmd = ['pandoc', '-f', fmt, '-t', to]
63 cmd = ['pandoc', '-f', fmt, '-t', to]
139 if extra_args:
64 if extra_args:
140 cmd.extend(extra_args)
65 cmd.extend(extra_args)
141
66
142 # this will raise an exception that will pop us out of here
67 # this will raise an exception that will pop us out of here
143 check_pandoc_version()
68 check_pandoc_version()
144
69
145 # we can safely continue
70 # we can safely continue
146 p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
71 p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
147 out, _ = p.communicate(cast_bytes(source, encoding))
72 out, _ = p.communicate(cast_bytes(source, encoding))
148 out = TextIOWrapper(BytesIO(out), encoding, 'replace').read()
73 out = TextIOWrapper(BytesIO(out), encoding, 'replace').read()
149 return out.rstrip('\n')
74 return out.rstrip('\n')
150
75
151
76
77 def pandoc_available(failmode="return", warn=False):
78 """Is pandoc available. Relies on `IPython.utils.process.find_cmd`.
79
80 Parameters
81 ----------
82 - failmode : string
83 either "return" or "raise". See below.
84 - warn : bool
85 issue a user warning if pandoc is not available.
86
87 Return
88 ------
89 out : (Bool, Exception)
90 On success will return True. On failure and failmode=="return"
91 will return False-valued PandocMissing instance. If failmode is
92 anything else, the function will not return but raise PandocMissing.
93 """
94
95 try:
96 find_cmd("pandoc")
97 return True
98 except FindCmdError as e:
99 if warn:
100 warnings.warn(
101 "Pandoc cannot be found (find_cmd('pandoc') failed).\n"+
102 "Please check that pandoc is installed:\n" +
103 "http://johnmacfarlane.net/pandoc/installing.html"
104 )
105
106 exc = PandocMissing("pandoc", e)
107 if failmode == "return":
108 return exc
109 else:
110 raise exc
111
112
152 def get_pandoc_version():
113 def get_pandoc_version():
153 """Gets the Pandoc version if Pandoc is installed.
114 """Gets the Pandoc version if Pandoc is installed.
154
115
155 Return
116 Return
156 ------
117 ------
157 If the minimal version is not met, it will probe Pandoc for its version, cache it and return that value.
118 If the minimal version is not met, it will probe Pandoc for its version, cache it and return that value.
158 If the minimal version is met, it will return the cached version and stop probing Pandoc
119 If the minimal version is met, it will return the cached version and stop probing Pandoc
159 (unless `clean_cache()` is called).
120 (unless `clean_cache()` is called).
160
121
161 Exceptions
122 Exceptions
162 ----------
123 ----------
163 PandocMissing will be raised if pandoc is unavailable.
124 PandocMissing will be raised if pandoc is unavailable.
164 """
125 """
165
126
166 if __cache['version_ok'] and __cache['version']:
127 if __cache['version_ok'] and __cache['version']:
167 return __cache['version']
128 return __cache['version']
168 else:
129 else:
169 pandoc_available(failmode="raise")
130 pandoc_available(failmode="raise")
170 out = subprocess.check_output(__pandoc_version_call, universal_newlines=True)
131 out = subprocess.check_output(__pandoc_version_call, universal_newlines=True)
171 pv_re = re.compile(r'(\d{0,3}\.\d{0,3}\.\d{0,3})')
132 pv_re = re.compile(r'(\d{0,3}\.\d{0,3}\.\d{0,3})')
172 __cache['version'] = version = pv_re.search(out).group(0)
133 __cache['version'] = version = pv_re.search(out).group(0)
173 return version
134 return version
174
135
175
136
176 def check_pandoc_version():
137 def check_pandoc_version():
177 """Returns True if minimal pandoc version is met.
138 """Returns True if minimal pandoc version is met.
178
139
179 Exceptions
140 Exceptions
180 ----------
141 ----------
181 PandocMissing will be raised if pandoc is unavailable.
142 PandocMissing will be raised if pandoc is unavailable.
182 """
143 """
183 ok = __cache['version_ok']
144 ok = __cache['version_ok']
184 if not ok:
145 if not ok:
185 __cache['version_ok'] = ok = check_version( get_pandoc_version(), minimal_version )
146 __cache['version_ok'] = ok = check_version( get_pandoc_version(), minimal_version )
186 if not ok:
147 if not ok:
187 warnings.warn( "You are using an old version of pandoc (%s)\n" % __cache['version'] +
148 warnings.warn( "You are using an old version of pandoc (%s)\n" % __cache['version'] +
188 "Recommended version is %s.\nTry updating." % minimal_version +
149 "Recommended version is %s.\nTry updating." % minimal_version +
189 "http://johnmacfarlane.net/pandoc/installing.html.\nContinuing with doubts...",
150 "http://johnmacfarlane.net/pandoc/installing.html.\nContinuing with doubts...",
190 RuntimeWarning, stacklevel=2)
151 RuntimeWarning, stacklevel=2)
191 return __cache['version_ok']
152 return __cache['version_ok']
192
153
154 #-----------------------------------------------------------------------------
155 # Exception handling
156 #-----------------------------------------------------------------------------
157 class PandocMissing(ConversionException):
158 """Exception raised when Pandoc is missing. """
159 def __init__(self, cmd, exc, *args, **kwargs):
160 super(PandocMissing, self).__init__( "The command '%s' returned an error: %s.\n" %(" ".join(cmd), exc) +
161 "Please check that pandoc is installed:\n" +
162 "http://johnmacfarlane.net/pandoc/installing.html" )
163 self.exc = exc
164
165 def __bool__(self):
166 return False
167
168 __nonzero__ = __bool__
169
170
171 #-----------------------------------------------------------------------------
172 # Internal state management
173 #-----------------------------------------------------------------------------
174 def clean_cache(new=False):
175 if new:
176 global __cache
177 cache = {}
178 __cache = cache
179 else:
180 cache = __cache
181 cache.clear()
182
183 cache['version_ok'] = False
184 cache['version'] = None
185 return cache
186
187 # The following holds cache values about the pandoc executable.
188 __cache = clean_cache(new=True)
189
190
191
193
192
General Comments 0
You need to be logged in to leave comments. Login now