From 97ea6aa1d9f6c7f8626539b130ebd81d38904452 2011-06-21 09:56:22 From: Thomas Kluyver Date: 2011-06-21 09:56:22 Subject: [PATCH] Add ObjectName and DottedObjectName trait types for referring to Python identifiers. --- diff --git a/IPython/core/formatters.py b/IPython/core/formatters.py index 655aca9..85b1b22 100644 --- a/IPython/core/formatters.py +++ b/IPython/core/formatters.py @@ -28,7 +28,7 @@ from StringIO import StringIO # Our own imports from IPython.config.configurable import Configurable from IPython.lib import pretty -from IPython.utils.traitlets import Bool, Dict, Int, Unicode, CUnicode +from IPython.utils.traitlets import Bool, Dict, Int, Unicode, CUnicode, ObjectName #----------------------------------------------------------------------------- @@ -195,7 +195,7 @@ class BaseFormatter(Configurable): enabled = Bool(True, config=True) - print_method = Unicode('__repr__') + print_method = ObjectName('__repr__') # The singleton printers. # Maps the IDs of the builtin singleton objects to the format functions. @@ -344,7 +344,7 @@ class PlainTextFormatter(BaseFormatter): enabled = Bool(True, config=False) # Look for a _repr_pretty_ methods to use for pretty printing. - print_method = Unicode('_repr_pretty_') + print_method = ObjectName('_repr_pretty_') # Whether to pretty-print or not. pprint = Bool(True, config=True) @@ -456,7 +456,7 @@ class HTMLFormatter(BaseFormatter): """ format_type = Unicode('text/html') - print_method = Unicode('_repr_html_') + print_method = ObjectName('_repr_html_') class SVGFormatter(BaseFormatter): @@ -473,7 +473,7 @@ class SVGFormatter(BaseFormatter): """ format_type = Unicode('image/svg+xml') - print_method = Unicode('_repr_svg_') + print_method = ObjectName('_repr_svg_') class PNGFormatter(BaseFormatter): @@ -489,7 +489,7 @@ class PNGFormatter(BaseFormatter): """ format_type = Unicode('image/png') - print_method = Unicode('_repr_png_') + print_method = ObjectName('_repr_png_') class LatexFormatter(BaseFormatter): @@ -505,7 +505,7 @@ class LatexFormatter(BaseFormatter): """ format_type = Unicode('text/latex') - print_method = Unicode('_repr_latex_') + print_method = ObjectName('_repr_latex_') class JSONFormatter(BaseFormatter): @@ -520,7 +520,7 @@ class JSONFormatter(BaseFormatter): """ format_type = Unicode('application/json') - print_method = Unicode('_repr_json_') + print_method = ObjectName('_repr_json_') class JavascriptFormatter(BaseFormatter): @@ -536,7 +536,7 @@ class JavascriptFormatter(BaseFormatter): """ format_type = Unicode('application/javascript') - print_method = Unicode('_repr_javascript_') + print_method = ObjectName('_repr_javascript_') FormatterABC.register(BaseFormatter) FormatterABC.register(PlainTextFormatter) diff --git a/IPython/utils/tests/test_traitlets.py b/IPython/utils/tests/test_traitlets.py index d934b9e..7e4ee49 100755 --- a/IPython/utils/tests/test_traitlets.py +++ b/IPython/utils/tests/test_traitlets.py @@ -22,12 +22,14 @@ Authors: # Imports #----------------------------------------------------------------------------- +import sys from unittest import TestCase from IPython.utils.traitlets import ( HasTraits, MetaHasTraits, TraitType, Any, CBytes, Int, Long, Float, Complex, Bytes, Unicode, TraitError, - Undefined, Type, This, Instance, TCPAddress, List, Tuple + Undefined, Type, This, Instance, TCPAddress, List, Tuple, + ObjectName, DottedObjectName ) @@ -725,11 +727,42 @@ class TestUnicode(TraitTestBase): _default_value = u'unicode' _good_values = ['10', '-10', '10L', '-10L', '10.1', - '-10.1', '', u'', 'string', u'string', ] + '-10.1', '', u'', 'string', u'string', u"€"] _bad_values = [10, -10, 10L, -10L, 10.1, -10.1, 1j, [10], ['ten'], [u'ten'], {'ten': 10},(10,), None] +class ObjectNameTrait(HasTraits): + value = ObjectName("abc") + +class TestObjectName(TraitTestBase): + obj = ObjectNameTrait() + + _default_value = "abc" + _good_values = ["a", "gh", "g9", "g_", "_G", u"a345_"] + _bad_values = [1, "", u"€", "9g", "!", "#abc", "aj@", "a.b", "a()", "a[0]", + object(), object] + if sys.version_info[0] < 3: + _bad_values.append(u"þ") + else: + _good_values.append(u"þ") # þ=1 is valid in Python 3 (PEP 3131). + + +class DottedObjectNameTrait(HasTraits): + value = DottedObjectName("a.b") + +class TestDottedObjectName(TraitTestBase): + obj = DottedObjectNameTrait() + + _default_value = "a.b" + _good_values = ["A", "y.t", "y765.__repr__", "os.path.join", u"os.path.join"] + _bad_values = [1, u"abc.€", "_.@", ".", ".abc", "abc.", ".abc."] + if sys.version_info[0] < 3: + _bad_values.append(u"t.þ") + else: + _good_values.append(u"t.þ") + + class TCPAddressTrait(HasTraits): value = TCPAddress() diff --git a/IPython/utils/traitlets.py b/IPython/utils/traitlets.py index 9420474..e95e7d0 100644 --- a/IPython/utils/traitlets.py +++ b/IPython/utils/traitlets.py @@ -50,6 +50,7 @@ Authors: import inspect +import re import sys import types from types import ( @@ -998,6 +999,50 @@ class CUnicode(Unicode): return unicode(value) except: self.error(obj, value) + + +class ObjectName(TraitType): + """A string holding a valid object name in this version of Python. + + This does not check that the name exists in any scope.""" + info_text = "a valid object identifier in Python" + + if sys.version_info[0] < 3: + # Python 2: + _name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$") + def isidentifier(self, s): + return bool(self._name_re.match(s)) + + def coerce_str(self, obj, value): + "In Python 2, coerce ascii-only unicode to str" + if isinstance(value, unicode): + try: + return str(value) + except UnicodeEncodeError: + self.error(obj, value) + return value + + else: + # Python 3: + isidentifier = staticmethod(lambda s: s.isidentifier()) + coerce_str = staticmethod(lambda _,s: s) + + def validate(self, obj, value): + value = self.coerce_str(obj, value) + + if isinstance(value, str) and self.isidentifier(value): + return value + self.error(obj, value) + +class DottedObjectName(ObjectName): + """A string holding a valid dotted object name in Python, such as A.b3._c""" + def validate(self, obj, value): + value = self.coerce_str(obj, value) + + if isinstance(value, str) and all(self.isidentifier(x) \ + for x in value.split('.')): + return value + self.error(obj, value) class Bool(TraitType):