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