[settings] Better impl. of Setting class, tests for it and refactor hardcoded settings to use it.
This commit is contained in:
@ -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:
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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):
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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())
|
||||||
|
|||||||
@ -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))
|
||||||
|
|||||||
@ -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
63
tests/test_settings.py
Normal 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()
|
||||||
Reference in New Issue
Block a user