Show More
@@ -73,7 +73,8 b' from IPython.utils.pickleshare import PickleShareDB' | |||
|
73 | 73 | from IPython.utils.process import system, getoutput |
|
74 | 74 | from IPython.utils.strdispatch import StrDispatch |
|
75 | 75 | from IPython.utils.syspathcontext import prepended_to_syspath |
|
76 | from IPython.utils.text import num_ini_spaces, format_screen, LSString, SList | |
|
76 | from IPython.utils.text import (num_ini_spaces, format_screen, LSString, SList, | |
|
77 | DollarFormatter) | |
|
77 | 78 | from IPython.utils.traitlets import (Integer, CBool, CaselessStrEnum, Enum, |
|
78 | 79 | List, Unicode, Instance, Type) |
|
79 | 80 | from IPython.utils.warn import warn, error, fatal |
@@ -2571,7 +2572,7 b' class InteractiveShell(SingletonConfigurable, Magic):' | |||
|
2571 | 2572 | # Utilities |
|
2572 | 2573 | #------------------------------------------------------------------------- |
|
2573 | 2574 | |
|
2574 | def var_expand(self,cmd,depth=0): | |
|
2575 | def var_expand(self, cmd, depth=0, formatter=DollarFormatter()): | |
|
2575 | 2576 | """Expand python variables in a string. |
|
2576 | 2577 | |
|
2577 | 2578 | The depth argument indicates how many frames above the caller should |
@@ -2580,11 +2581,10 b' class InteractiveShell(SingletonConfigurable, Magic):' | |||
|
2580 | 2581 | The global namespace for expansion is always the user's interactive |
|
2581 | 2582 | namespace. |
|
2582 | 2583 | """ |
|
2583 |
|
|
|
2584 | # Skip our own frame in searching for locals: | |
|
2585 | sys._getframe(depth+1).f_locals # locals | |
|
2586 | ) | |
|
2587 | return py3compat.str_to_unicode(str(res), res.codec) | |
|
2584 | ns = self.user_ns.copy() | |
|
2585 | ns.update(sys._getframe(depth+1).f_locals) | |
|
2586 | ns.pop('self', None) | |
|
2587 | return formatter.format(cmd, **ns) | |
|
2588 | 2588 | |
|
2589 | 2589 | def mktempfile(self, data=None, prefix='ipython_edit_'): |
|
2590 | 2590 | """Make a new tempfile and return its filename. |
@@ -45,7 +45,7 b' def test_columnize_long():' | |||
|
45 | 45 | nt.assert_equals(out, '\n'.join(items+[''])) |
|
46 | 46 | |
|
47 | 47 | def eval_formatter_check(f): |
|
48 | ns = dict(n=12, pi=math.pi, stuff='hello there', os=os) | |
|
48 | ns = dict(n=12, pi=math.pi, stuff='hello there', os=os, u=u"cafΓ©", b="cafΓ©") | |
|
49 | 49 | s = f.format("{n} {n//4} {stuff.split()[0]}", **ns) |
|
50 | 50 | nt.assert_equals(s, "12 3 hello") |
|
51 | 51 | s = f.format(' '.join(['{n//%i}'%i for i in range(1,8)]), **ns) |
@@ -57,6 +57,12 b' def eval_formatter_check(f):' | |||
|
57 | 57 | s = f.format("{stuff!r}", **ns) |
|
58 | 58 | nt.assert_equals(s, repr(ns['stuff'])) |
|
59 | 59 | |
|
60 | # Check with unicode: | |
|
61 | s = f.format("{u}", **ns) | |
|
62 | nt.assert_equals(s, ns['u']) | |
|
63 | # This decodes in a platform dependent manner, but it shouldn't error out | |
|
64 | s = f.format("{b}", **ns) | |
|
65 | ||
|
60 | 66 | nt.assert_raises(NameError, f.format, '{dne}', **ns) |
|
61 | 67 | |
|
62 | 68 | def eval_formatter_slicing_check(f): |
@@ -25,6 +25,7 b' import textwrap' | |||
|
25 | 25 | from string import Formatter |
|
26 | 26 | |
|
27 | 27 | from IPython.external.path import path |
|
28 | from IPython.testing.skipdoctest import skip_doctest_py3 | |
|
28 | 29 | from IPython.utils import py3compat |
|
29 | 30 | from IPython.utils.io import nlprint |
|
30 | 31 | from IPython.utils.data import flatten |
@@ -621,6 +622,7 b' class EvalFormatter(Formatter):' | |||
|
621 | 622 | v = eval(name, kwargs) |
|
622 | 623 | return v, name |
|
623 | 624 | |
|
625 | @skip_doctest_py3 | |
|
624 | 626 | class FullEvalFormatter(Formatter): |
|
625 | 627 | """A String Formatter that allows evaluation of simple expressions. |
|
626 | 628 | |
@@ -635,13 +637,13 b' class FullEvalFormatter(Formatter):' | |||
|
635 | 637 | |
|
636 | 638 | In [1]: f = FullEvalFormatter() |
|
637 | 639 | In [2]: f.format('{n//4}', n=8) |
|
638 | Out[2]: '2' | |
|
640 | Out[2]: u'2' | |
|
639 | 641 | |
|
640 | 642 | In [3]: f.format('{list(range(5))[2:4]}') |
|
641 | Out[3]: '[2, 3]' | |
|
643 | Out[3]: u'[2, 3]' | |
|
642 | 644 | |
|
643 | 645 | In [4]: f.format('{3*2}') |
|
644 | Out[4]: '6' | |
|
646 | Out[4]: u'6' | |
|
645 | 647 | """ |
|
646 | 648 | # copied from Formatter._vformat with minor changes to allow eval |
|
647 | 649 | # and replace the format_spec code with slicing |
@@ -675,8 +677,9 b' class FullEvalFormatter(Formatter):' | |||
|
675 | 677 | # format the object and append to the result |
|
676 | 678 | result.append(self.format_field(obj, '')) |
|
677 | 679 | |
|
678 | return ''.join(result) | |
|
680 | return u''.join(py3compat.cast_unicode(s) for s in result) | |
|
679 | 681 | |
|
682 | @skip_doctest_py3 | |
|
680 | 683 | class DollarFormatter(FullEvalFormatter): |
|
681 | 684 | """Formatter allowing Itpl style $foo replacement, for names and attribute |
|
682 | 685 | access only. Standard {foo} replacement also works, and allows full |
@@ -686,13 +689,13 b' class DollarFormatter(FullEvalFormatter):' | |||
|
686 | 689 | -------- |
|
687 | 690 | In [1]: f = DollarFormatter() |
|
688 | 691 | In [2]: f.format('{n//4}', n=8) |
|
689 | Out[2]: '2' | |
|
692 | Out[2]: u'2' | |
|
690 | 693 | |
|
691 | 694 | In [3]: f.format('23 * 76 is $result', result=23*76) |
|
692 | Out[3]: '23 * 76 is 1748' | |
|
695 | Out[3]: u'23 * 76 is 1748' | |
|
693 | 696 | |
|
694 | 697 | In [4]: f.format('$a or {b}', a=1, b=2) |
|
695 | Out[4]: '1 or 2' | |
|
698 | Out[4]: u'1 or 2' | |
|
696 | 699 | """ |
|
697 | 700 | _dollar_pattern = re.compile("(.*)\$([\w\.]+)") |
|
698 | 701 | def parse(self, fmt_string): |
@@ -703,7 +706,7 b' class DollarFormatter(FullEvalFormatter):' | |||
|
703 | 706 | continue_from = 0 |
|
704 | 707 | for m in self._dollar_pattern.finditer(literal_txt): |
|
705 | 708 | new_txt, new_field = m.group(1,2) |
|
706 |
yield (new_txt, new_field, "", |
|
|
709 | yield (new_txt, new_field, "", None) | |
|
707 | 710 | continue_from = m.end() |
|
708 | 711 | |
|
709 | 712 | # Re-yield the {foo} style pattern |
General Comments 0
You need to be logged in to leave comments.
Login now