[settings] Better impl. of Setting class, tests for it and refactor hardcoded settings to use it.

This commit is contained in:
Romain Dorgueil
2017-07-05 12:41:14 +02:00
parent 6ef25deac9
commit 9801c75720
8 changed files with 115 additions and 21 deletions

View File

@ -45,7 +45,7 @@ def run(graph, strategy=None, plugins=None, services=None):
from bonobo import settings from bonobo import settings
settings.check() settings.check()
if not settings.QUIET: # pragma: no cover if not settings.QUIET.get(): # pragma: no cover
if _is_interactive_console(): if _is_interactive_console():
from bonobo.ext.console import ConsoleOutputPlugin from bonobo.ext.console import ConsoleOutputPlugin
if ConsoleOutputPlugin not in plugins: if ConsoleOutputPlugin not in plugins:

View File

@ -27,9 +27,9 @@ def entrypoint(args=None):
args = parser.parse_args(args).__dict__ args = parser.parse_args(args).__dict__
if args.pop('debug', False): if args.pop('debug', False):
settings.DEBUG = True settings.DEBUG.set(True)
settings.LOGGING_LEVEL = logging.DEBUG settings.LOGGING_LEVEL.set(logging.DEBUG)
logging.set_level(settings.LOGGING_LEVEL) logging.set_level(settings.LOGGING_LEVEL.get())
logger.debug('Command: ' + args['command'] + ' Arguments: ' + repr(args)) logger.debug('Command: ' + args['command'] + ' Arguments: ' + repr(args))
commands[args.pop('command')](**args) commands[args.pop('command')](**args)

View File

@ -31,10 +31,10 @@ def execute(filename, module, install=False, quiet=False, verbose=False):
from bonobo import Graph, run, settings from bonobo import Graph, run, settings
if quiet: if quiet:
settings.QUIET = True settings.QUIET.set(True)
if verbose: if verbose:
settings.DEBUG = True settings.DEBUG.set(True)
if filename: if filename:
if os.path.isdir(filename): if os.path.isdir(filename):

View File

