Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Use DollarFormatter to fill in names in ! shell calls.

Closes gh-822
  • Loading branch information...
commit f5687fc7e0d0c5d373f8e6e26d7908716e7ec9fb 1 parent 7f99d33
@takluyver takluyver authored
View
14 IPython/core/interactiveshell.py
@@ -73,7 +73,8 @@
from IPython.utils.process import system, getoutput
from IPython.utils.strdispatch import StrDispatch
from IPython.utils.syspathcontext import prepended_to_syspath
-from IPython.utils.text import num_ini_spaces, format_screen, LSString, SList
+from IPython.utils.text import (num_ini_spaces, format_screen, LSString, SList,
+ DollarFormatter)
from IPython.utils.traitlets import (Integer, CBool, CaselessStrEnum, Enum,
List, Unicode, Instance, Type)
from IPython.utils.warn import warn, error, fatal
@@ -2571,7 +2572,7 @@ def enable_pylab(self, gui=None, import_all=True):
# Utilities
#-------------------------------------------------------------------------
- def var_expand(self,cmd,depth=0):
+ def var_expand(self, cmd, depth=0, formatter=DollarFormatter()):
"""Expand python variables in a string.
The depth argument indicates how many frames above the caller should
@@ -2580,11 +2581,10 @@ def var_expand(self,cmd,depth=0):
The global namespace for expansion is always the user's interactive
namespace.
"""
- res = ItplNS(cmd, self.user_ns, # globals
- # Skip our own frame in searching for locals:
- sys._getframe(depth+1).f_locals # locals
- )
- return py3compat.str_to_unicode(str(res), res.codec)
+ ns = self.user_ns.copy()
+ ns.update(sys._getframe(depth+1).f_locals)
+ ns.pop('self', None)
+ return formatter.format(cmd, **ns)
def mktempfile(self, data=None, prefix='ipython_edit_'):
"""Make a new tempfile and return its filename.
View
8 IPython/utils/tests/test_text.py
@@ -45,7 +45,7 @@ def test_columnize_long():
nt.assert_equals(out, '\n'.join(items+['']))
def eval_formatter_check(f):
- ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
+ ns = dict(n=12, pi=math.pi, stuff='hello there', os=os, u=u"café", b="café")
s = f.format("{n} {n//4} {stuff.split()[0]}", **ns)
nt.assert_equals(s, "12 3 hello")
s = f.format(' '.join(['{n//%i}'%i for i in range(1,8)]), **ns)
@@ -57,6 +57,12 @@ def eval_formatter_check(f):
s = f.format("{stuff!r}", **ns)
nt.assert_equals(s, repr(ns['stuff']))
+ # Check with unicode:
+ s = f.format("{u}", **ns)
+ nt.assert_equals(s, ns['u'])
+ # This decodes in a platform dependent manner, but it shouldn't error out
+ s = f.format("{b}", **ns)
+
nt.assert_raises(NameError, f.format, '{dne}', **ns)
def eval_formatter_slicing_check(f):
View
19 IPython/utils/text.py
@@ -25,6 +25,7 @@
from string import Formatter
from IPython.external.path import path
+from IPython.testing.skipdoctest import skip_doctest_py3
from IPython.utils import py3compat
from IPython.utils.io import nlprint
from IPython.utils.data import flatten
@@ -621,6 +622,7 @@ def get_field(self, name, args, kwargs):
v = eval(name, kwargs)
return v, name
+@skip_doctest_py3
class FullEvalFormatter(Formatter):
"""A String Formatter that allows evaluation of simple expressions.
@@ -635,13 +637,13 @@ class FullEvalFormatter(Formatter):
In [1]: f = FullEvalFormatter()
In [2]: f.format('{n//4}', n=8)
- Out[2]: '2'
+ Out[2]: u'2'
In [3]: f.format('{list(range(5))[2:4]}')
- Out[3]: '[2, 3]'
+ Out[3]: u'[2, 3]'
In [4]: f.format('{3*2}')
- Out[4]: '6'
+ Out[4]: u'6'
"""
# copied from Formatter._vformat with minor changes to allow eval
# and replace the format_spec code with slicing
@@ -675,8 +677,9 @@ def _vformat(self, format_string, args, kwargs, used_args, recursion_depth):
# format the object and append to the result
result.append(self.format_field(obj, ''))
- return ''.join(result)
+ return u''.join(py3compat.cast_unicode(s) for s in result)
+@skip_doctest_py3
class DollarFormatter(FullEvalFormatter):
"""Formatter allowing Itpl style $foo replacement, for names and attribute
access only. Standard {foo} replacement also works, and allows full
@@ -686,13 +689,13 @@ class DollarFormatter(FullEvalFormatter):
--------
In [1]: f = DollarFormatter()
In [2]: f.format('{n//4}', n=8)
- Out[2]: '2'
+ Out[2]: u'2'
In [3]: f.format('23 * 76 is $result', result=23*76)
- Out[3]: '23 * 76 is 1748'
+ Out[3]: u'23 * 76 is 1748'
In [4]: f.format('$a or {b}', a=1, b=2)
- Out[4]: '1 or 2'
+ Out[4]: u'1 or 2'
"""
_dollar_pattern = re.compile("(.*)\$([\w\.]+)")
def parse(self, fmt_string):
@@ -703,7 +706,7 @@ def parse(self, fmt_string):
continue_from = 0
for m in self._dollar_pattern.finditer(literal_txt):
new_txt, new_field = m.group(1,2)
- yield (new_txt, new_field, "", "s")
+ yield (new_txt, new_field, "", None)
continue_from = m.end()
# Re-yield the {foo} style pattern
Please sign in to comment.
Something went wrong with that request. Please try again.