Skip to content

Commit

Permalink
Add description to type constraints
Browse files Browse the repository at this point in the history
In cases when a type constraints types are multitudinous, it's nice to have repr / str display a human friendly name instead of the long list of types.

It's not used as of this patch, but it's a distinct optional dependency of a larger patch I'm working on. In that patch, I'm adding type constraints with the type contents of the BUILD file symbol tables. These are long enough and not obviously about parsing that I felt like having this is worth while.

Testing Done:
Added tests covering Exactly's repr and strs.

Bugs closed: 3865

Reviewed at https://rbcommons.com/s/twitter/r/4233/

closes pantsbuild#3865
  • Loading branch information
baroquebobcat committed Sep 14, 2016
1 parent 3e6e212 commit d30cca1
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 3 deletions.
20 changes: 17 additions & 3 deletions src/python/pants/engine/addressable.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,21 @@ class TypeConstraint(AbstractClass):
:class:`SubclassesOf`.
"""

def __init__(self, *types):
def __init__(self, *types, **kwargs):
"""Creates a type constraint centered around the given types.
The type constraint is satisfied as a whole if satisfied for at least one of the given types.
:param type *types: The focus of this type constraint.
:param str description: A description for this constraint if the list of types is too long.
"""
if not types:
raise ValueError('Must supply at least one type')
if any(not isinstance(t, type) for t in types):
raise TypeError('Supplied types must be types. {!r}'.format(types))

self._types = types
self._desc = kwargs.get('description', None)

@property
def types(self):
Expand All @@ -68,12 +70,24 @@ def __ne__(self, other):
return not (self == other)

def __str__(self):
if self._desc:
constrained_type = '({})'.format(self._desc)
else:
if len(self._types) == 1:
constrained_type = self._types[0].__name__
else:
constrained_type = '({})'.format(', '.join(t.__name__ for t in self._types))
return '{variance_symbol}{constrained_type}'.format(variance_symbol=self._variance_symbol,
constrained_type=self._types)
constrained_type=constrained_type)

def __repr__(self):
if self._desc:
constrained_type = self._desc
else:
constrained_type = ', '.join(t.__name__ for t in self._types)
return ('{type_constraint_type}({constrained_type})'
.format(type_constraint_type=type(self).__name__, constrained_type=self._types))
.format(type_constraint_type=type(self).__name__,
constrained_type=constrained_type))


class SuperclassesOf(TypeConstraint):
Expand Down
13 changes: 13 additions & 0 deletions tests/python/pants_test/engine/test_addressable.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,19 @@ def test_disallows_unsplatted_lists(self):
with self.assertRaises(TypeError):
Exactly([1])

def test_str_and_repr(self):
exactly_b_types = Exactly(self.B, description='B types')
self.assertEquals("=(B types)", str(exactly_b_types))
self.assertEquals("Exactly(B types)", repr(exactly_b_types))

exactly_b = Exactly(self.B)
self.assertEquals("=B", str(exactly_b))
self.assertEquals("Exactly(B)", repr(exactly_b))

exactly_multiple = Exactly(self.A, self.B)
self.assertEquals("=(A, B)", str(exactly_multiple))
self.assertEquals("Exactly(A, B)", repr(exactly_multiple))


class SubclassesOfTest(TypeConstraintTestBase):
def test_none(self):
Expand Down

0 comments on commit d30cca1

Please sign in to comment.