##// END OF EJS Templates
Reset the interactive namespace __warningregistry__ before executing code...
Reset the interactive namespace __warningregistry__ before executing code Fixes #6611. Idea: Right now, people often don't see important warnings when running code in IPython, because (to a first approximation) any given warning will only issue once per session. Blink and you'll miss it! This is a very common contributor to confused emails to numpy-discussion. E.g.: In [5]: 1 / my_array_with_random_contents /home/njs/.user-python2.7-64bit-3/bin/ipython:1: RuntimeWarning: divide by zero encountered in divide #!/home/njs/.user-python2.7-64bit-3/bin/python Out[5]: array([ 1.77073316, -2.29765021, -2.01800811, ..., 1.13871243, -1.08302964, -8.6185091 ]) Oo, right, guess I gotta be careful of those zeros -- thanks, numpy, for giving me that warning! A few days later: In [592]: 1 / some_other_array Out[592]: array([ 3.07735763, 0.50769289, 0.83984078, ..., -0.67563917, -0.85736257, -1.36511271]) Oops, it turns out that this array had a zero in it too, and that's going to bite me later. But no warning this time! The effect of this commit is to make it so that warnings triggered by the code in cell 5 do *not* suppress warnings triggered by the code in cell 592. Note that this only applies to warnings triggered *directly* by code entered interactively -- if somepkg.foo() calls anotherpkg.bad_func() which issues a warning, then this warning will still only be displayed once, even if multiple cells call somepkg.foo(). But if cell 5 and cell 592 both call anotherpkg.bad_func() directly, then both will get warnings. (Important exception: if foo() is defined *interactively*, and calls anotherpkg.bad_func(), then every cell that calls foo() will display the warning again. This is unavoidable without fixes to CPython upstream.) Explanation: Python's warning system has some weird quirks. By default, it tries to suppress duplicate warnings, where "duplicate" means the same warning message triggered twice by the same line of code. This requires determining which line of code is responsible for triggering a warning, and this is controlled by the stacklevel= argument to warnings.warn. Basically, though, the idea is that if foo() calls bar() which calls baz() which calls some_deprecated_api(), then baz() will get counted as being "responsible", and the warning system will make a note that the usage of some_deprecated_api() inside baz() has already been warned about and doesn't need to be warned about again. So far so good. To accomplish this, obviously, there has to be a record of somewhere which line this was. You might think that this would be done by recording the filename:linenumber pair in a dict inside the warnings module, or something like that. You would be wrong. What actually happens is that the warnings module will use stack introspection to reach into baz()'s execution environment, create a global (module-level) variable there named __warningregistry__, and then, inside this dictionary, record just the line number. Basically, it assumes that any given module contains only one line 1, only one line 2, etc., so storing the filename is irrelevant. Obviously for interactive code this is totally wrong -- all cells share the same execution environment and global namespace, and they all contain a new line 1. Currently the warnings module treats these as if they were all the same line. In fact they are not the same line; once we have executed a given chunk of code, we will never see those particular lines again. As soon as a given chunk of code finishes executing, its line number labels become meaningless, and the corresponding warning registry entries become meaningless as well. Therefore, with this patch we delete the __warningregistry__ each time we execute a new block of code.

File last commit:

