using configobj string interpolation and logging.config.dictConfig (was Re: dictConfig: logging.StreamHandler object is not iterable.)

Tim Williams tjandacw at cox.net
Thu May 25 15:57:14 EDT 2017


On Wednesday, May 24, 2017 at 5:47:37 PM UTC-4, Peter Otten wrote:
> Tim Williams wrote:
> 
> > Just as a followup, if I use 'unrepr=True' in my ConfigObj, I don't have
> > to convert the strings.
> 
> I'd keep it simple and would use JSON...

I looked at JSON at first, but went with configobj because I didn't see where it did string interpolation, which I needed for other parts of my INI file, and I'm trying to use it to specify my log file in my handler.

Which brings me to ...

I have this stripped down INI file:

[loggng]
version = 1
level = 'INFO'
RootDir = 'TestData'
CaptureDrive = 'C:/'
LogFile = '%(CaptureDrive)s%(RootDir)s/test.log'
	[[formatters]]
		[[[fmt1]]]
			format = '%(asctime)s: (%(levelname)s)  %(message)s'
			datefmt =
	[[handlers]]
		[[[console]]]
			class = 'logging.StreamHandler'
			level = 'INFO'
			stream = 'ext://sys.stdout'
		[[[file]]]
			class = 'logging.FileHandler'
			level = 'WARN'
			filename = '%(LogFile)s'
			#filename = 'cfg://loggng.LogFile'
	[[loggers]]
		[[[root]]]
		level = 'INFO'
		handlers = ['file','console']

When I parse the INI file with configobj:
config = configobj.ConfigObj('loggingtest.ini', unrepr=True, raise_errors=True)

config['loggng']['handlers']['file']['filename'] evaluates correctly to C:/TestData/test.log

However, when I try to call logging.config.dictConfig() on it, the stream that is opened on creating the logging.FileHandler object is "%(LogFile)s", not "C:/TestData/test.log".

Stepping into the debugger, I see that the dictionary is changed in BaseConfigurator.convert()

    def convert(self, value):
        """
        Convert values to an appropriate type. dicts, lists and tuples are
        replaced by their converting alternatives. Strings are checked to
        see if they have a conversion format and are converted if they do.
        """
        if not isinstance(value, ConvertingDict) and isinstance(value, dict):
            value = ConvertingDict(value)
            value.configurator = self

BEFORE calling ConvertingDict(value), the value dict has the correct value for key filename:

>>> value
{'class': 'logging.FileHandler', 'level': 'WARN', 'filename': 'C:/TestData/test.log'}

AFTER calling ConvertingDict(value):

>>> value
{'filename': '%(LogFile)s', 'class': 'logging.FileHandler', 'level': 'WARN'}

If I try to use 
			filename = 'cfg://loggng.LogFile'
in my INI file, I get a ValueError calling logging.config.dictConfig(config):


pydev debugger: starting (pid: 70744)
Traceback (most recent call last):
  File "C:\Python34\lib\logging\config.py", line 557, in configure
    handler = self.configure_handler(handlers[name])
  File "C:\Python34\lib\logging\config.py", line 723, in configure_handler
    kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
  File "C:\Python34\lib\logging\config.py", line 723, in <listcomp>
    kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
  File "C:\Python34\lib\logging\config.py", line 318, in __getitem__
    return self.convert_with_key(key, value)
  File "C:\Python34\lib\logging\config.py", line 284, in convert_with_key
    result = self.configurator.convert(value)
  File "C:\Python34\lib\logging\config.py", line 455, in convert
    value = converter(suffix)
  File "C:\Python34\lib\logging\config.py", line 404, in cfg_convert
    d = self.config[m.groups()[0]]
  File "C:\Python34\lib\logging\config.py", line 317, in __getitem__
    value = dict.__getitem__(self, key)
KeyError: 'loggng'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "L:\timothy.j.williams1\Documents\My Programs\eclipse_neon\eclipse\plugins\org.python.pydev_5.3.0.201610121612\pysrc\pydevd.py", line 1531, in <module>
    globals = debugger.run(setup['file'], None, None, is_module)
  File "L:\timothy.j.williams1\Documents\My Programs\eclipse_neon\eclipse\plugins\org.python.pydev_5.3.0.201610121612\pysrc\pydevd.py", line 938, in run
    pydev_imports.execfile(file, globals, locals)  # execute the script
  File "L:\timothy.j.williams1\Documents\My Programs\eclipse_neon\eclipse\plugins\org.python.pydev_5.3.0.201610121612\pysrc\_pydev_imps\_pydev_execfile.py", line 25, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "L:\workspace\MyPython\src\testlog.py", line 8, in <module>
    logging.config.dictConfig(config)
  File "C:\Python34\lib\logging\config.py", line 789, in dictConfig
    dictConfigClass(config).configure()
  File "C:\Python34\lib\logging\config.py", line 565, in configure
    '%r: %s' % (name, e))
ValueError: Unable to configure handler 'file': 'loggng'

testlog.py:
import logging, logging.config, logging.handlers
import configobj

logconfig = configobj.ConfigObj('loggingtest.ini', unrepr=True, raise_errors=True)

config=dict(logconfig['loggng'])
logging.config.dictConfig(config)
logger=logging.getLogger('root')
print(config['handlers']['file'])
print(logger.handlers[0].stream)

loggingtest.ini:
[loggng]
version = 1
level = 'INFO'
RootDir = 'TestData'
CaptureDrive = 'C:/'
LogFile = '%(CaptureDrive)s%(RootDir)s/test.log'
	[[formatters]]
		[[[fmt1]]]
			format = '%(asctime)s: (%(levelname)s)  %(message)s'
			datefmt =
	[[handlers]]
		[[[console]]]
			class = 'logging.StreamHandler'
			level = 'INFO'
			stream = 'ext://sys.stdout'
		[[[file]]]
			class = 'logging.FileHandler'
			level = 'WARN'
			filename = '%(LogFile)s'
			#filename = 'cfg://loggng.LogFile'
	[[loggers]]
		[[[root]]]
		level = 'INFO'
		handlers = ['file','console']

Thanks for any help.




More information about the Python-list mailing list