diff --git a/jedi/_compatibility.py b/jedi/_compatibility.py index b7ecd162..290eb520 100644 --- a/jedi/_compatibility.py +++ b/jedi/_compatibility.py @@ -180,3 +180,21 @@ def no_unicode_pprint(dct): import pprint s = pprint.pformat(dct) print(re.sub("u'", "'", s)) + + +def utf8_repr(func): + """ + ``__repr__`` methods in Python 2 don't allow unicode objects to be + returned. Therefore cast them to utf-8 bytes in this decorator. + """ + def wrapper(self): + result = func(self) + if isinstance(result, unicode): + return result.encode('utf-8') + else: + return result + + if is_py3: + return func + else: + return wrapper diff --git a/jedi/debug.py b/jedi/debug.py index 7da264fb..5c59a7b6 100644 --- a/jedi/debug.py +++ b/jedi/debug.py @@ -1,4 +1,4 @@ -from jedi._compatibility import encoding, is_py3 +from jedi._compatibility import encoding, is_py3, u import inspect import os import time @@ -63,13 +63,13 @@ def dbg(message, *args): mod = inspect.getmodule(frm[0]) if not (mod.__name__ in ignored_modules): i = ' ' * _debug_indent - debug_function(NOTICE, i + 'dbg: ' + message % args) + debug_function(NOTICE, i + 'dbg: ' + message % tuple(u(repr(a)) for a in args)) def warning(message, *args): if debug_function and enable_warning: i = ' ' * _debug_indent - debug_function(WARNING, i + 'warning: ' + message % args) + debug_function(WARNING, i + 'warning: ' + message % tuple(u(repr(a)) for a in args)) def speed(name): diff --git a/jedi/parser/tree.py b/jedi/parser/tree.py index c3c220cd..1c6faf0a 100644 --- a/jedi/parser/tree.py +++ b/jedi/parser/tree.py @@ -34,7 +34,7 @@ from inspect import cleandoc from itertools import chain import textwrap -from jedi._compatibility import (Python3Method, encoding, is_py3, +from jedi._compatibility import (Python3Method, encoding, is_py3, utf8_repr, literal_eval, use_metaclass, unicode) from jedi import cache @@ -223,8 +223,9 @@ class Leaf(Base): return None return self.parent.children[i - 1] + @utf8_repr def __repr__(self): - return "<%s: %s>" % (type(self).__name__, repr(self.value)) + return "<%s: %s>" % (type(self).__name__, self.value) class LeafWithNewLines(Leaf): @@ -336,16 +337,6 @@ class Literal(LeafWithNewLines): def eval(self): return literal_eval(self.value) - def __repr__(self): - # TODO remove? - """ - if is_py3: - s = self.literal - else: - s = self.literal.encode('ascii', 'replace') - """ - return "<%s: %s>" % (type(self).__name__, self.value) - class Number(Literal): type = 'number' @@ -472,6 +463,7 @@ class BaseNode(Base): except AttributeError: return self.children[0] + @utf8_repr def __repr__(self): code = self.get_code().replace('\n', ' ') if not is_py3: diff --git a/test/test_parser/test_parser.py b/test/test_parser/test_parser.py index 595f124a..c47fd47c 100644 --- a/test/test_parser/test_parser.py +++ b/test/test_parser/test_parser.py @@ -202,3 +202,8 @@ def test_param_splitting(): check('def x(a, (b, c)):\n pass', ['a']) check('def x((b, c)):\n pass', []) + + +def test_unicode_string(): + s = pt.String(None, u'bö', (0, 0)) + assert repr(s) # Should not raise an Error!