Skip to content


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP


Fix for prompts containing newlines. #1105

merged 2 commits into from

4 participants

Thomas Kluyver Robert Kern Min RK Fernando Perez
Thomas Kluyver

@rkern, I've tried this with the config example you supplied, and it seems to be working OK.

Specifically, we now only justify prompts based on the last line of the preceding prompt, and we don't attempt to justify multiline prompts at all.

Closes gh-1104

Robert Kern

Works for me.

Thomas Kluyver

Great, thanks. I'll give the others a day or two to make any comments, then merge it.

Min RK

works fine for me, thanks.

Fernando Perez

Glad to hear the fixes work correctly. I thought for a bit about what kind of automated test we could add to ensure this doesn't return to bite us later, but I can't quite seem to see a quick way to make one. I figured we could do one involving creating an irunner with custom prompts, running a session through it and validating back the output, but it sounds like a fair amount of hassle and I'm not sure the effort/payoff is worth it.

If you can think of a way to test this that isn't too painful to implement, go ahead and do it, otherwise merge as-is.

Thomas Kluyver
Fernando Perez

Sure :) We've made good progress on the other PRs today, so we can sit on this one for a few days before the 0.12 rc.

Thomas Kluyver

I've added a few simple tests, and checked that they're passing.

Min RK

nice, looks good to me.

Thomas Kluyver

Great. I'll merge it later today unless anyone objects.

Min RK

please do, thanks!

Thomas Kluyver takluyver merged commit 96e8539 into from
Thomas Kluyver

Rebased to avoid a merge, and pushed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Dec 8, 2011
  1. Thomas Kluyver
  2. Thomas Kluyver
This page is out of date. Refresh to see the latest.
Showing with 59 additions and 6 deletions.
  1. +16 −6 IPython/core/
  2. +43 −0 IPython/core/tests/
22 IPython/core/
@@ -232,7 +232,14 @@ def cwd_filt2(depth):
[LazyEvaluate(cwd_filt, x) for x in range(1,6)],
'cwd_y': [LazyEvaluate(cwd_filt2, x) for x in range(6)]
+def _lenlastline(s):
+ """Get the length of the last line. More intelligent than
+ len(s.splitlines()[-1]).
+ """
+ if not s or s.endswith(('\n', '\r')):
+ return 0
+ return len(s.splitlines()[-1])
class PromptManager(Configurable):
"""This is the primary interface for producing IPython's prompts."""
@@ -305,8 +312,10 @@ def update_prompt(self, name, new_template=None):
if new_template is not None:
self.templates[name] = multiple_replace(prompt_abbreviations, new_template)
- invis_chars = len(self._render(name, color=True)) - \
- len(self._render(name, color=False))
+ # We count invisible characters (colour escapes) on the last line of the
+ # prompt, to calculate the width for lining up subsequent prompts.
+ invis_chars = _lenlastline(self._render(name, color=True)) - \
+ _lenlastline(self._render(name, color=False))
self.invisible_chars[name] = invis_chars
def _update_prompt_trait(self, traitname, new_template):
@@ -388,9 +397,10 @@ def render(self, name, color=True, just=None, **kwargs):
# Handle justification of prompt
invis_chars = self.invisible_chars[name] if color else 0
- self.txtwidth = len(res) - invis_chars
+ self.txtwidth = _lenlastline(res) - invis_chars
just = self.justify if (just is None) else just
- if just:
+ # If the prompt spans more than one line, don't try to justify it:
+ if just and ('\n' not in res) and ('\r' not in res):
res = res.rjust(self.width + invis_chars)
- self.width = len(res) - invis_chars
+ self.width = _lenlastline(res) - invis_chars
return res
43 IPython/core/tests/
@@ -0,0 +1,43 @@
+"""Tests for prompt generation."""
+import unittest
+import as nt
+from IPython.testing import tools as tt, decorators as dec
+from IPython.core.prompts import PromptManager
+from IPython.testing.globalipapp import get_ipython
+ip = get_ipython()
+class PromptTests(unittest.TestCase):
+ def setUp(self):
+ = PromptManager(shell=ip, config=ip.config)
+ def test_multiline_prompt(self):
+ = "[In]\n>>>"
+ self.assertEqual(, 3)
+ self.assertEqual(, 3)
+ = '[In]\n'
+ self.assertEqual(, 0)
+ self.assertEqual(, 0)
+ def test_translate_abbreviations(self):
+ def do_translate(template):
+ = template
+ return['in']
+ pairs = [(r'%n>', '{color.number}{count}{color.prompt}>'),
+ (r'\T', '{time}'),
+ (r'\n', '\n')
+ ]
+ tt.check_pairs(do_translate, pairs)
+ def test_render(self):
+ = r'\#>'
+ self.assertEqual('in',color=False), '%d>' % ip.execution_count)
Something went wrong with that request. Please try again.