From 1a6ac2e5d1f0984d1d09f755b1437222ffca6ff7 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Sun, 13 Mar 2016 21:20:01 -0400 Subject: [PATCH 01/11] FIX: Allow unicode in code and outputs --- sphinx_gallery/gen_rst.py | 35 +++++++++++++++++----------- sphinx_gallery/tests/test_gen_rst.py | 26 ++++++++++----------- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/sphinx_gallery/gen_rst.py b/sphinx_gallery/gen_rst.py index 5e88858f4..4c62eee6b 100644 --- a/sphinx_gallery/gen_rst.py +++ b/sphinx_gallery/gen_rst.py @@ -49,9 +49,9 @@ def prefixed_lines(): return ''.join(prefixed_lines()) try: - from StringIO import StringIO + from BytesIO import BytesIO except ImportError: - from io import StringIO + from io import BytesIO try: # make sure that the Agg backend is set before importing any @@ -93,6 +93,15 @@ def flush(self): self.file2.flush() +class MyBytesIO(BytesIO): + """Wrapper class to act like StringIO while writing unicode strings""" + def write(self, data): + return super(MyBytesIO, self).write(data.encode('utf-8')) + + def getvalue(self): + return super(MyBytesIO, self).getvalue().decode('utf-8') + + ############################################################################### CODE_DOWNLOAD = """**Total running time of the script:** ({0:.0f} minutes {1:.3f} seconds)\n\n @@ -124,7 +133,7 @@ def flush(self): """ -CODE_OUTPUT = """.. rst-class:: sphx-glr-script-out +CODE_OUTPUT = u""".. rst-class:: sphx-glr-script-out Out:: @@ -143,7 +152,7 @@ def get_docstring_and_rest(filename): rest: str `filename` content without the docstring """ - with open(filename) as f: + with open(filename, 'rb') as f: content = f.read() node = ast.parse(content) @@ -156,7 +165,7 @@ def get_docstring_and_rest(filename): docstring = docstring_node.value.s # This get the content of the file after the docstring last line # Note: 'maxsplit' argument is not a keyword argument in python2 - rest = content.split('\n', docstring_node.lineno)[-1] + rest = content.decode('utf-8').split('\n', docstring_node.lineno)[-1] return docstring, rest else: raise ValueError(('Could not find docstring in file "{0}". ' @@ -203,8 +212,8 @@ def split_code_and_text_blocks(source_file): def codestr2rst(codestr, lang='python'): """Return reStructuredText code block from code string""" - code_directive = "\n.. code-block:: {0}\n\n".format(lang) - indented_block = indent(codestr, ' ' * 4) + code_directive = u"\n.. code-block:: {0}\n\n".format(lang) + indented_block = indent(codestr, u' ' * 4) return code_directive + indented_block @@ -461,7 +470,7 @@ def execute_script(code_block, example_globals, image_path, fig_count, # First cd in the original example dir, so that any file # created by the example get created in this directory os.chdir(os.path.dirname(src_file)) - my_buffer = StringIO() + my_buffer = MyBytesIO() my_stdout = Tee(sys.stdout, my_buffer) sys.stdout = my_stdout @@ -473,7 +482,7 @@ def execute_script(code_block, example_globals, image_path, fig_count, my_stdout = my_buffer.getvalue().strip().expandtabs() if my_stdout: - stdout = CODE_OUTPUT.format(indent(my_stdout, ' ' * 4)) + stdout = CODE_OUTPUT.format(indent(my_stdout, u' ' * 4)) os.chdir(cwd) figure_list = save_figures(image_path, fig_count, gallery_conf) @@ -514,7 +523,7 @@ def execute_script(code_block, example_globals, image_path, fig_count, sys.stdout = orig_stdout print(" - time elapsed : %.2g sec" % time_elapsed) - code_output = "\n{0}\n\n{1}\n\n".format(image_list, stdout) + code_output = u"\n{0}\n\n{1}\n\n".format(image_list, stdout) return code_output, time_elapsed, fig_count + len(figure_list) @@ -550,7 +559,7 @@ def generate_file_rst(fname, target_dir, src_dir, gallery_conf): time_elapsed = 0 ref_fname = example_file.replace(os.path.sep, '_') - example_rst = """\n\n.. _sphx_glr_{0}:\n\n""".format(ref_fname) + example_rst = u"""\n\n.. _sphx_glr_{0}:\n\n""".format(ref_fname) example_nb = Notebook(fname, target_dir) filename_pattern = gallery_conf.get('filename_pattern') @@ -609,9 +618,9 @@ def generate_file_rst(fname, target_dir, src_dir, gallery_conf): time_m, time_s = divmod(time_elapsed, 60) example_nb.save_file() - with open(os.path.join(target_dir, base_image_name + '.rst'), 'w') as f: + with open(os.path.join(target_dir, base_image_name + '.rst'), 'wb') as f: example_rst += CODE_DOWNLOAD.format(time_m, time_s, fname, example_nb.file_name) - f.write(example_rst) + f.write(example_rst.encode('utf-8')) return amount_of_code, time_elapsed diff --git a/sphinx_gallery/tests/test_gen_rst.py b/sphinx_gallery/tests/test_gen_rst.py index 0339a5c6a..bc8b6e831 100644 --- a/sphinx_gallery/tests/test_gen_rst.py +++ b/sphinx_gallery/tests/test_gen_rst.py @@ -28,7 +28,7 @@ '', '# and now comes the module code', 'x, y = 1, 2', - 'print("'"output"'") # need some code output'] + 'print(u"' u"Óscar output" '") # need some code output'] def test_split_code_and_text_blocks(): @@ -84,17 +84,17 @@ def test_codestr2rst(): def test_extract_intro(): - with tempfile.NamedTemporaryFile('w') as f: - f.write('\n'.join(CONTENT)) + with tempfile.NamedTemporaryFile('wb') as f: + f.write(u'\n'.join(CONTENT).encode('utf-8')) f.flush() result = sg.extract_intro(f.name) - assert_false('Docstring' in result) - assert_equal( - result, - 'This is the description of the example which goes on and on') - assert_false('second paragraph' in result) + assert_false('Docstring' in result) + assert_equal( + result, + 'This is the description of the example which goes on and on') + assert_false('second paragraph' in result) def test_md5sums(): @@ -129,19 +129,19 @@ def test_pattern_matching(): 'reference_url': {}, } - code_output = '\n Out::\n\n output\n\n' + code_output = u'\n Out::\n\n Óscar output\n\n' # create three files in tempdir (only one matches the pattern) fnames = ['plot_0.py', 'plot_1.py', 'plot_2.py'] for fname in fnames: - with open(os.path.join(examples_dir, fname), 'w') as f: - f.write('\n'.join(CONTENT)) + with open(os.path.join(examples_dir, fname), 'wb') as f: + f.write('\n'.join(CONTENT).encode('utf-8')) f.flush() # generate rst file sg.generate_file_rst(fname, gallery_dir, examples_dir, gallery_conf) # read rst file and check if it contains code output rst_fname = os.path.splitext(fname)[0] + '.rst' - with open(os.path.join(gallery_dir, rst_fname), 'r') as f: - rst = f.read() + with open(os.path.join(gallery_dir, rst_fname), 'rb') as f: + rst = f.read().decode('utf-8') if re.search(gallery_conf['filename_pattern'], os.path.join(gallery_dir, rst_fname)): assert_true(code_output in rst) From a910e55e8e5cd50afae2053a84a49ed928b92524 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Sun, 13 Mar 2016 22:05:26 -0400 Subject: [PATCH 02/11] FIX: Fix docstring unicode --- sphinx_gallery/gen_rst.py | 2 ++ sphinx_gallery/tests/test_gen_rst.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/sphinx_gallery/gen_rst.py b/sphinx_gallery/gen_rst.py index 4c62eee6b..20a0ebcee 100644 --- a/sphinx_gallery/gen_rst.py +++ b/sphinx_gallery/gen_rst.py @@ -163,6 +163,8 @@ def get_docstring_and_rest(filename): isinstance(node.body[0].value, ast.Str): docstring_node = node.body[0] docstring = docstring_node.value.s + if hasattr(docstring, 'decode'): # python2.7 + docstring = docstring.decode('utf-8') # This get the content of the file after the docstring last line # Note: 'maxsplit' argument is not a keyword argument in python2 rest = content.decode('utf-8').split('\n', docstring_node.lineno)[-1] diff --git a/sphinx_gallery/tests/test_gen_rst.py b/sphinx_gallery/tests/test_gen_rst.py index bc8b6e831..57693e5e1 100644 --- a/sphinx_gallery/tests/test_gen_rst.py +++ b/sphinx_gallery/tests/test_gen_rst.py @@ -20,7 +20,7 @@ '================', '', 'This is the description of the example', - 'which goes on and on', + u'which goes on and on, Óscar', '', '', 'And this is a second paragraph', @@ -93,7 +93,7 @@ def test_extract_intro(): assert_false('Docstring' in result) assert_equal( result, - 'This is the description of the example which goes on and on') + u'This is the description of the example which goes on and on, Óscar') assert_false('second paragraph' in result) From 7952f21b1ffbaf23fdc546af2bf99b69125077fc Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Sun, 13 Mar 2016 22:46:42 -0400 Subject: [PATCH 03/11] FIX: Better logging handling --- sphinx_gallery/gen_rst.py | 21 +++++++-------------- sphinx_gallery/tests/test_gen_rst.py | 16 ++++++++++++++-- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/sphinx_gallery/gen_rst.py b/sphinx_gallery/gen_rst.py index 20a0ebcee..c2f6a6d1b 100644 --- a/sphinx_gallery/gen_rst.py +++ b/sphinx_gallery/gen_rst.py @@ -49,9 +49,9 @@ def prefixed_lines(): return ''.join(prefixed_lines()) try: - from BytesIO import BytesIO + from StringIO import StringIO except ImportError: - from io import BytesIO + from io import StringIO try: # make sure that the Agg backend is set before importing any @@ -72,6 +72,7 @@ def prefixed_lines(): basestring except NameError: basestring = str + unicode = str ############################################################################### @@ -86,22 +87,13 @@ def __init__(self, file1, file2): def write(self, data): self.file1.write(data) - self.file2.write(data) + self.file2.write(unicode(data)) # this is the StringIO def flush(self): self.file1.flush() self.file2.flush() -class MyBytesIO(BytesIO): - """Wrapper class to act like StringIO while writing unicode strings""" - def write(self, data): - return super(MyBytesIO, self).write(data.encode('utf-8')) - - def getvalue(self): - return super(MyBytesIO, self).getvalue().decode('utf-8') - - ############################################################################### CODE_DOWNLOAD = """**Total running time of the script:** ({0:.0f} minutes {1:.3f} seconds)\n\n @@ -472,7 +464,7 @@ def execute_script(code_block, example_globals, image_path, fig_count, # First cd in the original example dir, so that any file # created by the example get created in this directory os.chdir(os.path.dirname(src_file)) - my_buffer = MyBytesIO() + my_buffer = StringIO() my_stdout = Tee(sys.stdout, my_buffer) sys.stdout = my_stdout @@ -502,6 +494,7 @@ def execute_script(code_block, example_globals, image_path, fig_count, except Exception: formatted_exception = traceback.format_exc() + sys.stdout = orig_stdout # need this here so these lines don't bomb print(80 * '_') print('%s is not compiling:' % src_file) print(formatted_exception) @@ -521,8 +514,8 @@ def execute_script(code_block, example_globals, image_path, fig_count, raise finally: - os.chdir(cwd) sys.stdout = orig_stdout + os.chdir(cwd) print(" - time elapsed : %.2g sec" % time_elapsed) code_output = u"\n{0}\n\n{1}\n\n".format(image_list, stdout) diff --git a/sphinx_gallery/tests/test_gen_rst.py b/sphinx_gallery/tests/test_gen_rst.py index 57693e5e1..8cf30457a 100644 --- a/sphinx_gallery/tests/test_gen_rst.py +++ b/sphinx_gallery/tests/test_gen_rst.py @@ -27,8 +27,17 @@ '"""', '', '# and now comes the module code', + 'import logging', + 'import sys', 'x, y = 1, 2', - 'print(u"' u"Óscar output" '") # need some code output'] + 'print(u"' u"Óscar output" '") # need some code output', + 'logger = logging.getLogger()', + 'logger.setLevel(logging.INFO)', + 'lh = logging.StreamHandler(sys.stdout)', + 'lh.setFormatter(logging.Formatter("log:%(message)s"))', + 'logger.addHandler(lh)', + 'logger.info(u"' u"Óscar" '")', + ] def test_split_code_and_text_blocks(): @@ -129,7 +138,7 @@ def test_pattern_matching(): 'reference_url': {}, } - code_output = u'\n Out::\n\n Óscar output\n\n' + code_output = u'\n Out::\n\n Óscar output\n log:Óscar\n\n' # create three files in tempdir (only one matches the pattern) fnames = ['plot_0.py', 'plot_1.py', 'plot_2.py'] for fname in fnames: @@ -144,6 +153,9 @@ def test_pattern_matching(): rst = f.read().decode('utf-8') if re.search(gallery_conf['filename_pattern'], os.path.join(gallery_dir, rst_fname)): + print(code_output) + print('') + print(rst) assert_true(code_output in rst) else: assert_false(code_output in rst) From 2b53be782e2cc582c8084a38778fe9d1ae0c3a47 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Mon, 14 Mar 2016 11:35:47 -0400 Subject: [PATCH 04/11] FIX: Minor fixes --- sphinx_gallery/gen_rst.py | 44 +++++++++++++++++++-------- sphinx_gallery/tests/test_gen_rst.py | 45 ++++++++++++++-------------- 2 files changed, 53 insertions(+), 36 deletions(-) diff --git a/sphinx_gallery/gen_rst.py b/sphinx_gallery/gen_rst.py index c2f6a6d1b..64f7883fb 100644 --- a/sphinx_gallery/gen_rst.py +++ b/sphinx_gallery/gen_rst.py @@ -12,9 +12,11 @@ Files that generate images should start with 'plot' """ -from __future__ import division, print_function, absolute_import +from __future__ import (division, print_function, absolute_import, + unicode_literals) from time import time import ast +import codecs import hashlib import os import re @@ -49,9 +51,9 @@ def prefixed_lines(): return ''.join(prefixed_lines()) try: - from StringIO import StringIO + from BytesIO import BytesIO except ImportError: - from io import StringIO + from io import BytesIO try: # make sure that the Agg backend is set before importing any @@ -87,13 +89,25 @@ def __init__(self, file1, file2): def write(self, data): self.file1.write(data) - self.file2.write(unicode(data)) # this is the StringIO + self.file2.write(data) def flush(self): self.file1.flush() self.file2.flush() +class MyBytesIO(BytesIO): + """Helper to deal with unicode not having buffer interface on Py2.7""" + def write(self, data): + # "unicode" objects don't have a buffer interface on Py2.7, + # so we need to manually convert + if isinstance(data, unicode): + data = data.encode('utf-8') + super(MyBytesIO, self).write(data) + + def getvalue(self): + return super(MyBytesIO, self).getvalue().decode('utf-8') + ############################################################################### CODE_DOWNLOAD = """**Total running time of the script:** ({0:.0f} minutes {1:.3f} seconds)\n\n @@ -125,7 +139,7 @@ def flush(self): """ -CODE_OUTPUT = u""".. rst-class:: sphx-glr-script-out +CODE_OUTPUT = """.. rst-class:: sphx-glr-script-out Out:: @@ -144,6 +158,9 @@ def get_docstring_and_rest(filename): rest: str `filename` content without the docstring """ + # can't use codecs.open(filename, 'r', 'utf-8') here b/c ast doesn't + # seem to work with unicode strings in Python2.7 + # "SyntaxError: encoding declaration in Unicode string" with open(filename, 'rb') as f: content = f.read() @@ -206,8 +223,8 @@ def split_code_and_text_blocks(source_file): def codestr2rst(codestr, lang='python'): """Return reStructuredText code block from code string""" - code_directive = u"\n.. code-block:: {0}\n\n".format(lang) - indented_block = indent(codestr, u' ' * 4) + code_directive = "\n.. code-block:: {0}\n\n".format(lang) + indented_block = indent(codestr, ' ' * 4) return code_directive + indented_block @@ -464,7 +481,7 @@ def execute_script(code_block, example_globals, image_path, fig_count, # First cd in the original example dir, so that any file # created by the example get created in this directory os.chdir(os.path.dirname(src_file)) - my_buffer = StringIO() + my_buffer = MyBytesIO() my_stdout = Tee(sys.stdout, my_buffer) sys.stdout = my_stdout @@ -476,7 +493,7 @@ def execute_script(code_block, example_globals, image_path, fig_count, my_stdout = my_buffer.getvalue().strip().expandtabs() if my_stdout: - stdout = CODE_OUTPUT.format(indent(my_stdout, u' ' * 4)) + stdout = CODE_OUTPUT.format(indent(my_stdout, ' ' * 4)) os.chdir(cwd) figure_list = save_figures(image_path, fig_count, gallery_conf) @@ -518,7 +535,7 @@ def execute_script(code_block, example_globals, image_path, fig_count, os.chdir(cwd) print(" - time elapsed : %.2g sec" % time_elapsed) - code_output = u"\n{0}\n\n{1}\n\n".format(image_list, stdout) + code_output = "\n{0}\n\n{1}\n\n".format(image_list, stdout) return code_output, time_elapsed, fig_count + len(figure_list) @@ -554,7 +571,7 @@ def generate_file_rst(fname, target_dir, src_dir, gallery_conf): time_elapsed = 0 ref_fname = example_file.replace(os.path.sep, '_') - example_rst = u"""\n\n.. _sphx_glr_{0}:\n\n""".format(ref_fname) + example_rst = """\n\n.. _sphx_glr_{0}:\n\n""".format(ref_fname) example_nb = Notebook(fname, target_dir) filename_pattern = gallery_conf.get('filename_pattern') @@ -613,9 +630,10 @@ def generate_file_rst(fname, target_dir, src_dir, gallery_conf): time_m, time_s = divmod(time_elapsed, 60) example_nb.save_file() - with open(os.path.join(target_dir, base_image_name + '.rst'), 'wb') as f: + with codecs.open(os.path.join(target_dir, base_image_name + '.rst'), + 'w', 'utf-8') as f: example_rst += CODE_DOWNLOAD.format(time_m, time_s, fname, example_nb.file_name) - f.write(example_rst.encode('utf-8')) + f.write(example_rst) return amount_of_code, time_elapsed diff --git a/sphinx_gallery/tests/test_gen_rst.py b/sphinx_gallery/tests/test_gen_rst.py index 8cf30457a..74f9bfa30 100644 --- a/sphinx_gallery/tests/test_gen_rst.py +++ b/sphinx_gallery/tests/test_gen_rst.py @@ -4,8 +4,10 @@ """ Testing the rst files generator """ -from __future__ import division, absolute_import, print_function +from __future__ import (division, absolute_import, print_function, + unicode_literals) import ast +import codecs import json import tempfile import re @@ -20,7 +22,7 @@ '================', '', 'This is the description of the example', - u'which goes on and on, Óscar', + 'which goes on and on, Óscar', '', '', 'And this is a second paragraph', @@ -30,13 +32,13 @@ 'import logging', 'import sys', 'x, y = 1, 2', - 'print(u"' u"Óscar output" '") # need some code output', + 'print(u"Óscar output") # need some code output', 'logger = logging.getLogger()', 'logger.setLevel(logging.INFO)', 'lh = logging.StreamHandler(sys.stdout)', 'lh.setFormatter(logging.Formatter("log:%(message)s"))', 'logger.addHandler(lh)', - 'logger.info(u"' u"Óscar" '")', + 'logger.info(u"Óscar")', ] @@ -70,7 +72,6 @@ def test_direct_comment_after_docstring(): 'x, y = 1, 2', ''])) f.flush() - result = sg.split_code_and_text_blocks(f.name) expected_result = [ @@ -94,15 +95,13 @@ def test_codestr2rst(): def test_extract_intro(): with tempfile.NamedTemporaryFile('wb') as f: - f.write(u'\n'.join(CONTENT).encode('utf-8')) - + f.write('\n'.join(CONTENT).encode('utf-8')) f.flush() - result = sg.extract_intro(f.name) assert_false('Docstring' in result) assert_equal( result, - u'This is the description of the example which goes on and on, Óscar') + 'This is the description of the example which goes on and on, Óscar') assert_false('second paragraph' in result) @@ -138,27 +137,27 @@ def test_pattern_matching(): 'reference_url': {}, } - code_output = u'\n Out::\n\n Óscar output\n log:Óscar\n\n' + code_output = '\n Out::\n\n Óscar output\n log:Óscar\n\n' # create three files in tempdir (only one matches the pattern) fnames = ['plot_0.py', 'plot_1.py', 'plot_2.py'] for fname in fnames: - with open(os.path.join(examples_dir, fname), 'wb') as f: - f.write('\n'.join(CONTENT).encode('utf-8')) - f.flush() + with codecs.open(os.path.join(examples_dir, fname), 'w', 'utf-8') as f: + f.write('\n'.join(CONTENT)) # generate rst file sg.generate_file_rst(fname, gallery_dir, examples_dir, gallery_conf) # read rst file and check if it contains code output rst_fname = os.path.splitext(fname)[0] + '.rst' - with open(os.path.join(gallery_dir, rst_fname), 'rb') as f: - rst = f.read().decode('utf-8') - if re.search(gallery_conf['filename_pattern'], - os.path.join(gallery_dir, rst_fname)): - print(code_output) - print('') - print(rst) - assert_true(code_output in rst) - else: - assert_false(code_output in rst) + with codecs.open(os.path.join(gallery_dir, rst_fname), + 'r', 'utf-8') as f: + rst = f.read() + if re.search(gallery_conf['filename_pattern'], + os.path.join(gallery_dir, rst_fname)): + print(code_output) + print('') + print(rst) + assert_true(code_output in rst) + else: + assert_false(code_output in rst) def test_ipy_notebook(): From 7aa20a83199f2a9598bfc75b549a0b4684a09322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20N=C3=A1jera?= Date: Tue, 15 Mar 2016 00:32:03 +0100 Subject: [PATCH 05/11] Quantum mechanics example that fail unicode --- examples/plot_quantum.py | 52 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 examples/plot_quantum.py diff --git a/examples/plot_quantum.py b/examples/plot_quantum.py new file mode 100644 index 000000000..0b4609be5 --- /dev/null +++ b/examples/plot_quantum.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +r""" +====================== +Some Quantum Mechanics +====================== + +We start with a two spin system :math:`\uparrow` and :math:`\downarrow` + +""" +# Author: Óscar Nájera + +from __future__ import division, absolute_import, print_function + + +def hamiltonian(M, mu): + r"""Generate a single orbital isolated atom Hamiltonian in particle-hole + symmetry. Include chemical potential for grand Canonical calculations + + .. math:: + \mathcal{H} - \mu N = M(n_\uparrow - n_\downarrow) + - \mu(n_\uparrow + n_\downarrow) + """ + pass + +############################################################################### +# Double occupation +# ----------------- +# +# To find out the double occupation one uses the relation +# (Works in Sphinx-Gallery) + +import matplotlib.pylab as plt +import numpy as np +x = np.linspace(0, 1, 20) +plt.plot(x, (1 - x**2) / 4) +plt.ylabel('$\\langle n_\\uparrow n_\\downarrow \\rangle$') +plt.show() + +############################################################################### +# +# .. math:: \langle n_\uparrow n_\downarrow \rangle = \frac{2\langle V \rangle}{U}+\frac{1}{4} + +print('pass') + +############################################################################### +# (Does not work in Sphinx-Gallery) +# --------------------------------- + +plt.plot(x, (1 - x) / 4) +plt.ylabel(r'$\langle n_\uparrow n_\downarrow \rangle$') +plt.xlabel('U') +plt.show() From e7a7bd49b41b8fa7958544bb740423194b3669a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20N=C3=A1jera?= Date: Tue, 15 Mar 2016 00:48:00 +0100 Subject: [PATCH 06/11] Pyface pip update to pass travis Mayavi build The conda virtualenv for Mayavi forces a version of pyface that clashes with sphinx. The manual update of pyface within conda is no longer enough to update to a new version that does not clash with sphinx. Thus the update is forced through pip. Mayavi is an experimentally supported use case of Sphinx-Gallery --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 59ca464fe..943cfde58 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,7 +39,7 @@ install: if [ "$PYTHON_VERSION" == "2.7" ]; then conda install --yes --quiet mayavi; conda upgrade --yes --all; - conda upgrade --yes pyface; + pip install --upgrade pyface; fi; fi; - pip install -r requirements.txt From 78793ca80a423f3fd138da591a9f85da4c341d60 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 16 Mar 2016 10:45:25 -0400 Subject: [PATCH 07/11] FIX: Fix unicode --- examples/plot_quantum.py | 4 +- sphinx_gallery/gen_rst.py | 16 +++++--- sphinx_gallery/tests/test_gen_rst.py | 55 ++++++++++++++++------------ 3 files changed, 43 insertions(+), 32 deletions(-) diff --git a/examples/plot_quantum.py b/examples/plot_quantum.py index 0b4609be5..d5c9eb616 100644 --- a/examples/plot_quantum.py +++ b/examples/plot_quantum.py @@ -43,8 +43,8 @@ def hamiltonian(M, mu): print('pass') ############################################################################### -# (Does not work in Sphinx-Gallery) -# --------------------------------- +# And then: +# --------- plt.plot(x, (1 - x) / 4) plt.ylabel(r'$\langle n_\uparrow n_\downarrow \rangle$') diff --git a/sphinx_gallery/gen_rst.py b/sphinx_gallery/gen_rst.py index 64f7883fb..23c1dc5ae 100644 --- a/sphinx_gallery/gen_rst.py +++ b/sphinx_gallery/gen_rst.py @@ -12,8 +12,9 @@ Files that generate images should start with 'plot' """ -from __future__ import (division, print_function, absolute_import, - unicode_literals) +# Don't use unicode_literals here (be explicit with u"..." instead) otherwise +# tricky errors come up with exec(code_blocks, ...) calls +from __future__ import division, print_function, absolute_import from time import time import ast import codecs @@ -139,7 +140,8 @@ def getvalue(self): """ -CODE_OUTPUT = """.. rst-class:: sphx-glr-script-out +# This one could contain unicode +CODE_OUTPUT = u""".. rst-class:: sphx-glr-script-out Out:: @@ -486,6 +488,8 @@ def execute_script(code_block, example_globals, image_path, fig_count, sys.stdout = my_stdout t_start = time() + # don't use unicode_literals at the top of this file or you get + # nasty errors here on Py2.7 exec(code_block, example_globals) time_elapsed = time() - t_start @@ -493,7 +497,7 @@ def execute_script(code_block, example_globals, image_path, fig_count, my_stdout = my_buffer.getvalue().strip().expandtabs() if my_stdout: - stdout = CODE_OUTPUT.format(indent(my_stdout, ' ' * 4)) + stdout = CODE_OUTPUT.format(indent(my_stdout, u' ' * 4)) os.chdir(cwd) figure_list = save_figures(image_path, fig_count, gallery_conf) @@ -527,7 +531,7 @@ def execute_script(code_block, example_globals, image_path, fig_count, # Breaks build on first example error - if gallery_conf['abort_on_example_error']: + if gallery_conf.get('abort_on_example_error', True): raise finally: @@ -535,7 +539,7 @@ def execute_script(code_block, example_globals, image_path, fig_count, os.chdir(cwd) print(" - time elapsed : %.2g sec" % time_elapsed) - code_output = "\n{0}\n\n{1}\n\n".format(image_list, stdout) + code_output = u"\n{0}\n\n{1}\n\n".format(image_list, stdout) return code_output, time_elapsed, fig_count + len(figure_list) diff --git a/sphinx_gallery/tests/test_gen_rst.py b/sphinx_gallery/tests/test_gen_rst.py index 74f9bfa30..c67e6a2d0 100644 --- a/sphinx_gallery/tests/test_gen_rst.py +++ b/sphinx_gallery/tests/test_gen_rst.py @@ -17,29 +17,31 @@ from sphinx_gallery import notebook -CONTENT = ['"""' - 'Docstring header', - '================', - '', - 'This is the description of the example', - 'which goes on and on, Óscar', - '', - '', - 'And this is a second paragraph', - '"""', - '', - '# and now comes the module code', - 'import logging', - 'import sys', - 'x, y = 1, 2', - 'print(u"Óscar output") # need some code output', - 'logger = logging.getLogger()', - 'logger.setLevel(logging.INFO)', - 'lh = logging.StreamHandler(sys.stdout)', - 'lh.setFormatter(logging.Formatter("log:%(message)s"))', - 'logger.addHandler(lh)', - 'logger.info(u"Óscar")', - ] +CONTENT = [ + '"""' + 'Docstring header', + '================', + '', + 'This is the description of the example', + 'which goes on and on, Óscar', + '', + '', + 'And this is a second paragraph', + '"""', + '', + '# and now comes the module code', + 'import logging', + 'import sys', + 'x, y = 1, 2', + 'print(u"Óscar output") # need some code output', + 'logger = logging.getLogger()', + 'logger.setLevel(logging.INFO)', + 'lh = logging.StreamHandler(sys.stdout)', + 'lh.setFormatter(logging.Formatter("log:%(message)s"))', + 'logger.addHandler(lh)', + 'logger.info(u"Óscar")', + 'print(r"$\\langle n_\\uparrow n_\\downarrow \\rangle$")', +] def test_split_code_and_text_blocks(): @@ -137,7 +139,12 @@ def test_pattern_matching(): 'reference_url': {}, } - code_output = '\n Out::\n\n Óscar output\n log:Óscar\n\n' + code_output = ('\n Out::\n' + '\n' + ' Óscar output\n' + ' log:Óscar\n' + ' $\\langle n_\\uparrow n_\\downarrow \\rangle$\n\n' + ) # create three files in tempdir (only one matches the pattern) fnames = ['plot_0.py', 'plot_1.py', 'plot_2.py'] for fname in fnames: From 4852d903addd9a3cfa47ab5a31d335030fd3888b Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 16 Mar 2016 13:37:15 -0400 Subject: [PATCH 08/11] FIX: Fix comment --- sphinx_gallery/gen_rst.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx_gallery/gen_rst.py b/sphinx_gallery/gen_rst.py index 23c1dc5ae..63219acab 100644 --- a/sphinx_gallery/gen_rst.py +++ b/sphinx_gallery/gen_rst.py @@ -530,7 +530,7 @@ def execute_script(code_block, example_globals, image_path, fig_count, fig_count += 1 # raise count to avoid overwriting image # Breaks build on first example error - + # Eventually this should hopefully be unified with the default conf if gallery_conf.get('abort_on_example_error', True): raise From e8d9fedde5a629d39889fab32a1b8a87b4bbd95c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20N=C3=A1jera?= Date: Wed, 16 Mar 2016 19:41:45 +0100 Subject: [PATCH 09/11] More content relevant quantum example For the unicode testing purposes There was the need of an example breaking it. Having Latex with raw strings and the \u from \uparrow was a good test. --- examples/plot_quantum.py | 91 +++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 38 deletions(-) diff --git a/examples/plot_quantum.py b/examples/plot_quantum.py index d5c9eb616..527a64110 100644 --- a/examples/plot_quantum.py +++ b/examples/plot_quantum.py @@ -1,52 +1,67 @@ # -*- coding: utf-8 -*- r""" -====================== -Some Quantum Mechanics -====================== +================================================= +Some Quantum Mechanics, filling an atomic orbital +================================================= -We start with a two spin system :math:`\uparrow` and :math:`\downarrow` +Considering an atomic single orbital and how to fill it by use of the +chemical potential. This system has a four element basis, :math:`B = +\{ \lvert \emptyset \rangle, \lvert \uparrow \rangle, \lvert +\downarrow \rangle, \lvert \uparrow\downarrow \rangle \}`, that is the +empty orbital, one spin up electron, one spin down electron and the +filled orbital. -""" -# Author: Óscar Nájera - -from __future__ import division, absolute_import, print_function +The environment of the orbital is set up by an energy cost for +occupying the orbital, that is :math:`\epsilon` and when both +electrons meet a contact interaction corresponding to the Coulomb +repulsion :math:`U`. Finally the chemical potential :math:`\mu` is +what allows in the Grand canonical picture, to fill up our atomic +orbital from a reservoir of electrons. + The the simple Hamiltonian to model this system is given by: -def hamiltonian(M, mu): - r"""Generate a single orbital isolated atom Hamiltonian in particle-hole - symmetry. Include chemical potential for grand Canonical calculations +.. math:: + \mathcal{H} = + \sum_{\sigma=\uparrow,\downarrow} \epsilon c^\dagger_\sigma c_\sigma + + Un_\uparrow n_\downarrow - \mu \hat{N} - .. math:: - \mathcal{H} - \mu N = M(n_\uparrow - n_\downarrow) - - \mu(n_\uparrow + n_\downarrow) - """ - pass +Here :math:`c^\dagger,c` creation and annihilation operators, +:math:`n=c^\dagger c`, and +:math:`\hat{N}=n_\uparrow+n_\downarrow`. This Hamiltonian is diagonal +in the basis of particle number we have chosen earlier, as the basis +elements are also eigenvectors. -############################################################################### -# Double occupation -# ----------------- -# -# To find out the double occupation one uses the relation -# (Works in Sphinx-Gallery) +.. math:: + \mathcal{H} \lvert \emptyset \rangle &= 0 \\ + \mathcal{H} \lvert \uparrow \rangle &= (\epsilon - \mu) | \uparrow \rangle \\ + \mathcal{H} \lvert \downarrow \rangle &= (\epsilon - \mu) | \downarrow \rangle \\ + \mathcal{H} \lvert \uparrow\downarrow \rangle &= (2\epsilon - 2\mu +U) \lvert \uparrow\downarrow \rangle -import matplotlib.pylab as plt -import numpy as np -x = np.linspace(0, 1, 20) -plt.plot(x, (1 - x**2) / 4) -plt.ylabel('$\\langle n_\\uparrow n_\\downarrow \\rangle$') -plt.show() +It is easy to see, that the system will prefer to be empty if +:math:`\mu \in [0,\epsilon)`, be single occupied if :math:`\mu \in (\epsilon, \epsilon +U)` +and doubly occupied if :math:`\mu > \epsilon +U`. -############################################################################### -# -# .. math:: \langle n_\uparrow n_\downarrow \rangle = \frac{2\langle V \rangle}{U}+\frac{1}{4} +For a more rigorous treatment, the partition function has to be +calculated and then the expected particle number can be +found. Introducing a new variable :math:`\xi = \epsilon - \mu`, and +:math:`\beta` corresponding to the inverse temperature of the system. -print('pass') +.. math:: + \mathcal{Z} &= Tr(e^{-\beta \mathcal{H}}) = 1 + 2e^{-\beta\xi} + e^{-\beta(2\xi + U)} \\ + \langle \hat{N} \rangle &= \frac{1}{\beta} \frac{\partial}{\partial \mu} \ln \mathcal{Z} +""" -############################################################################### -# And then: -# --------- +# Code source: Óscar Nájera +# License: BSD 3 clause -plt.plot(x, (1 - x) / 4) -plt.ylabel(r'$\langle n_\uparrow n_\downarrow \rangle$') -plt.xlabel('U') +import matplotlib.pylab as plt +import numpy as np +mu = np.linspace(0, 3, 800) +for b in [10, 20, 30]: + n = 2 * (np.exp(b * (mu - 1)) + np.exp(b * (2 * mu - 3))) / \ + (1 + np.exp(b * (mu - 1)) * (2 + np.exp(b * (mu - 2)))) + plt.plot(mu, n, label=r"$\beta={}$".format(b)) +plt.xlabel(r'$\mu$ ($\epsilon=1$, $U=1$)') +plt.ylabel(r'$\langle N \rangle=\langle n_\uparrow \rangle+\langle n_\downarrow\rangle$') +plt.legend(loc=0) plt.show() From 33c189358bfa78b2b37d8f96cfbb014f01e0d657 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Thu, 17 Mar 2016 09:14:05 -0400 Subject: [PATCH 10/11] FIX: Address comments --- sphinx_gallery/gen_rst.py | 4 ++-- sphinx_gallery/tests/test_gen_rst.py | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/sphinx_gallery/gen_rst.py b/sphinx_gallery/gen_rst.py index 63219acab..bf3e1300f 100644 --- a/sphinx_gallery/gen_rst.py +++ b/sphinx_gallery/gen_rst.py @@ -535,8 +535,8 @@ def execute_script(code_block, example_globals, image_path, fig_count, raise finally: - sys.stdout = orig_stdout os.chdir(cwd) + sys.stdout = orig_stdout print(" - time elapsed : %.2g sec" % time_elapsed) code_output = u"\n{0}\n\n{1}\n\n".format(image_list, stdout) @@ -635,7 +635,7 @@ def generate_file_rst(fname, target_dir, src_dir, gallery_conf): time_m, time_s = divmod(time_elapsed, 60) example_nb.save_file() with codecs.open(os.path.join(target_dir, base_image_name + '.rst'), - 'w', 'utf-8') as f: + mode='w', encoding='utf-8') as f: example_rst += CODE_DOWNLOAD.format(time_m, time_s, fname, example_nb.file_name) f.write(example_rst) diff --git a/sphinx_gallery/tests/test_gen_rst.py b/sphinx_gallery/tests/test_gen_rst.py index c67e6a2d0..efc23fedf 100644 --- a/sphinx_gallery/tests/test_gen_rst.py +++ b/sphinx_gallery/tests/test_gen_rst.py @@ -148,20 +148,18 @@ def test_pattern_matching(): # create three files in tempdir (only one matches the pattern) fnames = ['plot_0.py', 'plot_1.py', 'plot_2.py'] for fname in fnames: - with codecs.open(os.path.join(examples_dir, fname), 'w', 'utf-8') as f: + with codecs.open(os.path.join(examples_dir, fname), mode='w', + encoding='utf-8') as f: f.write('\n'.join(CONTENT)) # generate rst file sg.generate_file_rst(fname, gallery_dir, examples_dir, gallery_conf) # read rst file and check if it contains code output rst_fname = os.path.splitext(fname)[0] + '.rst' with codecs.open(os.path.join(gallery_dir, rst_fname), - 'r', 'utf-8') as f: + mode='r', encoding='utf-8') as f: rst = f.read() if re.search(gallery_conf['filename_pattern'], os.path.join(gallery_dir, rst_fname)): - print(code_output) - print('') - print(rst) assert_true(code_output in rst) else: assert_false(code_output in rst) From 81bd239e92bee9bfcbc6f8a16f0463ca8a8b5379 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Thu, 17 Mar 2016 11:12:48 -0400 Subject: [PATCH 11/11] FIX: Minor fixes --- sphinx_gallery/gen_rst.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/sphinx_gallery/gen_rst.py b/sphinx_gallery/gen_rst.py index bf3e1300f..f7a157edf 100644 --- a/sphinx_gallery/gen_rst.py +++ b/sphinx_gallery/gen_rst.py @@ -52,9 +52,9 @@ def prefixed_lines(): return ''.join(prefixed_lines()) try: - from BytesIO import BytesIO + from StringIO import StringIO except ImportError: - from io import BytesIO + from io import StringIO try: # make sure that the Agg backend is set before importing any @@ -97,17 +97,13 @@ def flush(self): self.file2.flush() -class MyBytesIO(BytesIO): - """Helper to deal with unicode not having buffer interface on Py2.7""" +class MixedEncodingStringIO(StringIO): + """Helper when both ASCII and unicode strings will be written""" def write(self, data): - # "unicode" objects don't have a buffer interface on Py2.7, - # so we need to manually convert - if isinstance(data, unicode): - data = data.encode('utf-8') - super(MyBytesIO, self).write(data) + if not isinstance(data, unicode): + data = data.decode('utf-8') + StringIO.write(self, data) - def getvalue(self): - return super(MyBytesIO, self).getvalue().decode('utf-8') ############################################################################### CODE_DOWNLOAD = """**Total running time of the script:** @@ -483,7 +479,7 @@ def execute_script(code_block, example_globals, image_path, fig_count, # First cd in the original example dir, so that any file # created by the example get created in this directory os.chdir(os.path.dirname(src_file)) - my_buffer = MyBytesIO() + my_buffer = MixedEncodingStringIO() my_stdout = Tee(sys.stdout, my_buffer) sys.stdout = my_stdout @@ -496,6 +492,7 @@ def execute_script(code_block, example_globals, image_path, fig_count, sys.stdout = orig_stdout my_stdout = my_buffer.getvalue().strip().expandtabs() + # raise RuntimeError if my_stdout: stdout = CODE_OUTPUT.format(indent(my_stdout, u' ' * 4)) os.chdir(cwd) @@ -530,8 +527,9 @@ def execute_script(code_block, example_globals, image_path, fig_count, fig_count += 1 # raise count to avoid overwriting image # Breaks build on first example error - # Eventually this should hopefully be unified with the default conf - if gallery_conf.get('abort_on_example_error', True): + # XXX This check can break during testing e.g. if you uncomment the + # `raise RuntimeError` by the `my_stdout` call, maybe use `.get()`? + if gallery_conf['abort_on_example_error']: raise finally: