From f6380c66c8d85a15ce3b7f2cea102b8d804c98a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Ma=C5=9Blanka?= <piotr.maslanka@henrietta.com.pl> Date: Tue, 11 May 2021 13:27:47 +0200 Subject: [PATCH] * added some syntactic sugar for the source dict * now unparseable "type" entries will be returned as-is * added `ConfigurationMisconfiguredError` , v2.16.2 --- CHANGELOG.md | 3 ++ docs/configuration/sources.rst | 15 ++++++++ docs/exceptions.rst | 36 +++++++++++-------- satella/__init__.py | 2 +- satella/configuration/sources/from_dict.py | 21 +++++++---- satella/exceptions.py | 7 +++- .../test_load_source_from_dict.py | 6 +++- 7 files changed, 66 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a81c1c0..a9f8f92c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,3 +4,6 @@ * added `file_contents` schema * added nested configuration sources * added `extract_optional` +* added some syntactic sugar for the source dict +* now unparseable "type" entries will be returned as-is +* added `ConfigurationMisconfiguredError` diff --git a/docs/configuration/sources.rst b/docs/configuration/sources.rst index 96a0f646..5a310bdd 100644 --- a/docs/configuration/sources.rst +++ b/docs/configuration/sources.rst @@ -137,3 +137,18 @@ The result of this execution will be a dictionary: "a": 5 } } + +If you have only a single argument, you can also do: + +.. code-block:: json + + { + "type": "DirectorySource", + "arg": "/app/config" + } + +You can put any objects you like as the arguments, note however that if you pass a dictionary, that +has a key of "type" and it's value is one of recognized sources, an attempt will be made to parse +it as a child. + +Note that in case you pass a dict with a type that is not recognized, a warning will be emitted. diff --git a/docs/exceptions.rst b/docs/exceptions.rst index 1710ec68..4a41cd15 100644 --- a/docs/exceptions.rst +++ b/docs/exceptions.rst @@ -92,7 +92,7 @@ it's magic :members: ImpossibleError ---------------- +~~~~~~~~~~~~~~~ .. autoclass:: satella.exceptions.ImpossibleError :members: @@ -101,92 +101,98 @@ Note that `ImpossibleError` inherits from `BaseException` instead of the standar The thought is, since this is an anomalous exception, it should get to the top of the stack ASAP. Satella-specific exceptions -=========================== +--------------------------- BaseSatellaError ----------------- +~~~~~~~~~~~~~~~~ .. autoclass:: satella.exceptions.BaseSatellaError :members: ResourceLockingError --------------------- +~~~~~~~~~~~~~~~~~~~~ .. autoclass:: satella.exceptions.ResourceLockingError :members: ResourceLocked --------------- +~~~~~~~~~~~~~~ .. autoclass:: satella.exceptions.ResourceLocked :members: ResourceNotLocked ------------------ +~~~~~~~~~~~~~~~~~ .. autoclass:: satella.exceptions.ResourceNotLocked :members: WouldWaitMore -------------- +~~~~~~~~~~~~~ .. autoclass:: satella.exceptions.WouldWaitMore :members: PreconditionError ------------------ +~~~~~~~~~~~~~~~~~ .. autoclass:: satella.exceptions.PreconditionError :members: ConfigurationError ------------------- +~~~~~~~~~~~~~~~~~~ .. autoclass:: satella.exceptions.ConfigurationError :members: ConfigurationSchemaError ------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: satella.exceptions.ConfigurationSchemaError :members: +ConfigurationMisconfiguredError +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: satella.exceptions.ConfigurationMisconfiguredError + :members: + ConfigurationValidationError ----------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: satella.exceptions.ConfigurationValidationError :members: Empty ------ +~~~~~ .. autoclass:: satella.exceptions.empty :members: MetricAlreadyExists -------------------- +~~~~~~~~~~~~~~~~~~~ .. autoclass:: satella.exceptions.MetricAlreadyExists :members: AlreadyAllocated ----------------- +~~~~~~~~~~~~~~~~ .. autoclass:: satella.exceptions.AlreadyAllocated :members: ProcessFailed -------------- +~~~~~~~~~~~~~ .. autoclass:: satella.exceptions.ProcessFailed :members: diff --git a/satella/__init__.py b/satella/__init__.py index 413aa79f..b34264c0 100644 --- a/satella/__init__.py +++ b/satella/__init__.py @@ -1 +1 @@ -__version__ = '2.16.2a6' +__version__ = '2.16.2' diff --git a/satella/configuration/sources/from_dict.py b/satella/configuration/sources/from_dict.py index ee563941..ecfe8cf7 100644 --- a/satella/configuration/sources/from_dict.py +++ b/satella/configuration/sources/from_dict.py @@ -1,11 +1,11 @@ import copy import importlib +import warnings from satella.coding.recast_exceptions import rethrow_as from satella.configuration import sources from satella.configuration.sources.base import BaseSource -from satella.configuration.sources.object_from import BuildObjectFrom -from satella.exceptions import ConfigurationError +from satella.exceptions import ConfigurationError, ConfigurationMisconfiguredError __all__ = [ 'load_source_from_dict', @@ -46,7 +46,11 @@ def load_source_from_dict(dct: dict) -> BaseSource: """ dct = copy.copy(dct) type_ = dct.pop('type') # type: str - args = dct.pop('args', []) # type: tp.List + if 'arg' in dct: + args = dct.pop('arg'), + else: + args = dct.pop('args', []) # type: tp.List + optional = dct.pop('optional', False) # type: bool def to_arg(arg): @@ -57,15 +61,20 @@ def load_source_from_dict(dct: dict) -> BaseSource: elif a_type in sources.__dict__: return load_source_from_dict(arg) else: - raise ValueError( - 'unrecognized argument type %s' % (arg['type'],)) + warnings.warn( + 'Caught %s attempting to parse a dict with type, returning original value' % ( + e,), UserWarning) + return arg else: return arg args = map(to_arg, args) kwargs = {k: to_arg(v) for k, v in dct.items()} - s = sources.__dict__[type_](*args, **kwargs) + try: + s = sources.__dict__[type_](*args, **kwargs) + except KeyError as e: + raise ConfigurationMisconfiguredError('unknown type %s' % (type_, )) if optional: s = sources.OptionalSource(s) diff --git a/satella/exceptions.py b/satella/exceptions.py index 0bea7338..49f50e6b 100644 --- a/satella/exceptions.py +++ b/satella/exceptions.py @@ -6,7 +6,8 @@ __all__ = ['BaseSatellaError', 'ResourceLockingError', 'ResourceNotLocked', 'Res 'ConfigurationValidationError', 'ConfigurationError', 'ConfigurationSchemaError', 'PreconditionError', 'MetricAlreadyExists', 'BaseSatellaException', 'CustomException', 'CodedCustomException', 'CodedCustomExceptionMetaclass', 'WouldWaitMore', - 'ProcessFailed', 'AlreadyAllocated', 'Empty', 'ImpossibleError'] + 'ProcessFailed', 'AlreadyAllocated', 'Empty', 'ImpossibleError', + 'ConfigurationMisconfiguredError'] class CustomException(Exception): @@ -181,6 +182,10 @@ class ConfigurationSchemaError(ConfigurationError): """Schema mismatch to what was seen""" +class ConfigurationMisconfiguredError(ConfigurationError): + """Configuration was improperly passed to Satella""" + + class ConfigurationValidationError(ConfigurationSchemaError): """A validator failed""" diff --git a/tests/test_configuration/test_load_source_from_dict.py b/tests/test_configuration/test_load_source_from_dict.py index 35a371ad..d5083509 100644 --- a/tests/test_configuration/test_load_source_from_dict.py +++ b/tests/test_configuration/test_load_source_from_dict.py @@ -17,6 +17,10 @@ INNER_DATA = [ ] } }, + { + 'type': 'JSONSource', + 'arg': '{"d": 10}' + }, { 'type': 'JSONSource', 'args': ['{"a": 5}'] @@ -70,7 +74,7 @@ DICT_DATA = { class TestLoadSourceFromDict(SourceTestCase): def test_lsf(self): - output = {'a': 5, 'b': 5, 'c': 21, 'test': {'a': 5}} + output = {'a': 5, 'b': 5, 'c': 21, 'test': {'a': 5}, 'd': 10} self.assertSourceHas(load_source_from_dict(DICT_DATA), output) self.assertSourceHas(load_source_from_list(INNER_DATA), output) -- GitLab