Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[MRG+1]: Allow unicode in code and outputs #106

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Mar 17, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also an orthogonal change, but was necessary to make the CIs happy...

fi;
fi;
- pip install -r requirements.txt
Expand Down
67 changes: 67 additions & 0 deletions examples/plot_quantum.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# -*- coding: utf-8 -*-
r"""
=================================================
Some Quantum Mechanics, filling an atomic orbital
=================================================

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.

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:

.. math::
\mathcal{H} =
\sum_{\sigma=\uparrow,\downarrow} \epsilon c^\dagger_\sigma c_\sigma
+ Un_\uparrow n_\downarrow - \mu \hat{N}

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.

.. 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

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`.

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.

.. 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}
"""

# Code source: Óscar Nájera
# License: BSD 3 clause

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()
40 changes: 32 additions & 8 deletions sphinx_gallery/gen_rst.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@
Files that generate images should start with 'plot'

"""
# 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
import hashlib
import os
import re
Expand Down Expand Up @@ -72,6 +75,7 @@ def prefixed_lines():
basestring
except NameError:
basestring = str
unicode = str


###############################################################################
Expand All @@ -93,6 +97,14 @@ def flush(self):
self.file2.flush()


class MixedEncodingStringIO(StringIO):
"""Helper when both ASCII and unicode strings will be written"""
def write(self, data):
if not isinstance(data, unicode):
data = data.decode('utf-8')
StringIO.write(self, data)


###############################################################################
CODE_DOWNLOAD = """**Total running time of the script:**
({0:.0f} minutes {1:.3f} seconds)\n\n
Expand Down Expand Up @@ -124,7 +136,8 @@ def flush(self):
"""


CODE_OUTPUT = """.. rst-class:: sphx-glr-script-out
# This one could contain unicode
CODE_OUTPUT = u""".. rst-class:: sphx-glr-script-out
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We start using in our files, so we don't miss a string

from __future__ import unicode_literals

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unicode_literals takes the need away to prefix every string with u, makes everything unicode. My first guess was to use that instead of going through each string prefixing the u. But it is not that perfect, and I could not get sphinx-gallery to run my examples with unicode_literals in gen_rst.py

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you try putting it in while on this branch, or on a previous one? It might work okay with this one since the unicode reading is a bit more unified.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I put in the __future__ line, removed all u' and u" and it seems to work fine)


Out::

Expand All @@ -143,7 +156,10 @@ def get_docstring_and_rest(filename):
rest: str
`filename` content without the docstring
"""
with open(filename) as f:
# 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()

node = ast.parse(content)
Expand All @@ -154,9 +170,11 @@ 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.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}". '
Expand Down Expand Up @@ -461,19 +479,22 @@ 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 = MixedEncodingStringIO()
my_stdout = Tee(sys.stdout, my_buffer)
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

sys.stdout = orig_stdout

my_stdout = my_buffer.getvalue().strip().expandtabs()
# raise RuntimeError
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)

Expand All @@ -491,6 +512,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)
Expand All @@ -505,7 +527,8 @@ 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

# XXX This check can break during testing e.g. if you uncomment the
# `raise RuntimeError` by the `my_stdout` call, maybe use `.get()`?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lesteve here you go

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this, we should probably fix it at one point.

if gallery_conf['abort_on_example_error']:
raise

Expand All @@ -514,7 +537,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)

Expand Down Expand Up @@ -609,7 +632,8 @@ 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 codecs.open(os.path.join(target_dir, base_image_name + '.rst'),
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)
Expand Down
84 changes: 50 additions & 34 deletions sphinx_gallery/tests/test_gen_rst.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -15,20 +17,31 @@
from sphinx_gallery import notebook


CONTENT = ['"""'
'Docstring header',
'================',
'',
'This is the description of the example',
'which goes on and on',
'',
'',
'And this is a second paragraph',
'"""',
'',
'# and now comes the module code',
'x, y = 1, 2',
'print("'"output"'") # need some code output']
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():
Expand Down Expand Up @@ -61,7 +74,6 @@ def test_direct_comment_after_docstring():
'x, y = 1, 2',
'']))
f.flush()

result = sg.split_code_and_text_blocks(f.name)

expected_result = [
Expand All @@ -84,17 +96,15 @@ 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('\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, Óscar')
assert_false('second paragraph' in result)


def test_md5sums():
Expand Down Expand Up @@ -129,24 +139,30 @@ def test_pattern_matching():
'reference_url': {},
}

code_output = '\n Out::\n\n output\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:
with open(os.path.join(examples_dir, fname), 'w') as f:
with codecs.open(os.path.join(examples_dir, fname), mode='w',
encoding='utf-8') as f:
f.write('\n'.join(CONTENT))
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:
with codecs.open(os.path.join(gallery_dir, rst_fname),
mode='r', encoding='utf-8') as f:
rst = f.read()
if re.search(gallery_conf['filename_pattern'],
os.path.join(gallery_dir, rst_fname)):
assert_true(code_output in rst)
else:
assert_false(code_output in rst)
if re.search(gallery_conf['filename_pattern'],
os.path.join(gallery_dir, rst_fname)):
assert_true(code_output in rst)
else:
assert_false(code_output in rst)


def test_ipy_notebook():
Expand Down