Skip to content

Commit e02b97c

Browse files
committed
Improve matcher repr output for clearer diagnostics
Use repr-style formatting for value-like matchers so string values are quoted consistently in diagnostics and nested matcher output. Also make Matches report only explicitly requested regex flags and render patterns via repr. Add matcher_repr_test.py with coverage for the updated repr behavior.
1 parent 5980331 commit e02b97c

File tree

2 files changed

+35
-7
lines changed

2 files changed

+35
-7
lines changed

mockito/matchers.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -137,15 +137,15 @@ def matches(self, arg):
137137
return True
138138

139139
def __repr__(self):
140-
return "<Any: %s>" % self.wanted_type
140+
return "<Any: %r>" % self.wanted_type
141141

142142

143143
class ValueMatcher(Matcher):
144144
def __init__(self, value):
145145
self.value = value
146146

147147
def __repr__(self):
148-
return "<%s: %s>" % (self.__class__.__name__, self.value)
148+
return "<%s: %r>" % (self.__class__.__name__, self.value)
149149

150150

151151
class Eq(ValueMatcher):
@@ -236,24 +236,24 @@ def matches(self, arg):
236236
return self.sub and len(self.sub) > 0 and arg.find(self.sub) > -1
237237

238238
def __repr__(self):
239-
return "<Contains: '%s'>" % self.sub
239+
return "<Contains: %r>" % self.sub
240240

241241

242242
class Matches(Matcher):
243243
def __init__(self, regex, flags=0):
244244
self.regex = re.compile(regex, flags)
245+
self.flags = flags
245246

246247
def matches(self, arg):
247248
if not isinstance(arg, str):
248249
return
249250
return self.regex.match(arg) is not None
250251

251252
def __repr__(self):
252-
if self.regex.flags:
253-
return "<Matches: %s flags=%d>" % (self.regex.pattern,
254-
self.regex.flags)
253+
if self.flags:
254+
return "<Matches: %r flags=%d>" % (self.regex.pattern, self.flags)
255255
else:
256-
return "<Matches: %s>" % self.regex.pattern
256+
return "<Matches: %r>" % self.regex.pattern
257257

258258

259259
class ArgumentCaptor(Matcher, Capturing):

tests/matcher_repr_test.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import re
2+
3+
from mockito import and_, any as any_, contains, eq, gt, matches, not_, or_
4+
5+
6+
def test_value_matchers_use_repr_for_string_values():
7+
assert repr(eq("foo")) == "<Eq: 'foo'>"
8+
9+
10+
def test_composed_matchers_include_quoted_nested_values():
11+
assert repr(not_(eq("foo"))) == "<Not: <Eq: 'foo'>>"
12+
assert repr(and_(eq("foo"), gt(1))) == "<And: [<Eq: 'foo'>, <Gt: 1>]>"
13+
assert repr(or_(eq("foo"), gt(1))) == "<Or: [<Eq: 'foo'>, <Gt: 1>]>"
14+
15+
16+
def test_any_repr_quotes_non_type_values():
17+
assert repr(any_("foo")) == "<Any: 'foo'>"
18+
19+
20+
def test_contains_repr_uses_safe_quoted_substring():
21+
assert repr(contains("a'b")) == "<Contains: \"a'b\">"
22+
23+
24+
def test_matches_repr_shows_only_explicit_flags():
25+
assert repr(matches("f..")) == "<Matches: 'f..'>"
26+
assert repr(matches("f..", re.IGNORECASE)) == (
27+
f"<Matches: 'f..' flags={int(re.IGNORECASE)}>"
28+
)

0 commit comments

Comments
 (0)