r16265:500d6fe8
r18548:61431d7d
Show More
pdf.py
141 lines | 5.2 KiB | text/x-python | PythonLexer
MinRK
remove PDF post processor
r16418 """Export to PDF via latex"""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
MinRK
add PDFExporter...
r16265
import subprocess
import os
import sys
MinRK
remove PDF post processor
r16418 from IPython.utils.traitlets import Integer, List, Bool, Instance
from IPython.utils.tempdir import TemporaryWorkingDirectory
from .latex import LatexExporter
MinRK
add PDFExporter...
r16265
MinRK
remove PDF post processor
r16418 class PDFExporter(LatexExporter):
MinRK
add PDFExporter...
r16265 """Writer designed to write to PDF files"""
MinRK
remove PDF post processor
r16418 latex_count = Integer(3, config=True,
help="How many times latex will be called."
)
MinRK
add PDFExporter...
r16265
MinRK
remove PDF post processor
r16418 latex_command = List([u"pdflatex", u"{filename}"], config=True,
help="Shell command used to compile latex."
)
MinRK
add PDFExporter...
r16265
MinRK
remove PDF post processor
r16418 bib_command = List([u"bibtex", u"{filename}"], config=True,
help="Shell command used to run bibtex."
)
MinRK
add PDFExporter...
r16265
MinRK
remove PDF post processor
r16418 verbose = Bool(False, config=True,
help="Whether to display the output of latex commands."
)
MinRK
add PDFExporter...
r16265
MinRK
remove PDF post processor
r16418 temp_file_exts = List(['.aux', '.bbl', '.blg', '.idx', '.log', '.out'], config=True,
help="File extensions of temp files to remove after running."
)
writer = Instance("IPython.nbconvert.writers.FilesWriter", args=())
MinRK
add PDFExporter...
r16265
def run_command(self, command_list, filename, count, log_function):
"""Run command_list count times.
Parameters
----------
command_list : list
A list of args to provide to Popen. Each element of this
list will be interpolated with the filename to convert.
filename : unicode
The name of the file to convert.
count : int
How many times to run the command.
Returns
-------
MinRK
remove PDF post processor
r16418 success : bool
MinRK
add PDFExporter...
r16265 A boolean indicating if the command was successful (True)
or failed (False).
"""
command = [c.format(filename=filename) for c in command_list]
#In windows and python 2.x there is a bug in subprocess.Popen and
# unicode commands are not supported
if sys.platform == 'win32' and sys.version_info < (3,0):
#We must use cp1252 encoding for calling subprocess.Popen
#Note that sys.stdin.encoding and encoding.DEFAULT_ENCODING
# could be different (cp437 in case of dos console)
command = [c.encode('cp1252') for c in command]
times = 'time' if count == 1 else 'times'
self.log.info("Running %s %i %s: %s", command_list[0], count, times, command)
with open(os.devnull, 'rb') as null:
stdout = subprocess.PIPE if not self.verbose else None
for index in range(count):
p = subprocess.Popen(command, stdout=stdout, stdin=null)
out, err = p.communicate()
if p.returncode:
if self.verbose:
# verbose means I didn't capture stdout with PIPE,
# so it's already been displayed and `out` is None.
out = u''
else:
out = out.decode('utf-8', 'replace')
log_function(command, out)
return False # failure
return True # success
def run_latex(self, filename):
"""Run pdflatex self.latex_count times."""
def log_error(command, out):
self.log.critical(u"%s failed: %s\n%s", command[0], command, out)
return self.run_command(self.latex_command, filename,
self.latex_count, log_error)
def run_bib(self, filename):
"""Run bibtex self.latex_count times."""
filename = os.path.splitext(filename)[0]
def log_error(command, out):
self.log.warn('%s had problems, most likely because there were no citations',
command[0])
self.log.debug(u"%s output: %s\n%s", command[0], command, out)
return self.run_command(self.bib_command, filename, 1, log_error)
def clean_temp_files(self, filename):
"""Remove temporary files created by pdflatex/bibtex."""
self.log.info("Removing temporary LaTeX files")
filename = os.path.splitext(filename)[0]
for ext in self.temp_file_exts:
try:
os.remove(filename+ext)
except OSError:
pass
MinRK
remove PDF post processor
r16418 def from_notebook_node(self, nb, resources=None, **kw):
latex, resources = super(PDFExporter, self).from_notebook_node(
nb, resources=resources, **kw
)
with TemporaryWorkingDirectory() as td:
notebook_name = "notebook"
tex_file = self.writer.write(latex, resources, notebook_name=notebook_name)
self.log.info("Building PDF")
rc = self.run_latex(tex_file)
if not rc:
rc = self.run_bib(tex_file)
if not rc:
rc = self.run_latex(tex_file)
pdf_file = notebook_name + '.pdf'
if not os.path.isfile(pdf_file):
raise RuntimeError("PDF creating failed")
MinRK
add PDFExporter...
r16265 self.log.info('PDF successfully created')
MinRK
remove PDF post processor
r16418 with open(pdf_file, 'rb') as f:
pdf_data = f.read()
# convert output extension to pdf
# the writer above required it to be tex
resources['output_extension'] = 'pdf'
return pdf_data, resources