diff --git a/src/python/pants/engine/addressable.py b/src/python/pants/engine/addressable.py index 6cf6a421ac0..d4fe5888543 100644 --- a/src/python/pants/engine/addressable.py +++ b/src/python/pants/engine/addressable.py @@ -29,12 +29,13 @@ 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') @@ -42,6 +43,7 @@ def __init__(self, *types): raise TypeError('Supplied types must be types. {!r}'.format(types)) self._types = types + self._desc = kwargs.get('description', None) @property def types(self): @@ -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): diff --git a/tests/python/pants_test/engine/test_addressable.py b/tests/python/pants_test/engine/test_addressable.py index ab531408b51..fe3d0dbb8fb 100644 --- a/tests/python/pants_test/engine/test_addressable.py +++ b/tests/python/pants_test/engine/test_addressable.py @@ -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):