1823b8294SYaroslav Brustinov# Copyright (c) 2009, Tim Cuthbertson # All rights reserved.
2823b8294SYaroslav Brustinov#
3823b8294SYaroslav Brustinov# Redistribution and use in source and binary forms, with or without
4823b8294SYaroslav Brustinov# modification, are permitted provided that the following conditions
5823b8294SYaroslav Brustinov# are met:
6823b8294SYaroslav Brustinov#
7823b8294SYaroslav Brustinov#     * Redistributions of source code must retain the above copyright
8823b8294SYaroslav Brustinov#       notice, this list of conditions and the following disclaimer.
9823b8294SYaroslav Brustinov#     * Redistributions in binary form must reproduce the above
10823b8294SYaroslav Brustinov#       copyright notice, this list of conditions and the following
11823b8294SYaroslav Brustinov#       disclaimer in the documentation and/or other materials provided
12823b8294SYaroslav Brustinov#       with the distribution.
13823b8294SYaroslav Brustinov#     * Neither the name of the organisation nor the names of its
14823b8294SYaroslav Brustinov#       contributors may be used to endorse or promote products derived
15823b8294SYaroslav Brustinov#       from this software without specific prior written permission.
16823b8294SYaroslav Brustinov#
17823b8294SYaroslav Brustinov# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18823b8294SYaroslav Brustinov# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19823b8294SYaroslav Brustinov# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20823b8294SYaroslav Brustinov# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21823b8294SYaroslav Brustinov# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22823b8294SYaroslav Brustinov# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23823b8294SYaroslav Brustinov# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
24823b8294SYaroslav Brustinov# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25823b8294SYaroslav Brustinov# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26823b8294SYaroslav Brustinov# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
27823b8294SYaroslav Brustinov# WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28823b8294SYaroslav Brustinov# POSSIBILITY OF SUCH DAMAGE.
29823b8294SYaroslav Brustinov
30823b8294SYaroslav Brustinovfrom __future__ import print_function
31823b8294SYaroslav Brustinovimport os
32823b8294SYaroslav Brustinovimport sys
33823b8294SYaroslav Brustinovimport linecache
34823b8294SYaroslav Brustinovimport re
35823b8294SYaroslav Brustinovimport time
36823b8294SYaroslav Brustinov
37823b8294SYaroslav Brustinovimport nose
38823b8294SYaroslav Brustinov
39823b8294SYaroslav Brustinovimport termstyle
40823b8294SYaroslav Brustinov
41823b8294SYaroslav Brustinovfailure = 'FAILED'
42823b8294SYaroslav Brustinoverror = 'ERROR'
43823b8294SYaroslav Brustinovsuccess = 'passed'
44823b8294SYaroslav Brustinovskip = 'skipped'
45823b8294SYaroslav Brustinovline_length = 77
46823b8294SYaroslav Brustinov
47823b8294SYaroslav BrustinovPY3 = sys.version_info[0] >= 3
48823b8294SYaroslav Brustinovif PY3:
49823b8294SYaroslav Brustinov	to_unicode = str
50823b8294SYaroslav Brustinovelse:
51823b8294SYaroslav Brustinov	def to_unicode(s):
52823b8294SYaroslav Brustinov		try:
53823b8294SYaroslav Brustinov			return unicode(s)
54823b8294SYaroslav Brustinov		except UnicodeDecodeError:
55823b8294SYaroslav Brustinov			return unicode(repr(str(s)))
56823b8294SYaroslav Brustinov
57823b8294SYaroslav BrustinovBLACKLISTED_WRITERS = [
58823b8294SYaroslav Brustinov	'nose[\\/]result\\.pyc?$',
59823b8294SYaroslav Brustinov	'unittest[\\/]runner\\.pyc?$'
60823b8294SYaroslav Brustinov]
61823b8294SYaroslav BrustinovREDNOSE_DEBUG = False
62823b8294SYaroslav Brustinov
63823b8294SYaroslav Brustinov
64823b8294SYaroslav Brustinovclass RedNose(nose.plugins.Plugin):
65823b8294SYaroslav Brustinov	env_opt = 'NOSE_REDNOSE'
66823b8294SYaroslav Brustinov	env_opt_color = 'NOSE_REDNOSE_COLOR'
67823b8294SYaroslav Brustinov	score = 199  # just under the `coverage` module
68823b8294SYaroslav Brustinov
69823b8294SYaroslav Brustinov	def __init__(self, *args):
70823b8294SYaroslav Brustinov		super(RedNose, self).__init__(*args)
71823b8294SYaroslav Brustinov		self.reports = []
72823b8294SYaroslav Brustinov		self.error = self.success = self.failure = self.skip = 0
73823b8294SYaroslav Brustinov		self.total = 0
74823b8294SYaroslav Brustinov		self.stream = None
75823b8294SYaroslav Brustinov		self.verbose = False
76823b8294SYaroslav Brustinov		self.enabled = False
77823b8294SYaroslav Brustinov		self.tree = False
78823b8294SYaroslav Brustinov
79823b8294SYaroslav Brustinov	def options(self, parser, env=os.environ):
80823b8294SYaroslav Brustinov		global REDNOSE_DEBUG
81823b8294SYaroslav Brustinov		rednose_on = bool(env.get(self.env_opt, False))
82823b8294SYaroslav Brustinov		rednose_color = env.get(self.env_opt_color, 'auto')
83823b8294SYaroslav Brustinov		REDNOSE_DEBUG = bool(env.get('REDNOSE_DEBUG', False))
84823b8294SYaroslav Brustinov
85823b8294SYaroslav Brustinov		parser.add_option(
86823b8294SYaroslav Brustinov			"--rednose",
87823b8294SYaroslav Brustinov			action="store_true",
88823b8294SYaroslav Brustinov			default=rednose_on,
89823b8294SYaroslav Brustinov			dest="rednose",
90823b8294SYaroslav Brustinov			help="enable colour output (alternatively, set $%s=1)" % (self.env_opt,)
91823b8294SYaroslav Brustinov		)
92823b8294SYaroslav Brustinov		parser.add_option(
93823b8294SYaroslav Brustinov			"--no-color",
94823b8294SYaroslav Brustinov			action="store_false",
95823b8294SYaroslav Brustinov			dest="rednose",
96823b8294SYaroslav Brustinov			help="disable colour output"
97823b8294SYaroslav Brustinov		)
98823b8294SYaroslav Brustinov		parser.add_option(
99823b8294SYaroslav Brustinov			"--force-color",
100823b8294SYaroslav Brustinov			action="store_const",
101823b8294SYaroslav Brustinov			dest='rednose_color',
102823b8294SYaroslav Brustinov			default=rednose_color,
103823b8294SYaroslav Brustinov			const='force',
104823b8294SYaroslav Brustinov			help="force colour output when not using a TTY (alternatively, set $%s=force)" % (self.env_opt_color,)
105823b8294SYaroslav Brustinov		)
106823b8294SYaroslav Brustinov		parser.add_option(
107823b8294SYaroslav Brustinov			"--immediate",
108823b8294SYaroslav Brustinov			action="store_true",
109823b8294SYaroslav Brustinov			default=False,
110823b8294SYaroslav Brustinov			help="print errors and failures as they happen, as well as at the end"
111823b8294SYaroslav Brustinov		)
112823b8294SYaroslav Brustinov
113823b8294SYaroslav Brustinov	def configure(self, options, conf):
114823b8294SYaroslav Brustinov		if options.rednose:
115823b8294SYaroslav Brustinov			self.enabled = True
116823b8294SYaroslav Brustinov			termstyle_init = {
117823b8294SYaroslav Brustinov				'force': termstyle.enable,
118823b8294SYaroslav Brustinov				'off': termstyle.disable
119823b8294SYaroslav Brustinov			}.get(options.rednose_color, termstyle.auto)
120823b8294SYaroslav Brustinov			termstyle_init()
121823b8294SYaroslav Brustinov
122823b8294SYaroslav Brustinov			self.immediate = options.immediate
123823b8294SYaroslav Brustinov			self.verbose = options.verbosity >= 2
124823b8294SYaroslav Brustinov
125823b8294SYaroslav Brustinov	def begin(self):
126823b8294SYaroslav Brustinov		self.start_time = time.time()
127823b8294SYaroslav Brustinov		self._in_test = False
128823b8294SYaroslav Brustinov
129823b8294SYaroslav Brustinov	def _format_test_name(self, test):
130823b8294SYaroslav Brustinov		return test.shortDescription() or to_unicode(test)
131823b8294SYaroslav Brustinov
132823b8294SYaroslav Brustinov	def prepareTestResult(self, result):
133823b8294SYaroslav Brustinov		result.stream = FilteringStream(self.stream, BLACKLISTED_WRITERS)
134823b8294SYaroslav Brustinov
135823b8294SYaroslav Brustinov	def beforeTest(self, test):
136823b8294SYaroslav Brustinov		self._in_test = True
137823b8294SYaroslav Brustinov		if self.verbose:
138823b8294SYaroslav Brustinov			self._out(self._format_test_name(test) + ' ... ')
139823b8294SYaroslav Brustinov
140823b8294SYaroslav Brustinov	def afterTest(self, test):
141823b8294SYaroslav Brustinov		if self._in_test:
142823b8294SYaroslav Brustinov			self.addSkip()
143823b8294SYaroslav Brustinov
144823b8294SYaroslav Brustinov	def _print_test(self, type_, color):
145823b8294SYaroslav Brustinov		self.total += 1
146823b8294SYaroslav Brustinov		if self.verbose:
147823b8294SYaroslav Brustinov			self._outln(color(type_))
148823b8294SYaroslav Brustinov		else:
149823b8294SYaroslav Brustinov			if type_ == failure:
150823b8294SYaroslav Brustinov				short_ = 'F'
151823b8294SYaroslav Brustinov			elif type_ == error:
152823b8294SYaroslav Brustinov				short_ = 'X'
153823b8294SYaroslav Brustinov			elif type_ == skip:
154823b8294SYaroslav Brustinov				short_ = '-'
155823b8294SYaroslav Brustinov			else:
156823b8294SYaroslav Brustinov				short_ = '.'
157823b8294SYaroslav Brustinov			self._out(color(short_))
158823b8294SYaroslav Brustinov			if self.total % line_length == 0:
159823b8294SYaroslav Brustinov				self._outln()
160823b8294SYaroslav Brustinov		self._in_test = False
161823b8294SYaroslav Brustinov
162823b8294SYaroslav Brustinov	def _add_report(self, report):
163823b8294SYaroslav Brustinov		failure_type, test, err = report
164823b8294SYaroslav Brustinov		self.reports.append(report)
165823b8294SYaroslav Brustinov		if self.immediate:
166823b8294SYaroslav Brustinov			self._outln()
167823b8294SYaroslav Brustinov			self._report_test(len(self.reports), *report)
168823b8294SYaroslav Brustinov
169823b8294SYaroslav Brustinov	def addFailure(self, test, err):
170823b8294SYaroslav Brustinov		self.failure += 1
171823b8294SYaroslav Brustinov		self._add_report((failure, test, err))
172823b8294SYaroslav Brustinov		self._print_test(failure, termstyle.red)
173823b8294SYaroslav Brustinov
174823b8294SYaroslav Brustinov	def addError(self, test, err):
175823b8294SYaroslav Brustinov		if err[0].__name__ == 'SkipTest':
176823b8294SYaroslav Brustinov			self.addSkip(test, err)
177823b8294SYaroslav Brustinov			return
178823b8294SYaroslav Brustinov		self.error += 1
179823b8294SYaroslav Brustinov		self._add_report((error, test, err))
180823b8294SYaroslav Brustinov		self._print_test(error, termstyle.yellow)
181823b8294SYaroslav Brustinov
182823b8294SYaroslav Brustinov	def addSuccess(self, test):
183823b8294SYaroslav Brustinov		self.success += 1
184823b8294SYaroslav Brustinov		self._print_test(success, termstyle.green)
185823b8294SYaroslav Brustinov
186823b8294SYaroslav Brustinov	def addSkip(self, test=None, err=None):
187823b8294SYaroslav Brustinov		self.skip += 1
188823b8294SYaroslav Brustinov		self._print_test(skip, termstyle.blue)
189823b8294SYaroslav Brustinov
190823b8294SYaroslav Brustinov	def setOutputStream(self, stream):
191823b8294SYaroslav Brustinov		self.stream = stream
192823b8294SYaroslav Brustinov
193823b8294SYaroslav Brustinov	def report(self, stream):
194823b8294SYaroslav Brustinov		"""report on all registered failures and errors"""
195823b8294SYaroslav Brustinov		self._outln()
196823b8294SYaroslav Brustinov		if self.immediate:
197823b8294SYaroslav Brustinov			for x in range(0, 5):
198823b8294SYaroslav Brustinov				self._outln()
199823b8294SYaroslav Brustinov		report_num = 0
200823b8294SYaroslav Brustinov		if len(self.reports) > 0:
201823b8294SYaroslav Brustinov			for report_num, report in enumerate(self.reports):
202823b8294SYaroslav Brustinov				self._report_test(report_num + 1, *report)
203823b8294SYaroslav Brustinov			self._outln()
204823b8294SYaroslav Brustinov
205823b8294SYaroslav Brustinov		self._summarize()
206823b8294SYaroslav Brustinov
207823b8294SYaroslav Brustinov	def _summarize(self):
208823b8294SYaroslav Brustinov		"""summarize all tests - the number of failures, errors and successes"""
209823b8294SYaroslav Brustinov		self._line(termstyle.black)
210823b8294SYaroslav Brustinov		self._out("%s test%s run in %0.1f seconds" % (
211823b8294SYaroslav Brustinov			self.total,
212823b8294SYaroslav Brustinov			self._plural(self.total),
213823b8294SYaroslav Brustinov			time.time() - self.start_time))
214823b8294SYaroslav Brustinov		if self.total > self.success:
215823b8294SYaroslav Brustinov			self._outln(". ")
216823b8294SYaroslav Brustinov			additionals = []
217823b8294SYaroslav Brustinov			if self.failure > 0:
218823b8294SYaroslav Brustinov				additionals.append(termstyle.red("%s FAILED" % (
219823b8294SYaroslav Brustinov					self.failure,)))
220823b8294SYaroslav Brustinov			if self.error > 0:
221823b8294SYaroslav Brustinov				additionals.append(termstyle.yellow("%s error%s" % (
222823b8294SYaroslav Brustinov					self.error,
223823b8294SYaroslav Brustinov					self._plural(self.error) )))
224823b8294SYaroslav Brustinov			if self.skip > 0:
225823b8294SYaroslav Brustinov				additionals.append(termstyle.blue("%s skipped" % (
226823b8294SYaroslav Brustinov					self.skip)))
227823b8294SYaroslav Brustinov			self._out(', '.join(additionals))
228823b8294SYaroslav Brustinov
229823b8294SYaroslav Brustinov		self._out(termstyle.green(" (%s test%s passed)" % (
230823b8294SYaroslav Brustinov			self.success,
231823b8294SYaroslav Brustinov			self._plural(self.success) )))
232823b8294SYaroslav Brustinov		self._outln()
233823b8294SYaroslav Brustinov
234823b8294SYaroslav Brustinov	def _report_test(self, report_num, type_, test, err):
235823b8294SYaroslav Brustinov		"""report the results of a single (failing or errored) test"""
236823b8294SYaroslav Brustinov		self._line(termstyle.black)
237823b8294SYaroslav Brustinov		self._out("%s) " % (report_num))
238823b8294SYaroslav Brustinov		if type_ == failure:
239823b8294SYaroslav Brustinov			color = termstyle.red
240823b8294SYaroslav Brustinov			self._outln(color('FAIL: %s' % (self._format_test_name(test),)))
241823b8294SYaroslav Brustinov		else:
242823b8294SYaroslav Brustinov			color = termstyle.yellow
243823b8294SYaroslav Brustinov			self._outln(color('ERROR: %s' % (self._format_test_name(test),)))
244823b8294SYaroslav Brustinov
245823b8294SYaroslav Brustinov		exc_type, exc_instance, exc_trace = err
246823b8294SYaroslav Brustinov
247823b8294SYaroslav Brustinov		self._outln()
248823b8294SYaroslav Brustinov		self._outln(self._fmt_traceback(exc_trace))
249823b8294SYaroslav Brustinov		self._out(color('   ', termstyle.bold(color(exc_type.__name__)), ": "))
250823b8294SYaroslav Brustinov		self._outln(self._fmt_message(exc_instance, color))
251823b8294SYaroslav Brustinov		self._outln()
252823b8294SYaroslav Brustinov
253823b8294SYaroslav Brustinov	def _relative_path(self, path):
254823b8294SYaroslav Brustinov		"""
255823b8294SYaroslav Brustinov		If path is a child of the current working directory, the relative
256823b8294SYaroslav Brustinov		path is returned surrounded by bold xterm escape sequences.
257823b8294SYaroslav Brustinov		If path is not a child of the working directory, path is returned
258823b8294SYaroslav Brustinov		"""
259823b8294SYaroslav Brustinov		try:
260823b8294SYaroslav Brustinov			here = os.path.abspath(os.path.realpath(os.getcwd()))
261823b8294SYaroslav Brustinov			fullpath = os.path.abspath(os.path.realpath(path))
262823b8294SYaroslav Brustinov		except OSError:
263823b8294SYaroslav Brustinov			return path
264823b8294SYaroslav Brustinov		if fullpath.startswith(here):
265823b8294SYaroslav Brustinov			return termstyle.bold(fullpath[len(here)+1:])
266823b8294SYaroslav Brustinov		return path
267823b8294SYaroslav Brustinov
268823b8294SYaroslav Brustinov	def _file_line(self, tb):
269823b8294SYaroslav Brustinov		"""formats the file / lineno / function line of a traceback element"""
270823b8294SYaroslav Brustinov		prefix = "file://"
271823b8294SYaroslav Brustinov		prefix = ""
272823b8294SYaroslav Brustinov
273823b8294SYaroslav Brustinov		f = tb.tb_frame
274823b8294SYaroslav Brustinov		if '__unittest' in f.f_globals:
275823b8294SYaroslav Brustinov			# this is the magical flag that prevents unittest internal
276823b8294SYaroslav Brustinov			# code from junking up the stacktrace
277823b8294SYaroslav Brustinov			return None
278823b8294SYaroslav Brustinov
279823b8294SYaroslav Brustinov		filename = f.f_code.co_filename
280823b8294SYaroslav Brustinov		lineno = tb.tb_lineno
281823b8294SYaroslav Brustinov		linecache.checkcache(filename)
282823b8294SYaroslav Brustinov		function_name = f.f_code.co_name
283823b8294SYaroslav Brustinov
284823b8294SYaroslav Brustinov		line_contents = linecache.getline(filename, lineno, f.f_globals).strip()
285823b8294SYaroslav Brustinov
286823b8294SYaroslav Brustinov		return "    %s line %s in %s\n      %s" % (
287823b8294SYaroslav Brustinov			termstyle.blue(prefix, self._relative_path(filename)),
288823b8294SYaroslav Brustinov			lineno,
289823b8294SYaroslav Brustinov			termstyle.cyan(function_name),
290823b8294SYaroslav Brustinov			line_contents)
291823b8294SYaroslav Brustinov
292823b8294SYaroslav Brustinov	def _fmt_traceback(self, trace):
293823b8294SYaroslav Brustinov		"""format a traceback"""
294823b8294SYaroslav Brustinov		ret = []
295823b8294SYaroslav Brustinov		ret.append(termstyle.default("   Traceback (most recent call last):"))
296823b8294SYaroslav Brustinov		current_trace = trace
297823b8294SYaroslav Brustinov		while current_trace is not None:
298823b8294SYaroslav Brustinov			line = self._file_line(current_trace)
299823b8294SYaroslav Brustinov			if line is not None:
300823b8294SYaroslav Brustinov				ret.append(line)
301823b8294SYaroslav Brustinov			current_trace = current_trace.tb_next
302823b8294SYaroslav Brustinov		return '\n'.join(ret)
303823b8294SYaroslav Brustinov
304823b8294SYaroslav Brustinov	def _fmt_message(self, exception, color):
305823b8294SYaroslav Brustinov		orig_message_lines = to_unicode(exception).splitlines()
306823b8294SYaroslav Brustinov
307823b8294SYaroslav Brustinov		if len(orig_message_lines) == 0:
308823b8294SYaroslav Brustinov			return ''
309823b8294SYaroslav Brustinov		message_lines = [color(orig_message_lines[0])]
310823b8294SYaroslav Brustinov		for line in orig_message_lines[1:]:
311823b8294SYaroslav Brustinov			match = re.match('^---.* begin captured stdout.*----$', line)
312823b8294SYaroslav Brustinov			if match:
313823b8294SYaroslav Brustinov				color = None
314823b8294SYaroslav Brustinov				message_lines.append('')
315823b8294SYaroslav Brustinov			line = '   ' + line
316823b8294SYaroslav Brustinov			message_lines.append(color(line) if color is not None else line)
317823b8294SYaroslav Brustinov		return '\n'.join(message_lines)
318823b8294SYaroslav Brustinov
319823b8294SYaroslav Brustinov	def _out(self, msg='', newline=False):
320823b8294SYaroslav Brustinov		self.stream.write(msg)
321823b8294SYaroslav Brustinov		if newline:
322823b8294SYaroslav Brustinov			self.stream.write('\n')
323823b8294SYaroslav Brustinov
324823b8294SYaroslav Brustinov	def _outln(self, msg=''):
325823b8294SYaroslav Brustinov		self._out(msg, True)
326823b8294SYaroslav Brustinov
327823b8294SYaroslav Brustinov	def _plural(self, num):
328823b8294SYaroslav Brustinov		return '' if num == 1 else 's'
329823b8294SYaroslav Brustinov
330823b8294SYaroslav Brustinov	def _line(self, color=termstyle.reset, char='-'):
331823b8294SYaroslav Brustinov		"""
332823b8294SYaroslav Brustinov		print a line of separator characters (default '-')
333823b8294SYaroslav Brustinov		in the given colour (default black)
334823b8294SYaroslav Brustinov		"""
335823b8294SYaroslav Brustinov		self._outln(color(char * line_length))
336823b8294SYaroslav Brustinov
337823b8294SYaroslav Brustinov
338823b8294SYaroslav Brustinovimport traceback
339823b8294SYaroslav Brustinovimport sys
340823b8294SYaroslav Brustinov
341823b8294SYaroslav Brustinov
342823b8294SYaroslav Brustinovclass FilteringStream(object):
343823b8294SYaroslav Brustinov	"""
344823b8294SYaroslav Brustinov	A wrapper for a stream that will filter
345823b8294SYaroslav Brustinov	calls to `write` and `writeln` to ignore calls
346823b8294SYaroslav Brustinov	from blacklisted callers
347823b8294SYaroslav Brustinov	(implemented as a regex on their filename, according
348823b8294SYaroslav Brustinov	to traceback.extract_stack())
349823b8294SYaroslav Brustinov
350823b8294SYaroslav Brustinov	It's super hacky, but there seems to be no other way
351823b8294SYaroslav Brustinov	to suppress nose's default output
352823b8294SYaroslav Brustinov	"""
353823b8294SYaroslav Brustinov	def __init__(self, stream, excludes):
354823b8294SYaroslav Brustinov		self.__stream = stream
355823b8294SYaroslav Brustinov		self.__excludes = list(map(re.compile, excludes))
356823b8294SYaroslav Brustinov
357823b8294SYaroslav Brustinov	def __should_filter(self):
358823b8294SYaroslav Brustinov		try:
359823b8294SYaroslav Brustinov			stack = traceback.extract_stack(limit=3)[0]
360823b8294SYaroslav Brustinov			filename = stack[0]
361823b8294SYaroslav Brustinov			pattern_matches_filename = lambda pattern: pattern.search(filename)
362823b8294SYaroslav Brustinov			should_filter = any(map(pattern_matches_filename, self.__excludes))
363823b8294SYaroslav Brustinov			if REDNOSE_DEBUG:
364823b8294SYaroslav Brustinov				print >> sys.stderr, "REDNOSE_DEBUG: got write call from %s, should_filter = %s" % (
365823b8294SYaroslav Brustinov						filename, should_filter)
366823b8294SYaroslav Brustinov			return should_filter
367823b8294SYaroslav Brustinov		except StandardError as e:
368823b8294SYaroslav Brustinov			if REDNOSE_DEBUG:
369823b8294SYaroslav Brustinov				print("\nError in rednose filtering: %s" % (e,), file=sys.stderr)
370823b8294SYaroslav Brustinov				traceback.print_exc(sys.stderr)
371823b8294SYaroslav Brustinov			return False
372823b8294SYaroslav Brustinov
373823b8294SYaroslav Brustinov	def write(self, *a):
374823b8294SYaroslav Brustinov		if self.__should_filter():
375823b8294SYaroslav Brustinov			return
376823b8294SYaroslav Brustinov		return self.__stream.write(*a)
377823b8294SYaroslav Brustinov
378823b8294SYaroslav Brustinov	def writeln(self, *a):
379823b8294SYaroslav Brustinov		if self.__should_filter():
380823b8294SYaroslav Brustinov			return
381823b8294SYaroslav Brustinov		return self.__stream.writeln(*a)
382823b8294SYaroslav Brustinov
383823b8294SYaroslav Brustinov	# pass non-known methods through to self.__stream
384823b8294SYaroslav Brustinov	def __getattr__(self, name):
385823b8294SYaroslav Brustinov		if REDNOSE_DEBUG:
386823b8294SYaroslav Brustinov			print("REDNOSE_DEBUG: getting attr %s" % (name,), file=sys.stderr)
387823b8294SYaroslav Brustinov		return getattr(self.__stream, name)
388