Почему 'var'% {1: 'variable'} печатает 'var' вместо того, чтобы вызывать исключение TypeError, как это делает 'var'% (1,)? - PullRequest
0 голосов
/ 26 октября 2018

Я отлаживал свой модуль отладки, и я полагаюсь на попытку ... ловить, чтобы обнаружить 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 сообщение было потеряно.

Ответы [ 2 ]

0 голосов
/ 06 мая 2019

В качестве обходного пути для этой проблемы я придумаю следующее решение:

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'}
0 голосов
/ 26 октября 2018

Я думаю, что наблюдаемое поведение соответствует документам .

Если для формата требуется один аргумент, значения могут быть одним объектом, не являющимся кортежем.[5] В противном случае значения должны представлять собой кортеж с точным количеством элементов, указанных в строке формата, или один объект сопоставления (например, словарь).

Примечание [5]:

Для форматирования только кортежа вы должны предоставить одноэлементный кортеж, единственным элементом которого является кортеж для форматирования.

Это объясняет, что кортеж принимается, только если он точносоответствует требованиям форматной строки.Кортеж с одним элементом, который сам не является кортежем, не может соответствовать ни одной строке формата и всегда вызывает исключение.

Это также объясняет, что dict всегда принимается как тип, но может вызывать другие ошибки.


И на всякий случай, я ошибаюсь, есть универсальная возможность, что вы только что обнаружили еще одну «причуду».Они явно предупреждают:

Операции форматирования, описанные здесь, обнаруживают различные причуды, которые приводят к ряду распространенных ошибок (таких как неправильное отображение кортежей и словарей).Использование более новых форматированных строковых литералов или интерфейса str.format () помогает избежать этих ошибок.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...