##// END OF EJS Templates
Merge pull request #4342 from abhinav-upadhyay/unnessary-variable-assignment...
Thomas Kluyver -
r12948:f8bc560e merge
parent child Browse files
Show More
@@ -1,338 +1,337 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 A module to change reload() so that it acts recursively.
4 4 To enable it type::
5 5
6 6 import __builtin__, deepreload
7 7 __builtin__.reload = deepreload.reload
8 8
9 9 You can then disable it with::
10 10
11 11 __builtin__.reload = deepreload.original_reload
12 12
13 13 Alternatively, you can add a dreload builtin alongside normal reload with::
14 14
15 15 __builtin__.dreload = deepreload.reload
16 16
17 17 This code is almost entirely based on knee.py, which is a Python
18 18 re-implementation of hierarchical module import.
19 19 """
20 20 #*****************************************************************************
21 21 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
22 22 #
23 23 # Distributed under the terms of the BSD License. The full license is in
24 24 # the file COPYING, distributed as part of this software.
25 25 #*****************************************************************************
26 26
27 27 import __builtin__
28 28 from contextlib import contextmanager
29 29 import imp
30 30 import sys
31 31
32 32 from types import ModuleType
33 33 from warnings import warn
34 34
35 35 original_import = __builtin__.__import__
36 36
37 37 @contextmanager
38 38 def replace_import_hook(new_import):
39 39 saved_import = __builtin__.__import__
40 40 __builtin__.__import__ = new_import
41 41 try:
42 42 yield
43 43 finally:
44 44 __builtin__.__import__ = saved_import
45 45
46 46 def get_parent(globals, level):
47 47 """
48 48 parent, name = get_parent(globals, level)
49 49
50 50 Return the package that an import is being performed in. If globals comes
51 51 from the module foo.bar.bat (not itself a package), this returns the
52 52 sys.modules entry for foo.bar. If globals is from a package's __init__.py,
53 53 the package's entry in sys.modules is returned.
54 54
55 55 If globals doesn't come from a package or a module in a package, or a
56 56 corresponding entry is not found in sys.modules, None is returned.
57 57 """
58 58 orig_level = level
59 59
60 60 if not level or not isinstance(globals, dict):
61 61 return None, ''
62 62
63 63 pkgname = globals.get('__package__', None)
64 64
65 65 if pkgname is not None:
66 66 # __package__ is set, so use it
67 67 if not hasattr(pkgname, 'rindex'):
68 68 raise ValueError('__package__ set to non-string')
69 69 if len(pkgname) == 0:
70 70 if level > 0:
71 71 raise ValueError('Attempted relative import in non-package')
72 72 return None, ''
73 73 name = pkgname
74 74 else:
75 75 # __package__ not set, so figure it out and set it
76 76 if '__name__' not in globals:
77 77 return None, ''
78 78 modname = globals['__name__']
79 79
80 80 if '__path__' in globals:
81 81 # __path__ is set, so modname is already the package name
82 82 globals['__package__'] = name = modname
83 83 else:
84 84 # Normal module, so work out the package name if any
85 85 lastdot = modname.rfind('.')
86 86 if lastdot < 0 and level > 0:
87 87 raise ValueError("Attempted relative import in non-package")
88 88 if lastdot < 0:
89 89 globals['__package__'] = None
90 90 return None, ''
91 91 globals['__package__'] = name = modname[:lastdot]
92 92
93 93 dot = len(name)
94 94 for x in xrange(level, 1, -1):
95 95 try:
96 96 dot = name.rindex('.', 0, dot)
97 97 except ValueError:
98 98 raise ValueError("attempted relative import beyond top-level "
99 99 "package")
100 100 name = name[:dot]
101 101
102 102 try:
103 103 parent = sys.modules[name]
104 104 except:
105 105 if orig_level < 1:
106 106 warn("Parent module '%.200s' not found while handling absolute "
107 107 "import" % name)
108 108 parent = None
109 109 else:
110 110 raise SystemError("Parent module '%.200s' not loaded, cannot "
111 111 "perform relative import" % name)
112 112
113 113 # We expect, but can't guarantee, if parent != None, that:
114 114 # - parent.__name__ == name
115 115 # - parent.__dict__ is globals
116 116 # If this is violated... Who cares?
117 117 return parent, name
118 118
119 119 def load_next(mod, altmod, name, buf):
120 120 """
121 121 mod, name, buf = load_next(mod, altmod, name, buf)
122 122
123 123 altmod is either None or same as mod
124 124 """
125 125
126 126 if len(name) == 0:
127 127 # completely empty module name should only happen in
128 128 # 'from . import' (or '__import__("")')
129 129 return mod, None, buf
130 130
131 131 dot = name.find('.')
132 132 if dot == 0:
133 133 raise ValueError('Empty module name')
134 134
135 135 if dot < 0:
136 136 subname = name
137 137 next = None
138 138 else:
139 139 subname = name[:dot]
140 140 next = name[dot+1:]
141 141
142 142 if buf != '':
143 143 buf += '.'
144 144 buf += subname
145 145
146 146 result = import_submodule(mod, subname, buf)
147 147 if result is None and mod != altmod:
148 148 result = import_submodule(altmod, subname, subname)
149 149 if result is not None:
150 150 buf = subname
151 151
152 152 if result is None:
153 153 raise ImportError("No module named %.200s" % name)
154 154
155 155 return result, next, buf
156 156
157 157 # Need to keep track of what we've already reloaded to prevent cyclic evil
158 158 found_now = {}
159 159
160 160 def import_submodule(mod, subname, fullname):
161 161 """m = import_submodule(mod, subname, fullname)"""
162 162 # Require:
163 163 # if mod == None: subname == fullname
164 164 # else: mod.__name__ + "." + subname == fullname
165 165
166 166 global found_now
167 167 if fullname in found_now and fullname in sys.modules:
168 168 m = sys.modules[fullname]
169 169 else:
170 170 print 'Reloading', fullname
171 171 found_now[fullname] = 1
172 172 oldm = sys.modules.get(fullname, None)
173 173
174 174 if mod is None:
175 175 path = None
176 176 elif hasattr(mod, '__path__'):
177 177 path = mod.__path__
178 178 else:
179 179 return None
180 180
181 181 try:
182 182 # This appears to be necessary on Python 3, because imp.find_module()
183 183 # tries to import standard libraries (like io) itself, and we don't
184 184 # want them to be processed by our deep_import_hook.
185 185 with replace_import_hook(original_import):
186 186 fp, filename, stuff = imp.find_module(subname, path)
187 187 except ImportError:
188 188 return None
189 189
190 190 try:
191 191 m = imp.load_module(fullname, fp, filename, stuff)
192 192 except:
193 193 # load_module probably removed name from modules because of
194 194 # the error. Put back the original module object.
195 195 if oldm:
196 196 sys.modules[fullname] = oldm
197 197 raise
198 198 finally:
199 199 if fp: fp.close()
200 200
201 201 add_submodule(mod, m, fullname, subname)
202 202
203 203 return m
204 204
205 205 def add_submodule(mod, submod, fullname, subname):
206 206 """mod.{subname} = submod"""
207 207 if mod is None:
208 208 return #Nothing to do here.
209 209
210 210 if submod is None:
211 211 submod = sys.modules[fullname]
212 212
213 213 setattr(mod, subname, submod)
214 214
215 215 return
216 216
217 217 def ensure_fromlist(mod, fromlist, buf, recursive):
218 218 """Handle 'from module import a, b, c' imports."""
219 219 if not hasattr(mod, '__path__'):
220 220 return
221 221 for item in fromlist:
222 222 if not hasattr(item, 'rindex'):
223 223 raise TypeError("Item in ``from list'' not a string")
224 224 if item == '*':
225 225 if recursive:
226 226 continue # avoid endless recursion
227 227 try:
228 228 all = mod.__all__
229 229 except AttributeError:
230 230 pass
231 231 else:
232 232 ret = ensure_fromlist(mod, all, buf, 1)
233 233 if not ret:
234 234 return 0
235 235 elif not hasattr(mod, item):
236 236 import_submodule(mod, item, buf + '.' + item)
237 237
238 238 def deep_import_hook(name, globals=None, locals=None, fromlist=None, level=-1):
239 239 """Replacement for __import__()"""
240 240 parent, buf = get_parent(globals, level)
241 241
242 242 head, name, buf = load_next(parent, None if level < 0 else parent, name, buf)
243 243
244 244 tail = head
245 245 while name:
246 246 tail, name, buf = load_next(tail, tail, name, buf)
247 247
248 248 # If tail is None, both get_parent and load_next found
249 249 # an empty module name: someone called __import__("") or
250 250 # doctored faulty bytecode
251 251 if tail is None:
252 252 raise ValueError('Empty module name')
253 253
254 254 if not fromlist:
255 255 return head
256 256
257 257 ensure_fromlist(tail, fromlist, buf, 0)
258 258 return tail
259 259
260 260 modules_reloading = {}
261 261
262 262 def deep_reload_hook(m):
263 263 """Replacement for reload()."""
264 264 if not isinstance(m, ModuleType):
265 265 raise TypeError("reload() argument must be module")
266 266
267 267 name = m.__name__
268 268
269 269 if name not in sys.modules:
270 270 raise ImportError("reload(): module %.200s not in sys.modules" % name)
271 271
272 272 global modules_reloading
273 273 try:
274 274 return modules_reloading[name]
275 275 except:
276 276 modules_reloading[name] = m
277 277
278 278 dot = name.rfind('.')
279 279 if dot < 0:
280 280 subname = name
281 281 path = None
282 282 else:
283 283 try:
284 284 parent = sys.modules[name[:dot]]
285 285 except KeyError:
286 286 modules_reloading.clear()
287 287 raise ImportError("reload(): parent %.200s not in sys.modules" % name[:dot])
288 288 subname = name[dot+1:]
289 289 path = getattr(parent, "__path__", None)
290 290
291 291 try:
292 292 # This appears to be necessary on Python 3, because imp.find_module()
293 293 # tries to import standard libraries (like io) itself, and we don't
294 294 # want them to be processed by our deep_import_hook.
295 295 with replace_import_hook(original_import):
296 296 fp, filename, stuff = imp.find_module(subname, path)
297 297 finally:
298 298 modules_reloading.clear()
299 299
300 300 try:
301 301 newm = imp.load_module(name, fp, filename, stuff)
302 302 except:
303 303 # load_module probably removed name from modules because of
304 304 # the error. Put back the original module object.
305 305 sys.modules[name] = m
306 306 raise
307 307 finally:
308 308 if fp: fp.close()
309 309
310 310 modules_reloading.clear()
311 311 return newm
312 312
313 313 # Save the original hooks
314 314 try:
315 315 original_reload = __builtin__.reload
316 316 except AttributeError:
317 317 original_reload = imp.reload # Python 3
318 318
319 319 # Replacement for reload()
320 320 def reload(module, exclude=['sys', 'os.path', '__builtin__', '__main__']):
321 321 """Recursively reload all modules used in the given module. Optionally
322 322 takes a list of modules to exclude from reloading. The default exclude
323 323 list contains sys, __main__, and __builtin__, to prevent, e.g., resetting
324 324 display, exception, and io hooks.
325 325 """
326 326 global found_now
327 327 for i in exclude:
328 328 found_now[i] = 1
329 329 try:
330 330 with replace_import_hook(deep_import_hook):
331 ret = deep_reload_hook(module)
331 return deep_reload_hook(module)
332 332 finally:
333 333 found_now = {}
334 return ret
335 334
336 335 # Uncomment the following to automatically activate deep reloading whenever
337 336 # this module is imported
338 337 #__builtin__.reload = reload
@@ -1,252 +1,251 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tools for handling LaTeX.
3 3
4 4 Authors:
5 5
6 6 * Brian Granger
7 7 """
8 8 #-----------------------------------------------------------------------------
9 9 # Copyright (C) 2010 IPython Development Team.
10 10 #
11 11 # Distributed under the terms of the Modified BSD License.
12 12 #
13 13 # The full license is in the file COPYING.txt, distributed with this software.
14 14 #-----------------------------------------------------------------------------
15 15
16 16 #-----------------------------------------------------------------------------
17 17 # Imports
18 18 #-----------------------------------------------------------------------------
19 19
20 20 from io import BytesIO
21 21 from base64 import encodestring
22 22 import os
23 23 import tempfile
24 24 import shutil
25 25 import subprocess
26 26
27 27 from IPython.utils.process import find_cmd, FindCmdError
28 28 from IPython.config.configurable import SingletonConfigurable
29 29 from IPython.utils.traitlets import List, CBool, CUnicode
30 30 from IPython.utils.py3compat import bytes_to_str
31 31
32 32 #-----------------------------------------------------------------------------
33 33 # Tools
34 34 #-----------------------------------------------------------------------------
35 35
36 36
37 37 class LaTeXTool(SingletonConfigurable):
38 38 """An object to store configuration of the LaTeX tool."""
39 39
40 40 backends = List(
41 41 CUnicode, ["matplotlib", "dvipng"],
42 42 help="Preferred backend to draw LaTeX math equations. "
43 43 "Backends in the list are checked one by one and the first "
44 44 "usable one is used. Note that `matplotlib` backend "
45 45 "is usable only for inline style equations. To draw "
46 46 "display style equations, `dvipng` backend must be specified. ",
47 47 # It is a List instead of Enum, to make configuration more
48 48 # flexible. For example, to use matplotlib mainly but dvipng
49 49 # for display style, the default ["matplotlib", "dvipng"] can
50 50 # be used. To NOT use dvipng so that other repr such as
51 51 # unicode pretty printing is used, you can use ["matplotlib"].
52 52 config=True)
53 53
54 54 use_breqn = CBool(
55 55 True,
56 56 help="Use breqn.sty to automatically break long equations. "
57 57 "This configuration takes effect only for dvipng backend.",
58 58 config=True)
59 59
60 60 packages = List(
61 61 ['amsmath', 'amsthm', 'amssymb', 'bm'],
62 62 help="A list of packages to use for dvipng backend. "
63 63 "'breqn' will be automatically appended when use_breqn=True.",
64 64 config=True)
65 65
66 66 preamble = CUnicode(
67 67 help="Additional preamble to use when generating LaTeX source "
68 68 "for dvipng backend.",
69 69 config=True)
70 70
71 71
72 72 def latex_to_png(s, encode=False, backend=None, wrap=False):
73 73 """Render a LaTeX string to PNG.
74 74
75 75 Parameters
76 76 ----------
77 77 s : str
78 78 The raw string containing valid inline LaTeX.
79 79 encode : bool, optional
80 80 Should the PNG data bebase64 encoded to make it JSON'able.
81 81 backend : {matplotlib, dvipng}
82 82 Backend for producing PNG data.
83 83 wrap : bool
84 84 If true, Automatically wrap `s` as a LaTeX equation.
85 85
86 86 None is returned when the backend cannot be used.
87 87
88 88 """
89 89 allowed_backends = LaTeXTool.instance().backends
90 90 if backend is None:
91 91 backend = allowed_backends[0]
92 92 if backend not in allowed_backends:
93 93 return None
94 94 if backend == 'matplotlib':
95 95 f = latex_to_png_mpl
96 96 elif backend == 'dvipng':
97 97 f = latex_to_png_dvipng
98 98 else:
99 99 raise ValueError('No such backend {0}'.format(backend))
100 100 bin_data = f(s, wrap)
101 101 if encode and bin_data:
102 102 bin_data = encodestring(bin_data)
103 103 return bin_data
104 104
105 105
106 106 def latex_to_png_mpl(s, wrap):
107 107 try:
108 108 from matplotlib import mathtext
109 109 except ImportError:
110 110 return None
111 111
112 112 if wrap:
113 113 s = '${0}$'.format(s)
114 114 mt = mathtext.MathTextParser('bitmap')
115 115 f = BytesIO()
116 116 mt.to_png(f, s, fontsize=12)
117 117 return f.getvalue()
118 118
119 119
120 120 def latex_to_png_dvipng(s, wrap):
121 121 try:
122 122 find_cmd('latex')
123 123 find_cmd('dvipng')
124 124 except FindCmdError:
125 125 return None
126 126 try:
127 127 workdir = tempfile.mkdtemp()
128 128 tmpfile = os.path.join(workdir, "tmp.tex")
129 129 dvifile = os.path.join(workdir, "tmp.dvi")
130 130 outfile = os.path.join(workdir, "tmp.png")
131 131
132 132 with open(tmpfile, "w") as f:
133 133 f.writelines(genelatex(s, wrap))
134 134
135 135 with open(os.devnull, 'w') as devnull:
136 136 subprocess.check_call(
137 137 ["latex", "-halt-on-error", tmpfile], cwd=workdir,
138 138 stdout=devnull, stderr=devnull)
139 139
140 140 subprocess.check_call(
141 141 ["dvipng", "-T", "tight", "-x", "1500", "-z", "9",
142 142 "-bg", "transparent", "-o", outfile, dvifile], cwd=workdir,
143 143 stdout=devnull, stderr=devnull)
144 144
145 145 with open(outfile, "rb") as f:
146 bin_data = f.read()
146 return f.read()
147 147 finally:
148 148 shutil.rmtree(workdir)
149 return bin_data
150 149
151 150
152 151 def kpsewhich(filename):
153 152 """Invoke kpsewhich command with an argument `filename`."""
154 153 try:
155 154 find_cmd("kpsewhich")
156 155 proc = subprocess.Popen(
157 156 ["kpsewhich", filename],
158 157 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
159 158 (stdout, stderr) = proc.communicate()
160 159 return stdout.strip()
161 160 except FindCmdError:
162 161 pass
163 162
164 163
165 164 def genelatex(body, wrap):
166 165 """Generate LaTeX document for dvipng backend."""
167 166 lt = LaTeXTool.instance()
168 167 breqn = wrap and lt.use_breqn and kpsewhich("breqn.sty")
169 168 yield r'\documentclass{article}'
170 169 packages = lt.packages
171 170 if breqn:
172 171 packages = packages + ['breqn']
173 172 for pack in packages:
174 173 yield r'\usepackage{{{0}}}'.format(pack)
175 174 yield r'\pagestyle{empty}'
176 175 if lt.preamble:
177 176 yield lt.preamble
178 177 yield r'\begin{document}'
179 178 if breqn:
180 179 yield r'\begin{dmath*}'
181 180 yield body
182 181 yield r'\end{dmath*}'
183 182 elif wrap:
184 183 yield '$${0}$$'.format(body)
185 184 else:
186 185 yield body
187 186 yield r'\end{document}'
188 187
189 188
190 189 _data_uri_template_png = """<img src="data:image/png;base64,%s" alt=%s />"""
191 190
192 191 def latex_to_html(s, alt='image'):
193 192 """Render LaTeX to HTML with embedded PNG data using data URIs.
194 193
195 194 Parameters
196 195 ----------
197 196 s : str
198 197 The raw string containing valid inline LateX.
199 198 alt : str
200 199 The alt text to use for the HTML.
201 200 """
202 201 base64_data = bytes_to_str(latex_to_png(s, encode=True), 'ascii')
203 202 if base64_data:
204 203 return _data_uri_template_png % (base64_data, alt)
205 204
206 205
207 206 # From matplotlib, thanks to mdboom. Once this is in matplotlib releases, we
208 207 # will remove.
209 208 def math_to_image(s, filename_or_obj, prop=None, dpi=None, format=None):
210 209 """
211 210 Given a math expression, renders it in a closely-clipped bounding
212 211 box to an image file.
213 212
214 213 *s*
215 214 A math expression. The math portion should be enclosed in
216 215 dollar signs.
217 216
218 217 *filename_or_obj*
219 218 A filepath or writable file-like object to write the image data
220 219 to.
221 220
222 221 *prop*
223 222 If provided, a FontProperties() object describing the size and
224 223 style of the text.
225 224
226 225 *dpi*
227 226 Override the output dpi, otherwise use the default associated
228 227 with the output format.
229 228
230 229 *format*
231 230 The output format, eg. 'svg', 'pdf', 'ps' or 'png'. If not
232 231 provided, will be deduced from the filename.
233 232 """
234 233 from matplotlib import figure
235 234 # backend_agg supports all of the core output formats
236 235 from matplotlib.backends import backend_agg
237 236 from matplotlib.font_manager import FontProperties
238 237 from matplotlib.mathtext import MathTextParser
239 238
240 239 if prop is None:
241 240 prop = FontProperties()
242 241
243 242 parser = MathTextParser('path')
244 243 width, height, depth, _, _ = parser.parse(s, dpi=72, prop=prop)
245 244
246 245 fig = figure.Figure(figsize=(width / 72.0, height / 72.0))
247 246 fig.text(0, depth/height, s, fontproperties=prop)
248 247 backend_agg.FigureCanvasAgg(fig)
249 248 fig.savefig(filename_or_obj, dpi=dpi, format=format)
250 249
251 250 return depth
252 251
General Comments 0
You need to be logged in to leave comments. Login now