klen / muffin

Muffin is a fast, simple and asyncronous web-framework for Python 3

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Inconsistent logging when importing config

MarSoft opened this issue · comments

There is something strange with config import problems handling.
When you specify a non-existing module, you get a message stating Error importing <modulename> (detailed since #32). When you have some imports problem within your config module, you get the same message.
At the same time, when there is some other error (like SyntaxError or whatever), it is just thrown immediately.

Also, logging actual error message is postponed to application start; but sometimes initialization of other application components depend on some config values, and will fail when config is no provided. Example:Rework Application.__init__ so that logging can be done

from muffin import Application
from redis import Redis

app = Application('test', CONFIG='no_such_config_module')
redis = Redis(app.cfg.REDIS_URL)

This example will yield AttributeError about missing REDIS_URL, without any notice that it could not find a config module. This is misleading.

So there are two improvements I would like to suggest, or alternatively one simplification:

  1. When handling ImportError in Application.cfg, check that exc.name matches module and just reraise exception otherwise. This will disable special processing when ImportError happened inside of configuration hierarchy.
  2. Change postponed logging to fire before returning from Application.__init__, not after initialization of the rest of application.
  3. Alternative way is to just abandon special processing of configuration ImportErrors, because if user provided wrong configuration value then the app is unlikely to work fine, and explicit ImportError from config loading routine looks more adequate for me than (semi-)silent ignoring such errors.

Or, as a modification of 3rd way, we can catch ImportError and re-throw it with altered message to make it obvious that problem is config-related:

try:
    module = import_module(module)
    config.update...
except ImportError as exc:
    raise ImportError('Failed to load config module %s' % module) from exc

In such way, the user will see both original ImportError and a notification that it is config-related:

$ muffin app1
ERROR:root:Error loading configuration module no_such_module
Traceback (most recent call last):
  File "/home/mars/opensource/muffin/muffin/app.py", line 130, in cfg
    module = import_module(module)
  File "/usr/lib/python3.5/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 986, in _gcd_import
  File "<frozen importlib._bootstrap>", line 969, in _find_and_load
  File "<frozen importlib._bootstrap>", line 956, in _find_and_load_unlocked
ImportError: No module named 'no_such_module'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/mars/opensource/muffin/muffin/manage.py", line 271, in run
    app = import_app(app_uri)
  File "/home/mars/pebble/PebWare/env/lib/python3.5/site-packages/gunicorn/util.py", line 357, in import_app
    __import__(module)
  File "/tmp/app1.py", line 4, in <module>
    app = Application('test', CONFIG='no_such_module')
  File "/home/mars/opensource/muffin/muffin/app.py", line 79, in __init__
    self.cfg.update(OPTIONS)
  File "/home/mars/pebble/PebWare/env/lib/python3.5/site-packages/cached_property.py", line 26, in __get__
    value = obj.__dict__[self.func.__name__] = self.func(obj)
  File "/home/mars/opensource/muffin/muffin/app.py", line 137, in cfg
    raise ImportError('Error loading configuration module %s' % module) from exc
ImportError: Error loading configuration module no_such_module