@ -65,7 +65,7 @@ class ConsoleOutputPlugin(Plugin):
for i in context.graph.topologically_sorted_indexes: for i in context.graph.topologically_sorted_indexes:
node = context[i] node = context[i]
name_suffix = '({})'.format(i) if settings.DEBUG else '' name_suffix = '({})'.format(i) if settings.DEBUG.get() else ''
if node.alive: if node.alive:
_line = ''.join( _line = ''.join(
( (
@ -100,7 +100,7 @@ class ConsoleOutputPlugin(Plugin):
print(MOVE_CURSOR_UP(t_cnt + 2), file=sys.stderr) print(MOVE_CURSOR_UP(t_cnt + 2), file=sys.stderr)
def _write(self, graph_context, rewind): def _write(self, graph_context, rewind):
if settings.PROFILE: if settings.PROFILE.get():
if self.counter % 10 and self._append_cache: if self.counter % 10 and self._append_cache:
append = self._append_cache append = self._append_cache
else: else:

View File

@ -75,4 +75,4 @@ def get_logger(name='bonobo'):
getLogger = get_logger getLogger = get_logger
# Setup formating and level. # Setup formating and level.
setup(level=settings.LOGGING_LEVEL) setup(level=settings.LOGGING_LEVEL.get())

View File

@ -69,7 +69,7 @@ def _count_counter(self, context):
class PrettyPrinter(Configurable): class PrettyPrinter(Configurable):
def call(self, *args, **kwargs): def call(self, *args, **kwargs):
formater = self._format_quiet if settings.QUIET else self._format_console formater = self._format_quiet if settings.QUIET.get() else self._format_console
for i, (item, value) in enumerate(itertools.chain(enumerate(args), kwargs.items())): for i, (item, value) in enumerate(itertools.chain(enumerate(args), kwargs.items())):
print(formater(i, item, value)) print(formater(i, item, value))

View File

@ -5,6 +5,10 @@ from bonobo.errors import ValidationError
def to_bool(s): def to_bool(s):
if s is None:
return False
if type(s) is bool:
return s
if len(s): if len(s):
if s.lower() in ('f', 'false', 'n', 'no', '0'): if s.lower() in ('f', 'false', 'n', 'no', '0'):
return False return False
@ -13,7 +17,18 @@ def to_bool(s):
class Setting: class Setting:
def __init__(self, name, default=None, validator=None): __all__ = {}
@classmethod
def clear_all(cls):
for setting in Setting.__all__.values():
setting.clear()
def __new__(cls, name, *args, **kwargs):
Setting.__all__[name] = super().__new__(cls)
return Setting.__all__[name]
def __init__(self, name, default=None, validator=None, formatter=None):
self.name = name self.name = name
if default: if default:
@ -21,15 +36,14 @@ class Setting:
else: else:
self.default = lambda: None self.default = lambda: None
if validator: self.validator = validator
self.validator = validator self.formatter = formatter
else:
self.validator = None
def __repr__(self): def __repr__(self):
return '<Setting {}={!r}>'.format(self.name, self.get()) return '<Setting {}={!r}>'.format(self.name, self.get())
def set(self, value): def set(self, value):
value = self.formatter(value) if self.formatter else value
if self.validator and not self.validator(value): if self.validator and not self.validator(value):
raise ValidationError('Invalid value {!r} for setting {}.'.format(value, self.name)) raise ValidationError('Invalid value {!r} for setting {}.'.format(value, self.name))
self.value = value self.value = value
@ -38,21 +52,35 @@ class Setting:
try: try:
return self.value return self.value
except AttributeError: except AttributeError:
self.value = self.default() value = os.environ.get(self.name, None)
if value is None:
value = self.default()
self.set(value)
return self.value return self.value
def clear(self):
try:
del self.value
except AttributeError:
pass
# Debug/verbose mode. # Debug/verbose mode.
DEBUG = to_bool(os.environ.get('DEBUG', 'f')) DEBUG = Setting('DEBUG', formatter=to_bool, default=False)
# Profile mode. # Profile mode.
PROFILE = to_bool(os.environ.get('PROFILE', 'f')) PROFILE = Setting('PROFILE', formatter=to_bool, default=False)
# Quiet mode. # Quiet mode.
QUIET = to_bool(os.environ.get('QUIET', 'f')) QUIET = Setting('QUIET', formatter=to_bool, default=False)
# Logging level. # Logging level.
LOGGING_LEVEL = logging.DEBUG if DEBUG else logging.INFO LOGGING_LEVEL = Setting(
'LOGGING_LEVEL',
formatter=logging._checkLevel,
validator=logging._checkLevel,
default=lambda: logging.DEBUG if DEBUG.get() else logging.INFO
)
# Input/Output format for transformations # Input/Output format for transformations
IOFORMAT_ARG0 = 'arg0' IOFORMAT_ARG0 = 'arg0'
@ -67,5 +95,8 @@ IOFORMAT = Setting('IOFORMAT', default=IOFORMAT_KWARGS, validator=IOFORMATS.__co
def check(): def check():
if DEBUG and QUIET: if DEBUG.get() and QUIET.get():
raise RuntimeError('I cannot be verbose and quiet at the same time.') raise RuntimeError('I cannot be verbose and quiet at the same time.')
clear_all = Setting.clear_all

63
tests/test_settings.py Normal file
View File

@ -0,0 +1,63 @@
import logging
from os import environ
from unittest.mock import patch
import pytest
from bonobo import settings
TEST_SETTING = 'TEST_SETTING'
def test_to_bool():
assert not settings.to_bool('')
assert not settings.to_bool('FALSE')
assert not settings.to_bool('NO')
assert not settings.to_bool('0')
assert settings.to_bool('yup')
assert settings.to_bool('True')
assert settings.to_bool('yes')
assert settings.to_bool('1')
def test_setting():
s = settings.Setting(TEST_SETTING)
assert s.get() is None
with patch.dict(environ, {TEST_SETTING: 'hello'}):
assert s.get() is None
s.clear()
assert s.get() == 'hello'
s = settings.Setting(TEST_SETTING, default='nope')
assert s.get() is 'nope'
with patch.dict(environ, {TEST_SETTING: 'hello'}):
assert s.get() == 'nope'
s.clear()
assert s.get() == 'hello'
def test_default_settings():
settings.clear_all()
assert settings.DEBUG.get() == False
assert settings.PROFILE.get() == False
assert settings.QUIET.get() == False
assert settings.LOGGING_LEVEL.get() == logging._checkLevel('INFO')
with patch.dict(environ, {'DEBUG': 't'}):
settings.clear_all()
assert settings.LOGGING_LEVEL.get() == logging._checkLevel('DEBUG')
settings.clear_all()
def test_check():
settings.check()
with patch.dict(environ, {'DEBUG': 't', 'PROFILE': 't', 'QUIET': 't'}):
settings.clear_all()
with pytest.raises(RuntimeError):
settings.check()
settings.clear_all()