Я отлаживал свой модуль отладки и полагаюсь на попытку ... catch для обнаружения TypeError и правильного форматирования моего сообщения, затем я заметил, что при передаче словарей Python не вызывает традиционное исключение.
>>> 'var' % {1: 'variable'}
'var'
>>> 'var' % (1,)
Traceback (most recent call last):
File "<string>", line 1, in <module>
Это минимальный пример с модулем ведения журнала:
import logging
class SmartLogRecord(logging.LogRecord):
def _getMessage(self, remaining_arguments):
try:
if self.args:
remaining_arguments.append( self.msg % self.args )
else:
remaining_arguments.append( self.msg )
return False
except TypeError as error:
last = self.args[-1]
self.args = self.args[:-1]
remaining_arguments.append( str( last ) )
if len( self.args ):
return True
else:
remaining_arguments.append( self.msg )
return False
def getMessage(self):
"""
Return the message for this LogRecord.
Return the message for this LogRecord after merging any user-supplied
arguments with the message.
"""
remaining_arguments = []
self.msg = str( self.msg )
while self._getMessage( remaining_arguments ): pass
return " ".join( reversed( remaining_arguments ) )
logging.setLogRecordFactory(SmartLogRecord)
var = 'SmartLogRecord'
logging.warning('I am a', var)
dumb = {1: 'variable'}
logging.warning('I am a', dumb)
Запустив его, вы получите:
WARNING:root:I am a SmartLogRecord
WARNING:root:I am a
Как видите, последнее сообщение dumb
было потеряно.
Я просто неправильно написал имя и не заметил.
Я думаю, что наблюдаемое поведение соответствует документы.
If format requires a single argument, values may be a single non-tuple object. [5] Otherwise, values must be a tuple with exactly the number of items specified by the format string, or a single mapping object (for example, a dictionary).
Примечание [5]:
To format only a tuple you should therefore provide a singleton tuple whose only element is the tuple to be formatted.
Это объясняет, что кортеж принимается только в том случае, если он точно соответствует требованиям строки формата. Кортеж с одним элементом, который сам по себе не является кортежем, не может соответствовать ни одной строке формата и всегда вызывает исключение.
Это также объясняет, что dict всегда принимается как тип, но может приводить к другим ошибкам.
И на всякий случай я ошибаюсь, есть всеохватывающая возможность, что вы только что обнаружили еще одну "причуду". Они прямо предупреждают:
The formatting operations described here exhibit a variety of quirks that lead to a number of common errors (such as failing to display tuples and dictionaries correctly). Using the newer formatted string literals or the str.format() interface helps avoid these errors.
В качестве обходного пути этой проблемы я предлагаю следующее решение:
import sys
import logging
if sys.version_info[0] < 3:
is_python2 = True
from collections import MutableMapping
else:
from collections.abc import MutableMapping
class SmartLogRecord(logging.LogRecord):
def _getMessage(self, remaining_arguments):
try:
args = self.args
if args:
# if isinstance( args, dict ):
if isinstance( args, MutableMapping ):
new_msg = self.msg % args
if new_msg == self.msg:
remaining_arguments.append( str( args ) )
remaining_arguments.append( new_msg )
else:
remaining_arguments.append( new_msg )
else:
remaining_arguments.append( self.msg % args )
else:
remaining_arguments.append( self.msg )
return False
except TypeError as error:
self.args = args[:-1]
remaining_arguments.append( str( args[-1] ) )
if len( args ) - 1 > 0:
return True
else:
remaining_arguments.append( self.msg )
return False
def getMessage(self):
"""
Return the message for this LogRecord.
Return the message for this LogRecord after merging any user-supplied
arguments with the message.
"""
remaining_arguments = []
self.msg = str( self.msg )
while self._getMessage( remaining_arguments ): pass
return " ".join( reversed( remaining_arguments ) )
logging.setLogRecordFactory(SmartLogRecord)
var = 'SmartLogRecord'
logging.warning('I am a', var)
dumb = {1: 'variable'}
logging.warning('I am a', dumb)
Что работает правильно:
WARNING:root:I am a SmartLogRecord
WARNING:root:I am a {1: 'variable'}
ОТ: Вы действительно хотите внести в свой модуль орфографическую ошибку, чтобы сохранить два символа для имени вашей переменной?
remaing
действительно режет мне глаза, и это тоже плохая практика.