Commit 7ed5e310 authored by Tim Schoof's avatar Tim Schoof
Browse files

Allow mixing Config and attr.ib parameters

This is useful to allow "hidden" parameters that can be used in the
constructor, but are not exposed as options on the command line
or in the configuration file.
parent 1aff432f
......@@ -20,7 +20,12 @@ def flattened_config_entries(type):
flattened_entries = []
for field in config_entries(type):
config_entry = field.metadata[CONFIGURABLE_CONFIG_ENTRY]
config_entry = field.metadata[CONFIGURABLE_CONFIG_ENTRY]
except KeyError:
# This is a normal attr.ib instead of a Config parameter, which
# means it should not be exposed as an option
if config_entry.proxy is not None:
sub_configurable = config_entry.proxy
......@@ -70,9 +75,15 @@ def Configurable(maybe_cls=None, *args, **kwargs):
typename = ""
typename = field.type.__name__
doc += "\n{name}: {type}\n {desc}".format(, type=typename,
doc += "\n{name}: {type}".format(, type=typename)
description = field.metadata[CONFIGURABLE_CONFIG_ENTRY].description
doc += "\n " + description
except KeyError:
if cls.__doc__:
if cls.__doc__.count("\n") > 3:
......@@ -6,7 +6,7 @@ import argparse
def create_config_from_configurable_class(configurable_class):
config = {}
for field in config_entries(configurable_class):
for field in flattened_config_entries(configurable_class):
config[] = create_config_for_attribute(field)
return config
......@@ -185,7 +185,18 @@ def create_instance_from_attribute(attribute, options, kwargs):
if in kwargs:
config_entry = attribute.metadata[CONFIGURABLE_CONFIG_ENTRY]
config_entry = attribute.metadata[CONFIGURABLE_CONFIG_ENTRY]
except KeyError:
# This is a normal attr.ib, just unpack the option and do nothing else
if options is not attr.NOTHING:
# use given option for this attribute
options = options.get(, attr.NOTHING)
if options is not attr.NOTHING:
kwargs[] = options
# else default options for this attribute will be used
if not config_entry.flatten:
if options is not attr.NOTHING:
options = options.get(, attr.NOTHING)
from collections import OrderedDict
import logging
import attr
from AsapoWorker.configurable import Configurable, Config
from AsapoWorker.configuration import create_config, \
create_yaml_from_configurable_class, create_instance_from_configurable, \
......@@ -499,3 +500,34 @@ def test_create_instance_from_configurable_zero():
instance = create_instance_from_configurable(Foo, {"i": 0})
assert instance.i == 0
def test_create_cli_parser_with_mixed_attr(capsys):
class Foo:
a = Config("An exposed option")
b = attr.ib()
parser = create_cli_parser_from_configurable_class(Foo)
captured = capsys.readouterr()
assert captured.out == (
"usage: Application [-h] --Foo.a ARG\n"
"optional arguments:\n"
" -h, --help show this help message and exit\n"
" --Foo.a ARG An exposed option (default: None)\n"
def test_create_instance_with_mixed_attr():
class Foo:
a = Config("An exposed option")
b = attr.ib()
instance = create_instance_from_configurable(Foo, {"a": 1, "b": 2})
assert instance == Foo(1, 2)
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment