def test_missing_default_raises(self, ini): """Missing values without a default raise an MissingSecretError.""" @environ.config class Cfg(object): pw = ini.secret() with pytest.raises(MissingSecretError): environ.to_config(Cfg, {})
def test_missing(self): """If a var is missing, a human-readable MissingEnvValueError is raised.""" @environ.config class Mandatory(object): x = environ.var() with pytest.raises(environ.MissingEnvValueError) as e: environ.to_config(Mandatory, environ={"y": "boring"}) assert ("APP_X", ) == e.value.args
def test_default(): """ Class based `from_environ` without `environ` argument. """ cfg = AppConfig.from_environ() assert cfg.host == "127.0.0.1" assert cfg.port == 5000 assert environ.to_config(AppConfig) == AppConfig.from_environ() assert environ.to_config(ConfigRenamed) == ConfigRenamed.from_env()
def test_env(): """ Class based `from_environ` with explicit `environ` argument. """ env = {"APP_HOST": "0.0.0.0"} cfg = AppConfig.from_environ(environ=env) assert cfg.host == "0.0.0.0" assert cfg.port == 5000 assert environ.to_config( AppConfig, environ=env) == AppConfig.from_environ(environ=env) assert environ.to_config( ConfigRenamed, environ=env) == ConfigRenamed.from_env(environ=env)
def get_app(extra_argv=None) -> Application: config = environ.to_config(Config) app = Application() app['config'] = config aiojobs.aiohttp.setup(app, limit=1) app.cleanup_ctx.extend((s3_ctx, mongo_ctx, keycloak_ctx)) cors = aiohttp_cors.setup(app) resource = cors.add( app.router.add_resource("/graphql"), { "*": aiohttp_cors.ResourceOptions(expose_headers="*", allow_headers="*", allow_credentials=True, allow_methods=["POST", "PUT", "GET"]), }) GraphQLView(schema=schema, graphiql=True, executor=AsyncioExecutor()) resource.add_route( "*", GraphQLView(schema=schema, graphiql=True, executor=AsyncioExecutor())) app.router.add_get('/upload_callback/{id}', upload_callback, name='upload_callback') return app
def test_frozen_child(self): """ Frozen child groups are immutable. """ @environ.config class Cfg(object): @environ.config(frozen=True) class Sub(object): z = environ.var() x = environ.var() y = environ.var() sub = environ.group(Sub) cfg = environ.to_config(Cfg, { "APP_X": "foo", "APP_Y": "bar", "APP_SUB_Z": "baz" }) cfg.x = "next_foo" assert cfg.x == "next_foo" with pytest.raises(FrozenInstanceError): cfg.sub.z = "next_baz" assert cfg.sub.z == "baz"
def test_ODBC_CONNECT_URL(self): with mock.patch.object(BigRaysConfig, 'ODBC_UID', 'foo'), \ mock.patch.object(BigRaysConfig, 'ODBC_PWD', 'bar'), \ mock.patch.object(BigRaysConfig, 'ODBC_DSN', 'baz'), \ mock.patch.object(BigRaysConfig, 'ODBC_FLAVOR', 'itsmysql'), \ mock.patch.object(BigRaysConfig, 'ODBC_CONNECT_PARAMS', ['ODBC_DSN', 'ODBC_UID', 'ODBC_PWD']), \ mock.patch('bigrays.config.urllib.parse.quote_plus', lambda x: x): expected = 'itsmysql+pyodbc:///?odbc_connect=DSN=baz;UID=foo;PWD=bar' actual = BigRaysConfig.ODBC_CONNECT_URL self.assertEqual(actual, expected) with mock.patch.object(BigRaysConfig, 'ODBC_UID', 'foo'), \ mock.patch.object(BigRaysConfig, 'ODBC_PWD', 'bar'), \ mock.patch.object(BigRaysConfig, 'ODBC_SERVER', 'baz'), \ mock.patch.object(BigRaysConfig, 'ODBC_CONNECT_PARAMS', ['ODBC_UID', 'ODBC_PWD', 'ODBC_SERVER']), \ mock.patch.object(BigRaysConfig, 'ODBC_FLAVOR', 'itsmysql'), \ mock.patch('bigrays.config.urllib.parse.quote_plus', lambda x: x): expected = 'itsmysql+pyodbc:///?odbc_connect=UID=foo;PWD=bar;SERVER=baz' actual = BigRaysConfig.ODBC_CONNECT_URL self.assertEqual(actual, expected) with mock.patch('bigrays.config.urllib.parse.quote_plus', lambda x: x): config = environ.to_config( Config, { 'BIGRAYS_ODBC_SERVER': 'foo.bar.com', 'BIGRAYS_ODBC_UID': 'me', 'BIGRAYS_ODBC_CONNECT_PARAMS': 'SERVER,UID', 'BIGRAYS_ODBC_FLAVOR': 'oracle' }) actual = config.ODBC_CONNECT_URL expected = 'oracle+pyodbc:///?odbc_connect=SERVER=foo.bar.com;UID=me' self.assertEqual(actual, expected)
def test_nested(self): """ Nested config is extracted, prefix and vault_prefix are propagated. """ env = {"APP_X": "nope", "XYZ_X": "foo", "XYZ_SUB_Y": "bar"} cfg = environ.to_config(Nested, environ=env) assert Nested(x="foo", sub=Nested.Sub(y="bar")) == cfg
def test_optional_group_mixed_children_optional_present(self): """ Optional groups are required if any optional child is present. """ @environ.config(prefix="PARENT") class WithOptionalChild(object): @environ.config(prefix="CHILD") class Child(object): grandchild_a = environ.var() grandchild_b = environ.var("FOO") child = environ.group(Child, optional=True) with pytest.raises(environ.MissingEnvValueError) as e: environ.to_config(WithOptionalChild, {"PARENT_CHILD_GRANDCHILD_B": "BAR"}) assert ("PARENT_CHILD_GRANDCHILD_A", ) == e.value.args
def test_name_overwrite(self, ini): """Passsing a specific key name is respected.""" @environ.config class Cfg(object): pw = ini.secret(name="password") cfg = environ.to_config(Cfg, {}) assert _SecretStr("foobar") == cfg.pw
def test_required_group_required_child_missing(self): """ Groups are required if any of their child elements are. """ @environ.config(prefix="PARENT") class WithRequiredChild(object): @environ.config(prefix="CHILD") class Child(object): grandchild = environ.var() child = environ.group(Child) cfg = environ.to_config(WithRequiredChild, {"PARENT_CHILD_GRANDCHILD": "FOO"}) assert cfg.child.grandchild == "FOO" with pytest.raises(environ.MissingEnvValueError) as e: environ.to_config(WithRequiredChild, dict()) assert ("PARENT_CHILD_GRANDCHILD", ) == e.value.args
def test_empty(self): """Empty config is accepted.""" @environ.config class Empty(object): pass cfg = environ.to_config(Empty) assert "Empty()" == repr(cfg)
def get_env() -> AppEnv: """Get the current environment instance. Returns: AppConfig: The current environment as read in by the :class:`~AppConfig`. """ return environ.to_config(AppEnv, environ=os.environ)
def test_default(self, ini): """Defaults are used iff the key is missing.""" @environ.config class Cfg(object): password = ini.secret(default="not used") secret = ini.secret(default="used!") cfg = environ.to_config(Cfg, {}) assert Cfg("foobar", "used!") == cfg
def test_returns_secret_str(self, vault): """The returned strings are `_SecretStr`.""" @environ.config class Cfg(object): x = vault.secret() cfg = environ.to_config(Cfg, {"SECRET_X": "foo"}) assert isinstance(cfg.x, _SecretStr) assert "foo" == cfg.x
async def amain() -> int: config: Config = environ.to_config(Config) logging.basicConfig(level={ 0: logging.ERROR, 1: logging.WARNING, 2: logging.INFO, }.get(config.lib_log_level, logging.DEBUG), ) logging.getLogger("authbot").setLevel({ 0: logging.ERROR, 1: logging.WARNING, 2: logging.INFO, }.get(config.log_level, logging.DEBUG)) client = aioxmpp.PresenceManagedClient( config.address, aioxmpp.make_security_layer(config.password, ), ) client.summon(aioxmpp.MUCClient) client.summon(aioxmpp.DiscoClient) stop_event = asyncio.Event() loop = asyncio.get_event_loop() loop.add_signal_handler(signal.SIGINT, stop_event.set) loop.add_signal_handler(signal.SIGTERM, stop_event.set) stop_future = asyncio.create_task(stop_event.wait()) async with client.connected() as stream: logger.info("connected as %s", stream.local_jid) bot_task = asyncio.create_task( bot.run_in_room( client, config.room_address, config.room_nickname, )) done, pending = await asyncio.wait( [bot_task, stop_future], return_when=asyncio.FIRST_COMPLETED, ) for fut in pending: fut.cancel() for fut in done: fut.result() for fut in pending: try: await fut except asyncio.CancelledError: pass
def test_no_prefix(self, prefix): """ If prefix is None or "", don't add a leading _ when adding namespaces. """ @environ.config(prefix=prefix) class Cfg(object): x = environ.var() cfg = environ.to_config(Cfg, environ={"X": "foo"}) assert Cfg("foo") == cfg
def test_from_path_in_env_delayed(self, ini_file): """`from_path_in_env` prepares for loading but doesn't load until `to_config` runs.""" secret = INISecrets.from_path_in_env("APP_SECRETS_INI").secret @environ.config class Cfg(object): password = secret() cfg = environ.to_config(Cfg, {"APP_SECRETS_INI": str(ini_file)}) assert "foobar" == cfg.password
def test_flat(self): """ Flat config is extracted. """ @environ.config(prefix="APP") class Flat(object): x = environ.var() y = environ.var() cfg = environ.to_config(Flat, environ={"APP_X": "foo", "APP_Y": "bar"}) assert Flat(x="foo", y="bar") == cfg
def test_default(self): """ Default values are used iff the vars are missing. """ @environ.config class Defaults(object): x = environ.var("foo") y = environ.var("qux") cfg = environ.to_config(Defaults, environ={"APP_Y": "bar"}) assert Defaults(x="foo", y="bar") == cfg
def test_overwrite_sections(self, ini): """The source section can be overwritten.""" ini.section = "yet_another_section" @environ.config class Cfg(object): password = ini.secret(section="other_secrets") secret = ini.secret() cfg = environ.to_config(Cfg, {}) assert _SecretStr("bar%foo") == cfg.password
def test_tolerates_attribs(self): """ Classes are allowed to have plain attr.ibs for e.g. __attrs_post_init__. """ @environ.config class Cfg(object): e = environ.var() x = attr.ib(default=42) cfg = environ.to_config(Cfg, environ={"APP_E": "e"}) assert Cfg("e", 42) == cfg
def test_factory_default(self): """ If the default value is an ``attr.Factory``, it used to generate the default value. """ @environ.config class Defaults(object): x = environ.var(attr.Factory(list)) y = environ.var(attr.Factory(list)) cfg = environ.to_config(Defaults, environ={"APP_Y": "bar"}) assert Defaults(x=[], y="bar") == cfg
def test_nested(self, ini): """Prefix building works.""" @environ.config class Cfg(object): @environ.config class DB(object): password = ini.secret() db = environ.group(DB) cfg = environ.to_config(Cfg, {}) assert _SecretStr("nested!") == cfg.db.password
def test_optional_group_required_child_missing(self): """ Optional groups are set to `None` if a required child is missing. """ @environ.config(prefix="PARENT") class WithOptionalChild(object): @environ.config(prefix="CHILD") class Child(object): grandchild = environ.var() child = environ.group(Child, optional=True) cfg = environ.to_config(WithOptionalChild, dict()) assert cfg.child is None
def test_optional_group_optional_child_missing(self): """ Optional groups with an optional child is fully structured. """ @environ.config(prefix="PARENT") class WithOptionalChild(object): @environ.config(prefix="CHILD") class Child(object): grandchild = environ.var("FOO") child = environ.group(Child, optional=True) cfg = environ.to_config(WithOptionalChild, dict()) assert cfg.child.grandchild == "FOO"
def test_overwrite_name(self, vault): """ The variable name can be overwritten. """ @environ.config class Cfg(object): password = vault.secret(name="not_password") cfg = environ.to_config(Cfg, { "SECRET_PASSWORD": "******", "not_password": "******" }) assert "correct" == cfg.password
def test_required_group_optional_child_missing(self): """ Required groups are fully structured if the only child is optional. """ @environ.config(prefix="PARENT") class WithRequiredChild(object): @environ.config(prefix="CHILD") class Child(object): grandchild = environ.var("FOO") child = environ.group(Child) cfg = environ.to_config(WithRequiredChild, dict()) assert cfg.child.grandchild == "FOO"
def test_frozen(self): """ Frozen configurations are immutable. """ @environ.config(frozen=True) class Cfg(object): x = environ.var() y = environ.var() cfg = environ.to_config(Cfg, {"APP_X": "foo", "APP_Y": "bar"}) with pytest.raises(FrozenInstanceError): cfg.x = "next_foo" assert cfg.x == "foo"
def test_default_factory(self, ini): """ Defaults are used iff the key is missing. """ def getpass(): return "thesecret" @environ.config class Cfg(object): password = ini.secret(default=attr.Factory(getpass)) secret = ini.secret(default=attr.Factory(getpass)) cfg = environ.to_config(Cfg, {}) assert Cfg("foobar", "thesecret") == cfg