mirror of https://github.com/python/cpython
gh-112997: Don't log arguments in asyncio unless debugging (#115667)
Nothing else in Python generally logs the contents of variables, so this can be very unexpected for developers and could leak sensitive information in to terminals and log files.
This commit is contained in:
parent
a355f60b03
commit
5a1559d949
|
@ -54,7 +54,8 @@ class Handle:
|
||||||
info.append('cancelled')
|
info.append('cancelled')
|
||||||
if self._callback is not None:
|
if self._callback is not None:
|
||||||
info.append(format_helpers._format_callback_source(
|
info.append(format_helpers._format_callback_source(
|
||||||
self._callback, self._args))
|
self._callback, self._args,
|
||||||
|
debug=self._loop.get_debug()))
|
||||||
if self._source_traceback:
|
if self._source_traceback:
|
||||||
frame = self._source_traceback[-1]
|
frame = self._source_traceback[-1]
|
||||||
info.append(f'created at {frame[0]}:{frame[1]}')
|
info.append(f'created at {frame[0]}:{frame[1]}')
|
||||||
|
@ -90,7 +91,8 @@ class Handle:
|
||||||
raise
|
raise
|
||||||
except BaseException as exc:
|
except BaseException as exc:
|
||||||
cb = format_helpers._format_callback_source(
|
cb = format_helpers._format_callback_source(
|
||||||
self._callback, self._args)
|
self._callback, self._args,
|
||||||
|
debug=self._loop.get_debug())
|
||||||
msg = f'Exception in callback {cb}'
|
msg = f'Exception in callback {cb}'
|
||||||
context = {
|
context = {
|
||||||
'message': msg,
|
'message': msg,
|
||||||
|
|
|
@ -19,19 +19,26 @@ def _get_function_source(func):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _format_callback_source(func, args):
|
def _format_callback_source(func, args, *, debug=False):
|
||||||
func_repr = _format_callback(func, args, None)
|
func_repr = _format_callback(func, args, None, debug=debug)
|
||||||
source = _get_function_source(func)
|
source = _get_function_source(func)
|
||||||
if source:
|
if source:
|
||||||
func_repr += f' at {source[0]}:{source[1]}'
|
func_repr += f' at {source[0]}:{source[1]}'
|
||||||
return func_repr
|
return func_repr
|
||||||
|
|
||||||
|
|
||||||
def _format_args_and_kwargs(args, kwargs):
|
def _format_args_and_kwargs(args, kwargs, *, debug=False):
|
||||||
"""Format function arguments and keyword arguments.
|
"""Format function arguments and keyword arguments.
|
||||||
|
|
||||||
Special case for a single parameter: ('hello',) is formatted as ('hello').
|
Special case for a single parameter: ('hello',) is formatted as ('hello').
|
||||||
|
|
||||||
|
Note that this function only returns argument details when
|
||||||
|
debug=True is specified, as arguments may contain sensitive
|
||||||
|
information.
|
||||||
"""
|
"""
|
||||||
|
if not debug:
|
||||||
|
return '()'
|
||||||
|
|
||||||
# use reprlib to limit the length of the output
|
# use reprlib to limit the length of the output
|
||||||
items = []
|
items = []
|
||||||
if args:
|
if args:
|
||||||
|
@ -41,10 +48,11 @@ def _format_args_and_kwargs(args, kwargs):
|
||||||
return '({})'.format(', '.join(items))
|
return '({})'.format(', '.join(items))
|
||||||
|
|
||||||
|
|
||||||
def _format_callback(func, args, kwargs, suffix=''):
|
def _format_callback(func, args, kwargs, *, debug=False, suffix=''):
|
||||||
if isinstance(func, functools.partial):
|
if isinstance(func, functools.partial):
|
||||||
suffix = _format_args_and_kwargs(args, kwargs) + suffix
|
suffix = _format_args_and_kwargs(args, kwargs, debug=debug) + suffix
|
||||||
return _format_callback(func.func, func.args, func.keywords, suffix)
|
return _format_callback(func.func, func.args, func.keywords,
|
||||||
|
debug=debug, suffix=suffix)
|
||||||
|
|
||||||
if hasattr(func, '__qualname__') and func.__qualname__:
|
if hasattr(func, '__qualname__') and func.__qualname__:
|
||||||
func_repr = func.__qualname__
|
func_repr = func.__qualname__
|
||||||
|
@ -53,7 +61,7 @@ def _format_callback(func, args, kwargs, suffix=''):
|
||||||
else:
|
else:
|
||||||
func_repr = repr(func)
|
func_repr = repr(func)
|
||||||
|
|
||||||
func_repr += _format_args_and_kwargs(args, kwargs)
|
func_repr += _format_args_and_kwargs(args, kwargs, debug=debug)
|
||||||
if suffix:
|
if suffix:
|
||||||
func_repr += suffix
|
func_repr += suffix
|
||||||
return func_repr
|
return func_repr
|
||||||
|
|
|
@ -2250,7 +2250,7 @@ class HandleTests(test_utils.TestCase):
|
||||||
h = asyncio.Handle(noop, (1, 2), self.loop)
|
h = asyncio.Handle(noop, (1, 2), self.loop)
|
||||||
filename, lineno = test_utils.get_function_source(noop)
|
filename, lineno = test_utils.get_function_source(noop)
|
||||||
self.assertEqual(repr(h),
|
self.assertEqual(repr(h),
|
||||||
'<Handle noop(1, 2) at %s:%s>'
|
'<Handle noop() at %s:%s>'
|
||||||
% (filename, lineno))
|
% (filename, lineno))
|
||||||
|
|
||||||
# cancelled handle
|
# cancelled handle
|
||||||
|
@ -2268,14 +2268,14 @@ class HandleTests(test_utils.TestCase):
|
||||||
# partial function
|
# partial function
|
||||||
cb = functools.partial(noop, 1, 2)
|
cb = functools.partial(noop, 1, 2)
|
||||||
h = asyncio.Handle(cb, (3,), self.loop)
|
h = asyncio.Handle(cb, (3,), self.loop)
|
||||||
regex = (r'^<Handle noop\(1, 2\)\(3\) at %s:%s>$'
|
regex = (r'^<Handle noop\(\)\(\) at %s:%s>$'
|
||||||
% (re.escape(filename), lineno))
|
% (re.escape(filename), lineno))
|
||||||
self.assertRegex(repr(h), regex)
|
self.assertRegex(repr(h), regex)
|
||||||
|
|
||||||
# partial function with keyword args
|
# partial function with keyword args
|
||||||
cb = functools.partial(noop, x=1)
|
cb = functools.partial(noop, x=1)
|
||||||
h = asyncio.Handle(cb, (2, 3), self.loop)
|
h = asyncio.Handle(cb, (2, 3), self.loop)
|
||||||
regex = (r'^<Handle noop\(x=1\)\(2, 3\) at %s:%s>$'
|
regex = (r'^<Handle noop\(\)\(\) at %s:%s>$'
|
||||||
% (re.escape(filename), lineno))
|
% (re.escape(filename), lineno))
|
||||||
self.assertRegex(repr(h), regex)
|
self.assertRegex(repr(h), regex)
|
||||||
|
|
||||||
|
@ -2316,6 +2316,24 @@ class HandleTests(test_utils.TestCase):
|
||||||
'<Handle cancelled noop(1, 2) at %s:%s created at %s:%s>'
|
'<Handle cancelled noop(1, 2) at %s:%s created at %s:%s>'
|
||||||
% (filename, lineno, create_filename, create_lineno))
|
% (filename, lineno, create_filename, create_lineno))
|
||||||
|
|
||||||
|
# partial function
|
||||||
|
cb = functools.partial(noop, 1, 2)
|
||||||
|
create_lineno = sys._getframe().f_lineno + 1
|
||||||
|
h = asyncio.Handle(cb, (3,), self.loop)
|
||||||
|
regex = (r'^<Handle noop\(1, 2\)\(3\) at %s:%s created at %s:%s>$'
|
||||||
|
% (re.escape(filename), lineno,
|
||||||
|
re.escape(create_filename), create_lineno))
|
||||||
|
self.assertRegex(repr(h), regex)
|
||||||
|
|
||||||
|
# partial function with keyword args
|
||||||
|
cb = functools.partial(noop, x=1)
|
||||||
|
create_lineno = sys._getframe().f_lineno + 1
|
||||||
|
h = asyncio.Handle(cb, (2, 3), self.loop)
|
||||||
|
regex = (r'^<Handle noop\(x=1\)\(2, 3\) at %s:%s created at %s:%s>$'
|
||||||
|
% (re.escape(filename), lineno,
|
||||||
|
re.escape(create_filename), create_lineno))
|
||||||
|
self.assertRegex(repr(h), regex)
|
||||||
|
|
||||||
def test_handle_source_traceback(self):
|
def test_handle_source_traceback(self):
|
||||||
loop = asyncio.get_event_loop_policy().new_event_loop()
|
loop = asyncio.get_event_loop_policy().new_event_loop()
|
||||||
loop.set_debug(True)
|
loop.set_debug(True)
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Stop logging potentially sensitive callback arguments in :mod:`asyncio`
|
||||||
|
unless debug mode is active.
|
Loading…
Reference in New Issue