Skip to content

Commit

Permalink
fix(help): ipython#381 neglected RsT from printing enum-choices
Browse files Browse the repository at this point in the history
+ Had to add an optional `as_rst=false` kwd in all`XxxEnum.info()`
methods.
+ Added TCs for `info().
  • Loading branch information
ankostis committed Aug 12, 2017
1 parent 62183d9 commit c1e756b
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 30 deletions.
2 changes: 1 addition & 1 deletion traitlets/config/configurable.py
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ def class_config_rst_doc(cls):
# Choices or type
if 'Enum' in ttype:
# include Enum choices
termline += ' : ' + '|'.join(repr(x) for x in trait.values)
termline += ' : ' + trait.info(as_rst=True)
else:
termline += ' : ' + ttype
lines.append(termline)
Expand Down
7 changes: 7 additions & 0 deletions traitlets/config/tests/test_configurable.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ class MyConf(Configurable):

help_str = "Many choices."
enum_choices_str = "Choices: any of ['Choice1', 'choice2']"
rst_choices_str = "MyConf.an_enum : any of ``'Choice1'``|``'choice2'``"
or_none_str = "or None"

cls_help = MyConf.class_get_help()
Expand All @@ -210,6 +211,12 @@ class MyConf(Configurable):
self.assertGreater(cls_cfg.index(enum_choices_str),
cls_cfg.index(help_str))

rst_help = MyConf.class_config_rst_doc()

self.assertIn(help_str, rst_help)
self.assertIn(rst_choices_str, rst_help)
self.assertNotIn(or_none_str, rst_help)

class MyConf2(Configurable):
an_enum = Enum('Choice1 choice2'.split(),
allow_none=True,
Expand Down
51 changes: 46 additions & 5 deletions traitlets/tests/test_traitlets_enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import unittest
import enum
from ipython_genutils.py3compat import string_types
from traitlets import HasTraits, TraitError, UseEnum, FuzzyEnum
from traitlets import HasTraits, TraitError, Enum, UseEnum, CaselessStrEnum, FuzzyEnum


# -----------------------------------------------------------------------------
Expand All @@ -25,6 +25,16 @@ class OtherColor(enum.Enum):
green = 1


class CSColor(enum.Enum):
red = 1
Green = 2
BLUE = 3
YeLLoW = 4


color_choices = 'red Green BLUE YeLLoW'.split()


# -----------------------------------------------------------------------------
# TESTSUITE:
# -----------------------------------------------------------------------------
Expand Down Expand Up @@ -180,11 +190,42 @@ class Example2(HasTraits):
with self.assertRaises(TraitError):
example.color = "BAD_VALUE"

def test_info(self):
choices = color_choices
class Example(HasTraits):
enum1 = Enum(choices, allow_none=False)
enum2 = CaselessStrEnum(choices, allow_none=False)
enum3 = FuzzyEnum(choices, allow_none=False)
enum4 = UseEnum(CSColor, allow_none=False)

for i in range(1,5):
attr = 'enum%s' % i
enum = getattr(Example, attr)

enum.allow_none = True

info = enum.info()
self.assertEqual(len(info.split(',')), len(choices))
self.assertIn('or None', info)

info = enum.info(as_rst=True)
self.assertEqual(len(info.split('|')), len(choices))
self.assertIn('or `None`', info)
## Check no single `\` exists.
self.assertNotRegex(info, r'\b\\\b')

enum.allow_none = False

info = enum.info()
self.assertEqual(len(info.split(',')), len(choices))
self.assertNotIn('None', info)

info = enum.info(as_rst=True)
self.assertEqual(len(info.split('|')), len(choices))
self.assertNotIn('None', info)
## Check no single `\` exists.
self.assertNotRegex(info, r'\b\\\b')

# -----------------------------------------------------------------------------
# TEST SUPPORT:
# -----------------------------------------------------------------------------
color_choices = 'red Green BLUE YeLLoW'.split()


# -----------------------------------------------------------------------------
Expand Down
70 changes: 46 additions & 24 deletions traitlets/traitlets.py
Original file line number Diff line number Diff line change
Expand Up @@ -1190,7 +1190,7 @@ def _notify_trait(self, name, old_value, new_value):
def notify_change(self, change):
"""Notify observers of a change event"""
return self._notify_observers(change)

def _notify_observers(self, event):
"""Notify observers of any event"""
if not isinstance(event, Bunch):
Expand Down Expand Up @@ -2263,12 +2263,21 @@ def validate(self, obj, value):
return value
self.error(obj, value)

def info(self):
def _choices_str(self, as_rst=False):
""" Returns a description of the trait choices (not none)."""
choices = self.values
if as_rst:
choices = '|'.join('``%r``' % x for x in choices)
else:
choices = repr(choices)
return 'any of ' + choices

def info(self, as_rst=False):
""" Returns a description of the trait."""
result = 'any of ' + repr(self.values)
if self.allow_none:
return result + ' or None'
return result
none = (' or %s' % ('`None`' if as_rst else 'None')
if self.allow_none else
'')
return self._choices_str(as_rst) + none


class CaselessStrEnum(Enum):
Expand All @@ -2289,12 +2298,14 @@ def validate(self, obj, value):
return v
self.error(obj, value)

def info(self):
""" Returns a description of the trait."""
result = 'any of %s (case-insensitive)' % (self.values, )
if self.allow_none:
return result + ' or None'
return result
def _choices_str(self, as_rst=True):
""" Returns a description of the trait choices (not none)."""
choices = self.values
if as_rst:
choices = '|'.join('``%r``' % x for x in choices)
else:
choices = repr(choices)
return 'any of %s (case-insensitive)' % (choices, )


class FuzzyEnum(Enum):
Expand Down Expand Up @@ -2332,14 +2343,16 @@ def validate(self, obj, value):

self.error(obj, value)

def info(self):
""" Returns a description of the trait."""
def _choices_str(self, as_rst=False):
""" Returns a description of the trait choices (not none)."""
choices = self.values
if as_rst:
choices = '|'.join('``%r``' % x for x in choices)
else:
choices = repr(choices)
case = 'sensitive' if self.case_sensitive else 'insensitive'
substr = 'substring' if self.substring_matching else 'prefix'
result = 'any case-%s %s of %s' % (case, substr, self.values)
if self.allow_none:
return result + ' or None'
return result
return 'any case-%s %s of %s' % (case, substr, choices)


class Container(Instance):
Expand Down Expand Up @@ -2921,12 +2934,21 @@ def validate(self, obj, value):
return self.default_value
self.error(obj, value)

def info(self):
"""Returns a description of this Enum trait (in case of errors)."""
result = "Any of: %s" % ", ".join(self.enum_class.__members__.keys())
if self.allow_none:
return result + " or None"
return result
def _choices_str(self, as_rst=False):
""" Returns a description of the trait choices (not none)."""
choices = self.enum_class.__members__.keys()
if as_rst:
return '|'.join('``%r``' % x for x in choices)
else:
return repr(choices)

def info(self, as_rst=False):
""" Returns a description of the trait."""
none = (' or %s' % ('`None`' if as_rst else 'None')
if self.allow_none else
'')
return self._choices_str(as_rst) + none


class Callable(TraitType):
"""A trait which is callable.
Expand Down

0 comments on commit c1e756b

Please sign in to comment.