diff --git a/tests/unit/providers/configuration/__init__.py b/tests/unit/providers/configuration/__init__.py new file mode 100644 index 00000000..fda0d161 --- /dev/null +++ b/tests/unit/providers/configuration/__init__.py @@ -0,0 +1 @@ +"""Configuration provider tests.""" diff --git a/tests/unit/providers/configuration/conftest.py b/tests/unit/providers/configuration/conftest.py new file mode 100644 index 00000000..027630ce --- /dev/null +++ b/tests/unit/providers/configuration/conftest.py @@ -0,0 +1,19 @@ +"""Fixtures module.""" + +from dependency_injector import providers +from pytest import fixture + + +@fixture +def config_type(): + return "default" + + +@fixture +def config(config_type): + if config_type == "strict": + return providers.Configuration(strict=True) + elif config_type == "default": + return providers.Configuration() + else: + raise ValueError("Undefined config type \"{0}\"".format(config_type)) diff --git a/tests/unit/providers/configuration/test_config_linking_py2_py3.py b/tests/unit/providers/configuration/test_config_linking_py2_py3.py new file mode 100644 index 00000000..1a1720a5 --- /dev/null +++ b/tests/unit/providers/configuration/test_config_linking_py2_py3.py @@ -0,0 +1,130 @@ +"""Tests for configuration provider linking.""" + + +from dependency_injector import containers, providers + + +class Core(containers.DeclarativeContainer): + config = providers.Configuration("core") + value_getter = providers.Callable(lambda _: _, config.value) + + +class Services(containers.DeclarativeContainer): + config = providers.Configuration("services") + value_getter = providers.Callable(lambda _: _, config.value) + + +def test(): + root_config = providers.Configuration("main") + core = Core(config=root_config.core) + services = Services(config=root_config.services) + + root_config.override( + { + "core": { + "value": "core", + }, + "services": { + "value": "services", + }, + }, + ) + + assert core.config() == {"value": "core"} + assert core.config.value() == "core" + assert core.value_getter() == "core" + + assert services.config() == {"value": "services"} + assert services.config.value() == "services" + assert services.value_getter() == "services" + + +def test_double_override(): + root_config = providers.Configuration("main") + core = Core(config=root_config.core) + services = Services(config=root_config.services) + + root_config.override( + { + "core": { + "value": "core1", + }, + "services": { + "value": "services1", + }, + }, + ) + root_config.override( + { + "core": { + "value": "core2", + }, + "services": { + "value": "services2", + }, + }, + ) + + assert core.config() == {"value": "core2"} + assert core.config.value() == "core2" + assert core.value_getter() == "core2" + + assert services.config() == {"value": "services2"} + assert services.config.value() == "services2" + assert services.value_getter() == "services2" + + +def test_reset_overriding_cache(): + # See: https://github.com/ets-labs/python-dependency-injector/issues/428 + class Core(containers.DeclarativeContainer): + config = providers.Configuration() + + greetings = providers.Factory(str, config.greeting) + + class Application(containers.DeclarativeContainer): + config = providers.Configuration() + + core = providers.Container( + Core, + config=config, + ) + + greetings = providers.Factory(str, config.greeting) + + container = Application() + + container.config.set("greeting", "Hello World") + assert container.greetings() == "Hello World" + assert container.core.greetings() == "Hello World" + + container.config.set("greeting", "Hello Bob") + assert container.greetings() == "Hello Bob" + assert container.core.greetings() == "Hello Bob" + + +def test_reset_overriding_cache_for_option(): + # See: https://github.com/ets-labs/python-dependency-injector/issues/428 + class Core(containers.DeclarativeContainer): + config = providers.Configuration() + + greetings = providers.Factory(str, config.greeting) + + class Application(containers.DeclarativeContainer): + config = providers.Configuration() + + core = providers.Container( + Core, + config=config.option, + ) + + greetings = providers.Factory(str, config.option.greeting) + + container = Application() + + container.config.set("option.greeting", "Hello World") + assert container.greetings() == "Hello World" + assert container.core.greetings() == "Hello World" + + container.config.set("option.greeting", "Hello Bob") + assert container.greetings() == "Hello Bob" + assert container.core.greetings() == "Hello Bob" diff --git a/tests/unit/providers/configuration/test_config_py2_py3.py b/tests/unit/providers/configuration/test_config_py2_py3.py new file mode 100644 index 00000000..a8c0acd2 --- /dev/null +++ b/tests/unit/providers/configuration/test_config_py2_py3.py @@ -0,0 +1,344 @@ +"""Configuration provider tests.""" + +import decimal + +from dependency_injector import containers, providers, errors +from pytest import mark, raises + + +def test_init_optional(config): + config.set_name("myconfig") + config.set_default({"foo": "bar"}) + config.set_strict(True) + + assert config.get_name() == "myconfig" + assert config.get_default() == {"foo": "bar"} + assert config.get_strict() is True + + +def test_set_name_returns_self(config): + assert config.set_name("myconfig") is config + + +def test_set_default_returns_self(config): + assert config.set_default({}) is config + + +def test_set_strict_returns_self(config): + assert config.set_strict(True) is config + + +def test_default_name(config): + assert config.get_name() == "config" + + +def test_providers_are_providers(config): + assert providers.is_provider(config.a) is True + assert providers.is_provider(config.a.b) is True + assert providers.is_provider(config.a.b.c) is True + assert providers.is_provider(config.a.b.d) is True + + +def test_providers_are_not_delegates(config): + assert providers.is_delegated(config.a) is False + assert providers.is_delegated(config.a.b) is False + assert providers.is_delegated(config.a.b.c) is False + assert providers.is_delegated(config.a.b.d) is False + + +def test_providers_identity(config): + assert config.a is config.a + assert config.a.b is config.a.b + assert config.a.b.c is config.a.b.c + assert config.a.b.d is config.a.b.d + + +def test_get_name(config): + assert config.a.b.c.get_name() == "config.a.b.c" + + +def test_providers_value_setting(config): + a = config.a + ab = config.a.b + abc = config.a.b.c + abd = config.a.b.d + + config.update({"a": {"b": {"c": 1, "d": 2}}}) + + assert a() == {"b": {"c": 1, "d": 2}} + assert ab() == {"c": 1, "d": 2} + assert abc() == 1 + assert abd() == 2 + + +def test_providers_with_already_set_value(config): + config.update({"a": {"b": {"c": 1, "d": 2}}}) + + a = config.a + ab = config.a.b + abc = config.a.b.c + abd = config.a.b.d + + assert a() == {"b": {"c": 1, "d": 2}} + assert ab() == {"c": 1, "d": 2} + assert abc() == 1 + assert abd() == 2 + + +def test_as_int(config): + value_provider = providers.Callable(lambda value: value, config.test.as_int()) + config.from_dict({"test": "123"}) + + value = value_provider() + assert value == 123 + + +def test_as_float(config): + value_provider = providers.Callable(lambda value: value, config.test.as_float()) + config.from_dict({"test": "123.123"}) + + value = value_provider() + assert value == 123.123 + + +def test_as_(config): + value_provider = providers.Callable( + lambda value: value, + config.test.as_(decimal.Decimal), + ) + config.from_dict({"test": "123.123"}) + + value = value_provider() + assert value == decimal.Decimal("123.123") + + +# TODO: do we really need to skip it? +# @unittest.skipIf(sys.version_info[:2] == (2, 7), "Python 2.7 does not support this assert") +def test_required(config): + provider = providers.Callable( + lambda value: value, + config.a.required(), + ) + with raises(errors.Error, match="Undefined configuration option \"config.a\""): + provider() + + +def test_required_defined_none(config): + provider = providers.Callable( + lambda value: value, + config.a.required(), + ) + config.from_dict({"a": None}) + assert provider() is None + + +def test_required_no_side_effect(config): + _ = providers.Callable( + lambda value: value, + config.a.required(), + ) + assert config.a() is None + + +def test_required_as_(config): + provider = providers.List( + config.int_test.required().as_int(), + config.float_test.required().as_float(), + config._as_test.required().as_(decimal.Decimal), + ) + config.from_dict({"int_test": "1", "float_test": "2.0", "_as_test": "3.0"}) + assert provider() == [1, 2.0, decimal.Decimal("3.0")] + + +def test_providers_value_override(config): + a = config.a + ab = config.a.b + abc = config.a.b.c + abd = config.a.b.d + + config.override({"a": {"b": {"c": 1, "d": 2}}}) + + assert a() == {"b": {"c": 1, "d": 2}} + assert ab() == {"c": 1, "d": 2} + assert abc() == 1 + assert abd() == 2 + + +def test_configuration_option_override_and_reset_override(config): + # Bug: https://github.com/ets-labs/python-dependency-injector/issues/319 + config.from_dict({"a": {"b": {"c": 1}}}) + + assert config.a.b.c() == 1 + + with config.set("a.b.c", "xxx"): + assert config.a.b.c() == "xxx" + assert config.a.b.c() == 1 + + with config.a.b.c.override("yyy"): + assert config.a.b.c() == "yyy" + + assert config.a.b.c() == 1 + + +def test_providers_with_already_overridden_value(config): + config.override({"a": {"b": {"c": 1, "d": 2}}}) + + a = config.a + ab = config.a.b + abc = config.a.b.c + abd = config.a.b.d + + assert a() == {"b": {"c": 1, "d": 2}} + assert ab() == {"c": 1, "d": 2} + assert abc() == 1 + assert abd() == 2 + + +def test_providers_with_default_value(config): + config.set_default({"a": {"b": {"c": 1, "d": 2}}}) + + a = config.a + ab = config.a.b + abc = config.a.b.c + abd = config.a.b.d + + assert a() == {"b": {"c": 1, "d": 2}} + assert ab() == {"c": 1, "d": 2} + assert abc() == 1 + assert abd() == 2 + + +def test_providers_with_default_value_overriding(config): + config.set_default({"a": {"b": {"c": 1, "d": 2}}}) + + assert config.a() == {"b": {"c": 1, "d": 2}} + assert config.a.b() == {"c": 1, "d": 2} + assert config.a.b.c() == 1 + assert config.a.b.d() == 2 + + config.override({"a": {"b": {"c": 3, "d": 4}}}) + assert config.a() == {"b": {"c": 3, "d": 4}} + assert config.a.b() == {"c": 3, "d": 4} + assert config.a.b.c() == 3 + assert config.a.b.d() == 4 + + config.reset_override() + assert config.a() == {"b": {"c": 1, "d": 2}} + assert config.a.b() == {"c": 1, "d": 2} + assert config.a.b.c() == 1 + assert config.a.b.d() == 2 + + +def test_value_of_undefined_option(config): + assert config.option() is None + + +# TODO: do we really need to skip it? +# @unittest.skipIf(sys.version_info[:2] == (2, 7), "Python 2.7 does not support this assert") +@mark.parametrize("config_type", ["strict"]) +def test_value_of_undefined_option_in_strict_mode(config): + with raises(errors.Error, match="Undefined configuration option \"config.option\""): + config.option() + + +# TODO: do we really need to skip it? +# @unittest.skipIf(sys.version_info[:2] == (2, 7), "Python 2.7 does not support this assert") +@mark.parametrize("config_type", ["strict"]) +def test_value_of_undefined_option_with_root_none_in_strict_mode(config): + config.override(None) + with raises(errors.Error, match="Undefined configuration option \"config.option\""): + config.option() + + +@mark.parametrize("config_type", ["strict"]) +def test_value_of_defined_none_option_in_strict_mode(config): + config.from_dict({"a": None}) + assert config.a() is None + + +def test_getting_of_special_attributes(config): + with raises(AttributeError): + config.__name__ + + +def test_getting_of_special_attributes_from_child(config): + with raises(AttributeError): + config.child.__name__ + + +def test_context_manager_alias(): + class Container(containers.DeclarativeContainer): + config = providers.Configuration() + + container = Container() + + with container.config as config: + config.override({"foo": "foo", "bar": "bar"}) + + assert container.config() == {"foo": "foo", "bar": "bar"} + assert config() == {"foo": "foo", "bar": "bar"} + assert container.config is config + + +def test_option_context_manager_alias(): + class Container(containers.DeclarativeContainer): + config = providers.Configuration() + + container = Container() + + with container.config.option as option: + option.override({"foo": "foo", "bar": "bar"}) + + assert container.config() == {"option": {"foo": "foo", "bar": "bar"}} + assert container.config.option() == {"foo": "foo", "bar": "bar"} + assert option() == {"foo": "foo", "bar": "bar"} + assert container.config.option is option + + +def test_missing_key(config): + # See: https://github.com/ets-labs/python-dependency-injector/issues/358 + config.override(None) + value = config.key() + assert value is None + + +def test_deepcopy(config): + config_copy = providers.deepcopy(config) + assert isinstance(config_copy, providers.Configuration) + assert config is not config_copy + + +def test_deepcopy_from_memo(config): + config_copy_memo = providers.Configuration() + + provider_copy = providers.deepcopy(config, memo={id(config): config_copy_memo}) + assert provider_copy is config_copy_memo + + +def test_deepcopy_overridden(config): + object_provider = providers.Object(object()) + + config.override(object_provider) + + provider_copy = providers.deepcopy(config) + object_provider_copy = provider_copy.overridden[0] + + assert config is not provider_copy + assert isinstance(config, providers.Configuration) + + assert object_provider is not object_provider_copy + assert isinstance(object_provider_copy, providers.Object) + + +def test_repr(config): + assert repr(config) == ( + "".format(repr("config"), hex(id(config))) + ) + + +def test_repr_child(config): + assert repr(config.a.b.c) == ( + "".format(repr("config.a.b.c"), hex(id(config.a.b.c))) + ) diff --git a/tests/unit/providers/configuration/test_from_dict_py2_py3.py b/tests/unit/providers/configuration/test_from_dict_py2_py3.py new file mode 100644 index 00000000..ebc833e7 --- /dev/null +++ b/tests/unit/providers/configuration/test_from_dict_py2_py3.py @@ -0,0 +1,103 @@ +"""Configuration.from_dict() tests.""" + +from pytest import mark, raises + + +CONFIG_OPTIONS_1 = { + "section1": { + "value1": "1", + }, + "section2": { + "value2": "2", + }, +} +CONFIG_OPTIONS_2 = { + "section1": { + "value1": "11", + "value11": "11", + }, + "section3": { + "value3": "3", + }, +} + + +def test(config): + config.from_dict(CONFIG_OPTIONS_1) + + assert config() == {"section1": {"value1": "1"}, "section2": {"value2": "2"}} + assert config.section1() == {"value1": "1"} + assert config.section1.value1() == "1" + assert config.section2() == {"value2": "2"} + assert config.section2.value2() == "2" + + +def test_merge(config): + config.from_dict(CONFIG_OPTIONS_1) + config.from_dict(CONFIG_OPTIONS_2) + + assert config() == { + "section1": { + "value1": "11", + "value11": "11", + }, + "section2": { + "value2": "2", + }, + "section3": { + "value3": "3", + }, + } + assert config.section1() == {"value1": "11", "value11": "11"} + assert config.section1.value1() == "11" + assert config.section1.value11() == "11" + assert config.section2() == {"value2": "2"} + assert config.section2.value2() == "2" + assert config.section3() == {"value3": "3"} + assert config.section3.value3() == "3" + + +def test_empty_dict(config): + config.from_dict({}) + assert config() == {} + + +def test_option_empty_dict(config): + config.option.from_dict({}) + assert config.option() == {} + + +@mark.parametrize("config_type", ["strict"]) +def test_empty_dict_in_strict_mode(config): + with raises(ValueError): + config.from_dict({}) + + +@mark.parametrize("config_type", ["strict"]) +def test_option_empty_dict_in_strict_mode(config): + with raises(ValueError): + config.option.from_dict({}) + + +def test_required_empty_dict(config): + with raises(ValueError): + config.from_dict({}, required=True) + + +def test_required_option_empty_dict(config): + with raises(ValueError): + config.option.from_dict({}, required=True) + + +@mark.parametrize("config_type", ["strict"]) +def test_not_required_empty_dict_strict_mode(config): + config.from_dict({}, required=False) + assert config() == {} + + +@mark.parametrize("config_type", ["strict"]) +def test_not_required_option_empty_dict_strict_mode(config): + config.option.from_dict({}, required=False) + assert config.option() == {} + assert config() == {"option": {}} + diff --git a/tests/unit/providers/configuration/test_from_env_py2_py3.py b/tests/unit/providers/configuration/test_from_env_py2_py3.py new file mode 100644 index 00000000..5c6b6395 --- /dev/null +++ b/tests/unit/providers/configuration/test_from_env_py2_py3.py @@ -0,0 +1,107 @@ +"""Configuration.from_env() tests.""" + +import os + +from pytest import fixture, mark, raises + + +@fixture(autouse=True) +def environment_variables(): + os.environ["CONFIG_TEST_ENV"] = "test-value" + yield + os.environ.pop("CONFIG_TEST_ENV", None) + + +def test(config): + config.from_env("CONFIG_TEST_ENV") + assert config() == "test-value" + + +def test_with_children(config): + config.section1.value1.from_env("CONFIG_TEST_ENV") + + assert config() == {"section1": {"value1": "test-value"}} + assert config.section1() == {"value1": "test-value"} + assert config.section1.value1() == "test-value" + + +def test_default(config): + config.from_env("UNDEFINED_ENV", "default-value") + assert config() == "default-value" + + +def test_default_none(config): + config.from_env("UNDEFINED_ENV") + assert config() is None + + +def test_option_default_none(config): + config.option.from_env("UNDEFINED_ENV") + assert config.option() is None + + +@mark.parametrize("config_type", ["strict"]) +def test_undefined_in_strict_mode(config): + with raises(ValueError): + config.from_env("UNDEFINED_ENV") + + +@mark.parametrize("config_type", ["strict"]) +def test_option_undefined_in_strict_mode(config): + with raises(ValueError): + config.option.from_env("UNDEFINED_ENV") + + +def test_undefined_in_strict_mode_with_default(config): + config.from_env("UNDEFINED_ENV", "default-value") + assert config() == "default-value" + + +@mark.parametrize("config_type", ["strict"]) +def test_option_undefined_in_strict_mode_with_default(config): + config.option.from_env("UNDEFINED_ENV", "default-value") + assert config.option() == "default-value" + + +def test_required_undefined(config): + with raises(ValueError): + config.from_env("UNDEFINED_ENV", required=True) + + +def test_required_undefined_with_default(config): + config.from_env("UNDEFINED_ENV", default="default-value", required=True) + assert config() == "default-value" + + +def test_option_required_undefined(config): + with raises(ValueError): + config.option.from_env("UNDEFINED_ENV", required=True) + + +def test_option_required_undefined_with_default(config): + config.option.from_env("UNDEFINED_ENV", default="default-value", required=True) + assert config.option() == "default-value" + + +@mark.parametrize("config_type", ["strict"]) +def test_not_required_undefined_in_strict_mode(config): + config.from_env("UNDEFINED_ENV", required=False) + assert config() is None + + +@mark.parametrize("config_type", ["strict"]) +def test_option_not_required_undefined_in_strict_mode(config): + config.option.from_env("UNDEFINED_ENV", required=False) + assert config.option() is None + + +@mark.parametrize("config_type", ["strict"]) +def test_not_required_undefined_with_default_in_strict_mode(config): + config.from_env("UNDEFINED_ENV", default="default-value", required=False) + assert config() == "default-value" + + +@mark.parametrize("config_type", ["strict"]) +def test_option_not_required_undefined_with_default_in_strict_mode(config): + config.option.from_env("UNDEFINED_ENV", default="default-value", required=False) + assert config.option() == "default-value" diff --git a/tests/unit/providers/configuration/test_from_ini_py2_py3.py b/tests/unit/providers/configuration/test_from_ini_py2_py3.py new file mode 100644 index 00000000..0f81c879 --- /dev/null +++ b/tests/unit/providers/configuration/test_from_ini_py2_py3.py @@ -0,0 +1,123 @@ +"""Configuration.from_ini() tests.""" + +from dependency_injector import errors +from pytest import fixture, mark, raises + + +@fixture +def config_file_1(tmp_path): + config_file = tmp_path / "config_1.ini" + with open(config_file, "w") as file: + file.write( + "[section1]\n" + "value1=1\n" + "\n" + "[section2]\n" + "value2=2\n" + ) + return config_file + + +@fixture +def config_file_2(tmp_path): + config_file = tmp_path / "config_2.ini" + with open(config_file, "w") as file: + file.write( + "[section1]\n" + "value1=11\n" + "value11=11\n" + "[section3]\n" + "value3=3\n" + ) + return config_file + + +def test(config, config_file_1): + config.from_ini(config_file_1) + + assert config() == {"section1": {"value1": "1"}, "section2": {"value2": "2"}} + assert config.section1() == {"value1": "1"} + assert config.section1.value1() == "1" + assert config.section2() == {"value2": "2"} + assert config.section2.value2() == "2" + + +def test_option(config, config_file_1): + config.option.from_ini(config_file_1) + + assert config() == {"option": {"section1": {"value1": "1"}, "section2": {"value2": "2"}}} + assert config.option() == {"section1": {"value1": "1"}, "section2": {"value2": "2"}} + assert config.option.section1() == {"value1": "1"} + assert config.option.section1.value1() == "1" + assert config.option.section2() == {"value2": "2"} + assert config.option.section2.value2() == "2" + + +def test_merge(config, config_file_1, config_file_2): + config.from_ini(config_file_1) + config.from_ini(config_file_2) + + assert config() == { + "section1": { + "value1": "11", + "value11": "11", + }, + "section2": { + "value2": "2", + }, + "section3": { + "value3": "3", + }, + } + assert config.section1() == {"value1": "11", "value11": "11"} + assert config.section1.value1() == "11" + assert config.section1.value11() == "11" + assert config.section2() == {"value2": "2"} + assert config.section2.value2() == "2" + assert config.section3() == {"value3": "3"} + assert config.section3.value3() == "3" + + +def test_file_does_not_exist(config): + config.from_ini("./does_not_exist.ini") + assert config() == {} + + +@mark.parametrize("config_type", ["strict"]) +def test_file_does_not_exist_strict_mode(config): + with raises(IOError): + config.from_ini("./does_not_exist.ini") + + +def test_option_file_does_not_exist(config): + config.option.from_ini("does_not_exist.ini") + assert config.option.undefined() is None + + +@mark.parametrize("config_type", ["strict"]) +def test_option_file_does_not_exist_strict_mode(config): + with raises(IOError): + config.option.from_ini("./does_not_exist.ini") + + +def test_required_file_does_not_exist(config): + with raises(IOError): + config.from_ini("./does_not_exist.ini", required=True) + + +def test_required_option_file_does_not_exist(config): + with raises(IOError): + config.option.from_ini("./does_not_exist.ini", required=True) + + +@mark.parametrize("config_type", ["strict"]) +def test_not_required_file_does_not_exist_strict_mode(config): + config.from_ini("./does_not_exist.ini", required=False) + assert config() == {} + + +@mark.parametrize("config_type", ["strict"]) +def test_not_required_option_file_does_not_exist_strict_mode(config): + config.option.from_ini("./does_not_exist.ini", required=False) + with raises(errors.Error): + config.option() diff --git a/tests/unit/providers/configuration/test_from_ini_with_env_py2_py3.py b/tests/unit/providers/configuration/test_from_ini_with_env_py2_py3.py new file mode 100644 index 00000000..7c6ed329 --- /dev/null +++ b/tests/unit/providers/configuration/test_from_ini_with_env_py2_py3.py @@ -0,0 +1,145 @@ +"""Configuration.from_ini() with environment variables interpolation tests.""" + +import os + +from pytest import fixture, mark, raises + + +@fixture +def config_file(tmp_path): + config_file = tmp_path / "config_1.ini" + with open(config_file, "w") as file: + file.write( + "[section1]\n" + "value1=${CONFIG_TEST_ENV}\n" + "value2=${CONFIG_TEST_PATH}/path\n" + ) + return config_file + + +@fixture(autouse=True) +def environment_variables(): + os.environ["CONFIG_TEST_ENV"] = "test-value" + os.environ["CONFIG_TEST_PATH"] = "test-path" + os.environ["DEFINED"] = "defined" + yield + os.environ.pop("CONFIG_TEST_ENV", None) + os.environ.pop("CONFIG_TEST_PATH", None) + os.environ.pop("DEFINED", None) + + +def test_env_variable_interpolation(config, config_file): + config.from_ini(config_file) + + assert config() == { + "section1": { + "value1": "test-value", + "value2": "test-path/path", + }, + } + assert config.section1() == { + "value1": "test-value", + "value2": "test-path/path", + } + assert config.section1.value1() == "test-value" + assert config.section1.value2() == "test-path/path" + + +def test_missing_envs_not_required(config, config_file): + del os.environ["CONFIG_TEST_ENV"] + del os.environ["CONFIG_TEST_PATH"] + + config.from_ini(config_file) + + assert config() == { + "section1": { + "value1": "", + "value2": "/path", + }, + } + assert config.section1() == { + "value1": "", + "value2": "/path", + } + assert config.section1.value1() == "" + assert config.section1.value2() == "/path" + + +def test_missing_envs_required(config, config_file): + with open(config_file, "w") as file: + file.write( + "[section]\n" + "undefined=${UNDEFINED}\n" + ) + with raises(ValueError, match="Missing required environment variable \"UNDEFINED\""): + config.from_ini(config_file, envs_required=True) + + +@mark.parametrize("config_type", ["strict"]) +def test_missing_envs_strict_mode(config, config_file): + with open(config_file, "w") as file: + file.write( + "[section]\n" + "undefined=${UNDEFINED}\n" + ) + with raises(ValueError, match="Missing required environment variable \"UNDEFINED\""): + config.from_ini(config_file) + + +def test_option_missing_envs_not_required(config, config_file): + del os.environ["CONFIG_TEST_ENV"] + del os.environ["CONFIG_TEST_PATH"] + + config.option.from_ini(config_file) + + assert config.option() == { + "section1": { + "value1": "", + "value2": "/path", + }, + } + assert config.option.section1() == { + "value1": "", + "value2": "/path", + } + assert config.option.section1.value1() == "" + assert config.option.section1.value2() == "/path" + + +def test_option_missing_envs_required(config, config_file): + with open(config_file, "w") as file: + file.write( + "[section]\n" + "undefined=${UNDEFINED}\n" + ) + with raises(ValueError, match="Missing required environment variable \"UNDEFINED\""): + config.option.from_ini(config_file, envs_required=True) + + +@mark.parametrize("config_type", ["strict"]) +def test_option_missing_envs_strict_mode(config, config_file): + with open(config_file, "w") as file: + file.write( + "[section]\n" + "undefined=${UNDEFINED}\n" + ) + with raises(ValueError, match="Missing required environment variable \"UNDEFINED\""): + config.option.from_ini(config_file) + + +def test_default_values(config, config_file): + with open(config_file, "w") as file: + file.write( + "[section]\n" + "defined_with_default=${DEFINED:default}\n" + "undefined_with_default=${UNDEFINED:default}\n" + "complex=${DEFINED}/path/${DEFINED:default}/${UNDEFINED}/${UNDEFINED:default}\n" + ) + + config.from_ini(config_file) + + assert config.section() == { + "defined_with_default": "defined", + "undefined_with_default": "default", + "complex": "defined/path/defined//default", + } diff --git a/tests/unit/providers/configuration/test_from_pydantic_py36.py b/tests/unit/providers/configuration/test_from_pydantic_py36.py new file mode 100644 index 00000000..f5a2c97e --- /dev/null +++ b/tests/unit/providers/configuration/test_from_pydantic_py36.py @@ -0,0 +1,184 @@ +"""Configuration.from_pydantic() tests.""" + +import pydantic +from dependency_injector import providers, errors +from pytest import fixture, mark, raises + + +class Section11(pydantic.BaseModel): + value1 = 1 + + +class Section12(pydantic.BaseModel): + value2 = 2 + + +class Settings1(pydantic.BaseSettings): + section1 = Section11() + section2 = Section12() + + +class Section21(pydantic.BaseModel): + value1 = 11 + value11 = 11 + + +class Section3(pydantic.BaseModel): + value3 = 3 + + +class Settings2(pydantic.BaseSettings): + section1 = Section21() + section3 = Section3() + +@fixture +def no_pydantic_module_installed(): + providers.pydantic = None + yield + providers.pydantic = pydantic + + +def test(config): + config.from_pydantic(Settings1()) + + assert config() == {"section1": {"value1": 1}, "section2": {"value2": 2}} + assert config.section1() == {"value1": 1} + assert config.section1.value1() == 1 + assert config.section2() == {"value2": 2} + assert config.section2.value2() == 2 + + +def test_kwarg(config): + config.from_pydantic(Settings1(), exclude={"section2"}) + + assert config() == {"section1": {"value1": 1}} + assert config.section1() == {"value1": 1} + assert config.section1.value1() == 1 + + +def test_merge(config): + config.from_pydantic(Settings1()) + config.from_pydantic(Settings2()) + + assert config() == { + "section1": { + "value1": 11, + "value11": 11, + }, + "section2": { + "value2": 2, + }, + "section3": { + "value3": 3, + }, + } + assert config.section1() == {"value1": 11, "value11": 11} + assert config.section1.value1() == 11 + assert config.section1.value11() == 11 + assert config.section2() == {"value2": 2} + assert config.section2.value2() == 2 + assert config.section3() == {"value3": 3} + assert config.section3.value3() == 3 + + +def test_empty_settings(config): + config.from_pydantic(pydantic.BaseSettings()) + assert config() == {} + + +@mark.parametrize("config_type", ["strict"]) +def test_empty_settings_strict_mode(config): + with raises(ValueError): + config.from_pydantic(pydantic.BaseSettings()) + + +def test_option_empty_settings(config): + config.option.from_pydantic(pydantic.BaseSettings()) + assert config.option() == {} + + +@mark.parametrize("config_type", ["strict"]) +def test_option_empty_settings_strict_mode(config): + with raises(ValueError): + config.option.from_pydantic(pydantic.BaseSettings()) + + +def test_required_empty_settings(config): + with raises(ValueError): + config.from_pydantic(pydantic.BaseSettings(), required=True) + + +def test_required_option_empty_settings(config): + with raises(ValueError): + config.option.from_pydantic(pydantic.BaseSettings(), required=True) + + +@mark.parametrize("config_type", ["strict"]) +def test_not_required_empty_settings_strict_mode(config): + config.from_pydantic(pydantic.BaseSettings(), required=False) + assert config() == {} + + +@mark.parametrize("config_type", ["strict"]) +def test_not_required_option_empty_settings_strict_mode(config): + config.option.from_pydantic(pydantic.BaseSettings(), required=False) + assert config.option() == {} + assert config() == {"option": {}} + + +def test_not_instance_of_settings(config): + with raises(errors.Error) as error: + config.from_pydantic({}) + assert error.value.args[0] == ( + "Unable to recognize settings instance, expect \"pydantic.BaseSettings\", " + "got {0} instead".format({}) + ) + + +def test_option_not_instance_of_settings(config): + with raises(errors.Error) as error: + config.option.from_pydantic({}) + assert error.value.args[0] == ( + "Unable to recognize settings instance, expect \"pydantic.BaseSettings\", " + "got {0} instead".format({}) + ) + + +def test_subclass_instead_of_instance(config): + with raises(errors.Error) as error: + config.from_pydantic(Settings1) + assert error.value.args[0] == ( + "Got settings class, but expect instance: " + "instead \"Settings1\" use \"Settings1()\"" + ) + + +def test_option_subclass_instead_of_instance(config): + with raises(errors.Error) as error: + config.option.from_pydantic(Settings1) + assert error.value.args[0] == ( + "Got settings class, but expect instance: " + "instead \"Settings1\" use \"Settings1()\"" + ) + + +@mark.usefixtures("no_pydantic_module_installed") +def test_no_pydantic_installed(config): + with raises(errors.Error) as error: + config.from_pydantic(Settings1()) + assert error.value.args[0] == ( + "Unable to load pydantic configuration - pydantic is not installed. " + "Install pydantic or install Dependency Injector with pydantic extras: " + "\"pip install dependency-injector[pydantic]\"" + ) + + +@mark.usefixtures("no_pydantic_module_installed") +def test_option_no_pydantic_installed(config): + with raises(errors.Error) as error: + config.option.from_pydantic(Settings1()) + assert error.value.args[0] == ( + "Unable to load pydantic configuration - pydantic is not installed. " + "Install pydantic or install Dependency Injector with pydantic extras: " + "\"pip install dependency-injector[pydantic]\"" + ) diff --git a/tests/unit/providers/configuration/test_from_value_py2_py3.py b/tests/unit/providers/configuration/test_from_value_py2_py3.py new file mode 100644 index 00000000..23dd5191 --- /dev/null +++ b/tests/unit/providers/configuration/test_from_value_py2_py3.py @@ -0,0 +1,19 @@ +"""Configuration.from_value() tests.""" + + +def test_from_value(config): + test_value = 123321 + config.from_value(test_value) + assert config() == test_value + + +def test_option_from_value(config): + test_value_1 = 123 + test_value_2 = 321 + + config.option1.from_value(test_value_1) + config.option2.from_value(test_value_2) + + assert config() == {"option1": test_value_1, "option2": test_value_2} + assert config.option1() == test_value_1 + assert config.option2() == test_value_2 diff --git a/tests/unit/providers/configuration/test_from_yaml_py2_py3.py b/tests/unit/providers/configuration/test_from_yaml_py2_py3.py new file mode 100644 index 00000000..5e8f72fd --- /dev/null +++ b/tests/unit/providers/configuration/test_from_yaml_py2_py3.py @@ -0,0 +1,142 @@ +"""Configuration.from_yaml() tests.""" + +from dependency_injector import providers, errors +from pytest import fixture, mark, raises + + +@fixture +def config_file_1(tmp_path): + config_file = tmp_path / "config_1.ini" + with open(config_file, "w") as file: + file.write( + "section1:\n" + " value1: 1\n" + "\n" + "section2:\n" + " value2: 2\n" + ) + return config_file + + +@fixture +def config_file_2(tmp_path): + config_file = tmp_path / "config_2.ini" + with open(config_file, "w") as file: + file.write( + "section1:\n" + " value1: 11\n" + " value11: 11\n" + "section3:\n" + " value3: 3\n" + ) + return config_file + + +@fixture +def no_yaml_module_installed(): + yaml = providers.yaml + providers.yaml = None + yield + providers.yaml = yaml + + +def test(config, config_file_1): + config.from_yaml(config_file_1) + + assert config() == {"section1": {"value1": 1}, "section2": {"value2": 2}} + assert config.section1() == {"value1": 1} + assert config.section1.value1() == 1 + assert config.section2() == {"value2": 2} + assert config.section2.value2() == 2 + + +def test_merge(config, config_file_1, config_file_2): + config.from_yaml(config_file_1) + config.from_yaml(config_file_2) + + assert config() == { + "section1": { + "value1": 11, + "value11": 11, + }, + "section2": { + "value2": 2, + }, + "section3": { + "value3": 3, + }, + } + assert config.section1() == {"value1": 11, "value11": 11} + assert config.section1.value1() == 11 + assert config.section1.value11() == 11 + assert config.section2() == {"value2": 2} + assert config.section2.value2() == 2 + assert config.section3() == {"value3": 3} + assert config.section3.value3() == 3 + + +def test_file_does_not_exist(config): + config.from_yaml("./does_not_exist.yml") + assert config() == {} + + +@mark.parametrize("config_type", ["strict"]) +def test_file_does_not_exist_strict_mode(config): + with raises(IOError): + config.from_yaml("./does_not_exist.yml") + + +def test_option_file_does_not_exist(config): + config.option.from_yaml("./does_not_exist.yml") + assert config.option() is None + + +@mark.parametrize("config_type", ["strict"]) +def test_option_file_does_not_exist_strict_mode(config): + with raises(IOError): + config.option.from_yaml("./does_not_exist.yml") + + +def test_required_file_does_not_exist(config): + with raises(IOError): + config.from_yaml("./does_not_exist.yml", required=True) + + +def test_required_option_file_does_not_exist(config): + with raises(IOError): + config.option.from_yaml("./does_not_exist.yml", required=True) + + +@mark.parametrize("config_type", ["strict"]) +def test_not_required_file_does_not_exist_strict_mode(config): + config.from_yaml("./does_not_exist.yml", required=False) + assert config() == {} + + +@mark.parametrize("config_type", ["strict"]) +def test_not_required_option_file_does_not_exist_strict_mode(config): + config.option.from_yaml("./does_not_exist.yml", required=False) + with raises(errors.Error): + config.option() + + +@mark.usefixtures("no_yaml_module_installed") +def test_no_yaml_installed(config, config_file_1): + with raises(errors.Error) as error: + config.from_yaml(config_file_1) + assert error.value.args[0] == ( + "Unable to load yaml configuration - PyYAML is not installed. " + "Install PyYAML or install Dependency Injector with yaml extras: " + "\"pip install dependency-injector[yaml]\"" + ) + + +@mark.usefixtures("no_yaml_module_installed") +def test_option_no_yaml_installed(config, config_file_1): + with raises(errors.Error) as error: + config.option.from_yaml(config_file_1) + assert error.value.args[0] == ( + "Unable to load yaml configuration - PyYAML is not installed. " + "Install PyYAML or install Dependency Injector with yaml extras: " + "\"pip install dependency-injector[yaml]\"" + ) diff --git a/tests/unit/providers/configuration/test_from_yaml_with_env_py2_py3.py b/tests/unit/providers/configuration/test_from_yaml_with_env_py2_py3.py new file mode 100644 index 00000000..44e2d1b4 --- /dev/null +++ b/tests/unit/providers/configuration/test_from_yaml_with_env_py2_py3.py @@ -0,0 +1,185 @@ +"""Configuration.from_yaml() with environment variables interpolation tests.""" + +import os + +import yaml +from pytest import fixture, mark, raises + + +@fixture +def config_file(tmp_path): + config_file = tmp_path / "config_1.ini" + with open(config_file, "w") as file: + file.write( + "section1:\n" + " value1: ${CONFIG_TEST_ENV}\n" + " value2: ${CONFIG_TEST_PATH}/path\n" + ) + return config_file + + +@fixture(autouse=True) +def environment_variables(): + os.environ["CONFIG_TEST_ENV"] = "test-value" + os.environ["CONFIG_TEST_PATH"] = "test-path" + os.environ["DEFINED"] = "defined" + yield + os.environ.pop("CONFIG_TEST_ENV", None) + os.environ.pop("CONFIG_TEST_PATH", None) + os.environ.pop("DEFINED", None) + + +def test_env_variable_interpolation(config, config_file): + config.from_yaml(config_file) + + assert config() == { + "section1": { + "value1": "test-value", + "value2": "test-path/path", + }, + } + assert config.section1() == { + "value1": "test-value", + "value2": "test-path/path", + } + assert config.section1.value1() == "test-value" + assert config.section1.value2() == "test-path/path" + + +def test_missing_envs_not_required(config, config_file): + del os.environ["CONFIG_TEST_ENV"] + del os.environ["CONFIG_TEST_PATH"] + + config.from_yaml(config_file) + + assert config() == { + "section1": { + "value1": None, + "value2": "/path", + }, + } + assert config.section1() == { + "value1": None, + "value2": "/path", + } + assert config.section1.value1() is None + assert config.section1.value2() == "/path" + + +def test_missing_envs_required(config, config_file): + with open(config_file, "w") as file: + file.write( + "section:\n" + " undefined: ${UNDEFINED}\n" + ) + with raises(ValueError, match="Missing required environment variable \"UNDEFINED\""): + config.from_yaml(config_file, envs_required=True) + + +@mark.parametrize("config_type", ["strict"]) +def test_missing_envs_strict_mode(config, config_file): + with open(config_file, "w") as file: + file.write( + "section:\n" + " undefined: ${UNDEFINED}\n" + ) + with raises(ValueError, match="Missing required environment variable \"UNDEFINED\""): + config.from_yaml(config_file) + + +def test_option_missing_envs_not_required(config, config_file): + del os.environ["CONFIG_TEST_ENV"] + del os.environ["CONFIG_TEST_PATH"] + + config.option.from_yaml(config_file) + + assert config.option() == { + "section1": { + "value1": None, + "value2": "/path", + }, + } + assert config.option.section1() == { + "value1": None, + "value2": "/path", + } + assert config.option.section1.value1() is None + assert config.option.section1.value2() == "/path" + + +def test_option_missing_envs_required(config, config_file): + with open(config_file, "w") as file: + file.write( + "section:\n" + " undefined: ${UNDEFINED}\n" + ) + with raises(ValueError, match="Missing required environment variable \"UNDEFINED\""): + config.option.from_yaml(config_file, envs_required=True) + + +@mark.parametrize("config_type", ["strict"]) +def test_option_missing_envs_strict_mode(config, config_file): + with open(config_file, "w") as file: + file.write( + "section:\n" + " undefined: ${UNDEFINED}\n" + ) + with raises(ValueError, match="Missing required environment variable \"UNDEFINED\""): + config.option.from_yaml(config_file) + + +def test_default_values(config, config_file): + with open(config_file, "w") as file: + file.write( + "section:\n" + " defined_with_default: ${DEFINED:default}\n" + " undefined_with_default: ${UNDEFINED:default}\n" + " complex: ${DEFINED}/path/${DEFINED:default}/${UNDEFINED}/${UNDEFINED:default}\n" + ) + + config.from_yaml(config_file) + + assert config.section() == { + "defined_with_default": "defined", + "undefined_with_default": "default", + "complex": "defined/path/defined//default", + } + + +def test_option_env_variable_interpolation(config, config_file): + config.option.from_yaml(config_file) + + assert config.option() == { + "section1": { + "value1": "test-value", + "value2": "test-path/path", + }, + } + assert config.option.section1() == { + "value1": "test-value", + "value2": "test-path/path", + } + assert config.option.section1.value1() == "test-value" + assert config.option.section1.value2() == "test-path/path" + + +def test_env_variable_interpolation_custom_loader(config, config_file): + config.from_yaml(config_file, loader=yaml.UnsafeLoader) + + assert config.section1() == { + "value1": "test-value", + "value2": "test-path/path", + } + assert config.section1.value1() == "test-value" + assert config.section1.value2() == "test-path/path" + + +def test_option_env_variable_interpolation_custom_loader(config, config_file): + config.option.from_yaml(config_file, loader=yaml.UnsafeLoader) + + assert config.option.section1() == { + "value1": "test-value", + "value2": "test-path/path", + } + assert config.option.section1.value1() == "test-value" + assert config.option.section1.value2() == "test-path/path" diff --git a/tests/unit/providers/resource/test_async_resource_py3.py b/tests/unit/providers/resource/test_async_resource_py35.py similarity index 98% rename from tests/unit/providers/resource/test_async_resource_py3.py rename to tests/unit/providers/resource/test_async_resource_py35.py index bc66a010..ba983d60 100644 --- a/tests/unit/providers/resource/test_async_resource_py3.py +++ b/tests/unit/providers/resource/test_async_resource_py35.py @@ -2,6 +2,7 @@ import asyncio import inspect +import sys from typing import Any from dependency_injector import containers, providers, resources @@ -33,6 +34,7 @@ async def _init(): @mark.asyncio +@mark.skipif(sys.version_info < (3, 6), reason="requires Python 3.6+") async def test_init_async_generator(): resource = object() diff --git a/tests/unit/providers/test_configuration_py2_py3.py b/tests/unit/providers/test_configuration_py2_py3.py deleted file mode 100644 index b91c0a77..00000000 --- a/tests/unit/providers/test_configuration_py2_py3.py +++ /dev/null @@ -1,1445 +0,0 @@ -"""Dependency injector config providers unit tests.""" - -import contextlib -import decimal -import os -import sys -import tempfile - -import unittest - -from dependency_injector import containers, providers, errors -from pytest import raises -try: - import yaml -except ImportError: - yaml = None - -try: - import pydantic -except ImportError: - pydantic = None - - -class ConfigTests(unittest.TestCase): - - def setUp(self): - self.config = providers.Configuration(name="config") - - def tearDown(self): - del self.config - - def test_init_optional(self): - provider = providers.Configuration() - provider.set_name("myconfig") - provider.set_default({"foo": "bar"}) - provider.set_strict(True) - - assert provider.get_name() == "myconfig" - assert provider.get_default() == {"foo": "bar"} - assert provider.get_strict() is True - - def test_set_name_returns_self(self): - provider = providers.Configuration() - assert provider.set_name("myconfig") is provider - - def test_set_default_returns_self(self): - provider = providers.Configuration() - assert provider.set_default({}) is provider - - def test_set_strict_returns_self(self): - provider = providers.Configuration() - assert provider.set_strict(True) is provider - - def test_default_name(self): - config = providers.Configuration() - assert config.get_name() == "config" - - def test_providers_are_providers(self): - assert providers.is_provider(self.config.a) is True - assert providers.is_provider(self.config.a.b) is True - assert providers.is_provider(self.config.a.b.c) is True - assert providers.is_provider(self.config.a.b.d) is True - - def test_providers_are_not_delegates(self): - assert providers.is_delegated(self.config.a) is False - assert providers.is_delegated(self.config.a.b) is False - assert providers.is_delegated(self.config.a.b.c) is False - assert providers.is_delegated(self.config.a.b.d) is False - - def test_providers_identity(self): - assert self.config.a is self.config.a - assert self.config.a.b is self.config.a.b - assert self.config.a.b.c is self.config.a.b.c - assert self.config.a.b.d is self.config.a.b.d - - def test_get_name(self): - assert self.config.a.b.c.get_name() == "config.a.b.c" - - def test_providers_value_setting(self): - a = self.config.a - ab = self.config.a.b - abc = self.config.a.b.c - abd = self.config.a.b.d - - self.config.update({"a": {"b": {"c": 1, "d": 2}}}) - - assert a() == {"b": {"c": 1, "d": 2}} - assert ab() == {"c": 1, "d": 2} - assert abc() == 1 - assert abd() == 2 - - def test_providers_with_already_set_value(self): - self.config.update({"a": {"b": {"c": 1, "d": 2}}}) - - a = self.config.a - ab = self.config.a.b - abc = self.config.a.b.c - abd = self.config.a.b.d - - assert a() == {"b": {"c": 1, "d": 2}} - assert ab() == {"c": 1, "d": 2} - assert abc() == 1 - assert abd() == 2 - - def test_as_int(self): - value_provider = providers.Callable(lambda value: value, self.config.test.as_int()) - self.config.from_dict({"test": "123"}) - - value = value_provider() - assert value == 123 - - def test_as_float(self): - value_provider = providers.Callable(lambda value: value, self.config.test.as_float()) - self.config.from_dict({"test": "123.123"}) - - value = value_provider() - assert value == 123.123 - - def test_as_(self): - value_provider = providers.Callable( - lambda value: value, - self.config.test.as_(decimal.Decimal), - ) - self.config.from_dict({"test": "123.123"}) - - value = value_provider() - assert value == decimal.Decimal("123.123") - - @unittest.skipIf(sys.version_info[:2] == (2, 7), "Python 2.7 does not support this assert") - def test_required(self): - provider = providers.Callable( - lambda value: value, - self.config.a.required(), - ) - with raises(errors.Error, match="Undefined configuration option \"config.a\""): - provider() - - def test_required_defined_none(self): - provider = providers.Callable( - lambda value: value, - self.config.a.required(), - ) - self.config.from_dict({"a": None}) - assert provider() is None - - def test_required_no_side_effect(self): - _ = providers.Callable( - lambda value: value, - self.config.a.required(), - ) - assert self.config.a() is None - - def test_required_as_(self): - provider = providers.List( - self.config.int_test.required().as_int(), - self.config.float_test.required().as_float(), - self.config._as_test.required().as_(decimal.Decimal), - ) - self.config.from_dict({"int_test": "1", "float_test": "2.0", "_as_test": "3.0"}) - - assert provider() == [1, 2.0, decimal.Decimal("3.0")] - - def test_providers_value_override(self): - a = self.config.a - ab = self.config.a.b - abc = self.config.a.b.c - abd = self.config.a.b.d - - self.config.override({"a": {"b": {"c": 1, "d": 2}}}) - - assert a() == {"b": {"c": 1, "d": 2}} - assert ab() == {"c": 1, "d": 2} - assert abc() == 1 - assert abd() == 2 - - def test_configuration_option_override_and_reset_override(self): - # Bug: https://github.com/ets-labs/python-dependency-injector/issues/319 - self.config.from_dict({"a": {"b": {"c": 1}}}) - - assert self.config.a.b.c() == 1 - - with self.config.set("a.b.c", "xxx"): - assert self.config.a.b.c() == "xxx" - assert self.config.a.b.c() == 1 - - with self.config.a.b.c.override("yyy"): - assert self.config.a.b.c() == "yyy" - - assert self.config.a.b.c() == 1 - - def test_providers_with_already_overridden_value(self): - self.config.override({"a": {"b": {"c": 1, "d": 2}}}) - - a = self.config.a - ab = self.config.a.b - abc = self.config.a.b.c - abd = self.config.a.b.d - - assert a() == {"b": {"c": 1, "d": 2}} - assert ab() == {"c": 1, "d": 2} - assert abc() == 1 - assert abd() == 2 - - def test_providers_with_default_value(self): - self.config = providers.Configuration(name="config", default={"a": {"b": {"c": 1, "d": 2}}}) - - a = self.config.a - ab = self.config.a.b - abc = self.config.a.b.c - abd = self.config.a.b.d - - assert a() == {"b": {"c": 1, "d": 2}} - assert ab() == {"c": 1, "d": 2} - assert abc() == 1 - assert abd() == 2 - - def test_providers_with_default_value_overriding(self): - self.config = providers.Configuration( - name="config", default={"a": {"b": {"c": 1, "d": 2}}}) - - assert self.config.a() == {"b": {"c": 1, "d": 2}} - assert self.config.a.b() == {"c": 1, "d": 2} - assert self.config.a.b.c() == 1 - assert self.config.a.b.d() == 2 - - self.config.override({"a": {"b": {"c": 3, "d": 4}}}) - assert self.config.a() == {"b": {"c": 3, "d": 4}} - assert self.config.a.b() == {"c": 3, "d": 4} - assert self.config.a.b.c() == 3 - assert self.config.a.b.d() == 4 - - self.config.reset_override() - assert self.config.a() == {"b": {"c": 1, "d": 2}} - assert self.config.a.b() == {"c": 1, "d": 2} - assert self.config.a.b.c() == 1 - assert self.config.a.b.d() == 2 - - def test_value_of_undefined_option(self): - assert self.config.a() is None - - @unittest.skipIf(sys.version_info[:2] == (2, 7), "Python 2.7 does not support this assert") - def test_value_of_undefined_option_in_strict_mode(self): - self.config = providers.Configuration(strict=True) - with raises(errors.Error, match="Undefined configuration option \"config.a\""): - self.config.a() - - @unittest.skipIf(sys.version_info[:2] == (2, 7), "Python 2.7 does not support this assert") - def test_value_of_undefined_option_with_root_none_in_strict_mode(self): - self.config = providers.Configuration(strict=True) - self.config.override(None) - with raises(errors.Error, match="Undefined configuration option \"config.a\""): - self.config.a() - - def test_value_of_defined_none_option_in_strict_mode(self): - self.config = providers.Configuration(strict=True) - self.config.from_dict({"a": None}) - assert self.config.a() is None - - def test_getting_of_special_attributes(self): - with raises(AttributeError): - self.config.__name__ - - def test_getting_of_special_attributes_from_child(self): - a = self.config.a - with raises(AttributeError): - a.__name__ - - def test_context_manager_alias(self): - class Container(containers.DeclarativeContainer): - config = providers.Configuration() - - container = Container() - - with container.config as cfg: - cfg.override({"foo": "foo", "bar": "bar"}) - - assert container.config() == {"foo": "foo", "bar": "bar"} - assert cfg() == {"foo": "foo", "bar": "bar"} - assert container.config is cfg - - def test_option_context_manager_alias(self): - class Container(containers.DeclarativeContainer): - config = providers.Configuration() - - container = Container() - - with container.config.option as opt: - opt.override({"foo": "foo", "bar": "bar"}) - - assert container.config() == {"option": {"foo": "foo", "bar": "bar"}} - assert container.config.option() == {"foo": "foo", "bar": "bar"} - assert opt() == {"foo": "foo", "bar": "bar"} - assert container.config.option is opt - - def test_missing_key(self): - # See: https://github.com/ets-labs/python-dependency-injector/issues/358 - self.config.override(None) - value = self.config.key() - assert value is None - - def test_deepcopy(self): - provider = providers.Configuration("config") - provider_copy = providers.deepcopy(provider) - - assert provider is not provider_copy - assert isinstance(provider, providers.Configuration) - - def test_deepcopy_from_memo(self): - provider = providers.Configuration("config") - provider_copy_memo = providers.Configuration("config") - - provider_copy = providers.deepcopy(provider, memo={id(provider): provider_copy_memo}) - - assert provider_copy is provider_copy_memo - - def test_deepcopy_overridden(self): - provider = providers.Configuration("config") - object_provider = providers.Object(object()) - - provider.override(object_provider) - - provider_copy = providers.deepcopy(provider) - object_provider_copy = provider_copy.overridden[0] - - assert provider is not provider_copy - assert isinstance(provider, providers.Configuration) - - assert object_provider is not object_provider_copy - assert isinstance(object_provider_copy, providers.Object) - - def test_repr(self): - assert repr(self.config) == ( - "".format(repr("config"), hex(id(self.config))) - ) - - def test_repr_child(self): - assert repr(self.config.a.b.c) == ( - "".format(repr("config.a.b.c"), hex(id(self.config.a.b.c))) - ) - - -class ConfigLinkingTests(unittest.TestCase): - - class TestCore(containers.DeclarativeContainer): - config = providers.Configuration("core") - value_getter = providers.Callable(lambda _: _, config.value) - - class TestServices(containers.DeclarativeContainer): - config = providers.Configuration("services") - value_getter = providers.Callable(lambda _: _, config.value) - - def test(self): - root_config = providers.Configuration("main") - core = self.TestCore(config=root_config.core) - services = self.TestServices(config=root_config.services) - - root_config.override( - { - "core": { - "value": "core", - }, - "services": { - "value": "services", - }, - }, - ) - - assert core.config() == {"value": "core"} - assert core.config.value() == "core" - assert core.value_getter() == "core" - - assert services.config() == {"value": "services"} - assert services.config.value() == "services" - assert services.value_getter() == "services" - - def test_double_override(self): - root_config = providers.Configuration("main") - core = self.TestCore(config=root_config.core) - services = self.TestServices(config=root_config.services) - - root_config.override( - { - "core": { - "value": "core1", - }, - "services": { - "value": "services1", - }, - }, - ) - root_config.override( - { - "core": { - "value": "core2", - }, - "services": { - "value": "services2", - }, - }, - ) - - assert core.config() == {"value": "core2"} - assert core.config.value() == "core2" - assert core.value_getter() == "core2" - - assert services.config() == {"value": "services2"} - assert services.config.value() == "services2" - assert services.value_getter() == "services2" - - def test_reset_overriding_cache(self): - # See: https://github.com/ets-labs/python-dependency-injector/issues/428 - class Core(containers.DeclarativeContainer): - config = providers.Configuration() - - greetings = providers.Factory(str, config.greeting) - - class Application(containers.DeclarativeContainer): - config = providers.Configuration() - - core = providers.Container( - Core, - config=config, - ) - - greetings = providers.Factory(str, config.greeting) - - container = Application() - - container.config.set("greeting", "Hello World") - assert container.greetings() == "Hello World" - assert container.core.greetings() == "Hello World" - - container.config.set("greeting", "Hello Bob") - assert container.greetings() == "Hello Bob" - assert container.core.greetings() == "Hello Bob" - - def test_reset_overriding_cache_for_option(self): - # See: https://github.com/ets-labs/python-dependency-injector/issues/428 - class Core(containers.DeclarativeContainer): - config = providers.Configuration() - - greetings = providers.Factory(str, config.greeting) - - class Application(containers.DeclarativeContainer): - config = providers.Configuration() - - core = providers.Container( - Core, - config=config.option, - ) - - greetings = providers.Factory(str, config.option.greeting) - - container = Application() - - container.config.set("option.greeting", "Hello World") - assert container.greetings() == "Hello World" - assert container.core.greetings() == "Hello World" - - container.config.set("option.greeting", "Hello Bob") - assert container.greetings() == "Hello Bob" - assert container.core.greetings() == "Hello Bob" - - -class ConfigFromIniTests(unittest.TestCase): - - def setUp(self): - self.config = providers.Configuration(name="config") - - _, self.config_file_1 = tempfile.mkstemp() - with open(self.config_file_1, "w") as config_file: - config_file.write( - "[section1]\n" - "value1=1\n" - "\n" - "[section2]\n" - "value2=2\n" - ) - - _, self.config_file_2 = tempfile.mkstemp() - with open(self.config_file_2, "w") as config_file: - config_file.write( - "[section1]\n" - "value1=11\n" - "value11=11\n" - "[section3]\n" - "value3=3\n" - ) - - def tearDown(self): - del self.config - os.unlink(self.config_file_1) - os.unlink(self.config_file_2) - - def test(self): - self.config.from_ini(self.config_file_1) - - assert self.config() == {"section1": {"value1": "1"}, "section2": {"value2": "2"}} - assert self.config.section1() == {"value1": "1"} - assert self.config.section1.value1() == "1" - assert self.config.section2() == {"value2": "2"} - assert self.config.section2.value2() == "2" - - def test_option(self): - self.config.option.from_ini(self.config_file_1) - - assert self.config() == {"option": {"section1": {"value1": "1"}, "section2": {"value2": "2"}}} - assert self.config.option() == {"section1": {"value1": "1"}, "section2": {"value2": "2"}} - assert self.config.option.section1() == {"value1": "1"} - assert self.config.option.section1.value1() == "1" - assert self.config.option.section2() == {"value2": "2"} - assert self.config.option.section2.value2() == "2" - - def test_merge(self): - self.config.from_ini(self.config_file_1) - self.config.from_ini(self.config_file_2) - - assert self.config() == { - "section1": { - "value1": "11", - "value11": "11", - }, - "section2": { - "value2": "2", - }, - "section3": { - "value3": "3", - }, - } - assert self.config.section1() == {"value1": "11", "value11": "11"} - assert self.config.section1.value1() == "11" - assert self.config.section1.value11() == "11" - assert self.config.section2() == {"value2": "2"} - assert self.config.section2.value2() == "2" - assert self.config.section3() == {"value3": "3"} - assert self.config.section3.value3() == "3" - - def test_file_does_not_exist(self): - self.config.from_ini("./does_not_exist.ini") - assert self.config() == {} - - def test_file_does_not_exist_strict_mode(self): - self.config = providers.Configuration(strict=True) - with raises(IOError): - self.config.from_ini("./does_not_exist.ini") - - def test_option_file_does_not_exist(self): - self.config.option.from_ini("does_not_exist.ini") - assert self.config.option.undefined() is None - - def test_option_file_does_not_exist_strict_mode(self): - self.config = providers.Configuration(strict=True) - with raises(IOError): - self.config.option.from_ini("./does_not_exist.ini") - - def test_required_file_does_not_exist(self): - with raises(IOError): - self.config.from_ini("./does_not_exist.ini", required=True) - - def test_required_option_file_does_not_exist(self): - with raises(IOError): - self.config.option.from_ini("./does_not_exist.ini", required=True) - - def test_not_required_file_does_not_exist_strict_mode(self): - self.config = providers.Configuration(strict=True) - self.config.from_ini("./does_not_exist.ini", required=False) - assert self.config() == {} - - def test_not_required_option_file_does_not_exist_strict_mode(self): - self.config = providers.Configuration(strict=True) - self.config.option.from_ini("./does_not_exist.ini", required=False) - with raises(errors.Error): - self.config.option() - - -class ConfigFromIniWithEnvInterpolationTests(unittest.TestCase): - - def setUp(self): - self.config = providers.Configuration(name="config") - - os.environ["CONFIG_TEST_ENV"] = "test-value" - os.environ["CONFIG_TEST_PATH"] = "test-path" - - _, self.config_file = tempfile.mkstemp() - with open(self.config_file, "w") as config_file: - config_file.write( - "[section1]\n" - "value1=${CONFIG_TEST_ENV}\n" - "value2=${CONFIG_TEST_PATH}/path\n" - ) - - def tearDown(self): - del self.config - os.environ.pop("CONFIG_TEST_ENV", None) - os.environ.pop("CONFIG_TEST_PATH", None) - os.unlink(self.config_file) - - def test_env_variable_interpolation(self): - self.config.from_ini(self.config_file) - - assert self.config() == { - "section1": { - "value1": "test-value", - "value2": "test-path/path", - }, - } - assert self.config.section1() == { - "value1": "test-value", - "value2": "test-path/path", - } - assert self.config.section1.value1() == "test-value" - assert self.config.section1.value2() == "test-path/path" - - def test_missing_envs_not_required(self): - del os.environ["CONFIG_TEST_ENV"] - del os.environ["CONFIG_TEST_PATH"] - - self.config.from_ini(self.config_file) - - assert self.config() == { - "section1": { - "value1": "", - "value2": "/path", - }, - } - assert self.config.section1() == { - "value1": "", - "value2": "/path", - } - assert self.config.section1.value1() == "" - assert self.config.section1.value2() == "/path" - - def test_missing_envs_required(self): - with open(self.config_file, "w") as config_file: - config_file.write( - "[section]\n" - "undefined=${UNDEFINED}\n" - ) - - with raises(ValueError) as exception_info: - self.config.from_ini(self.config_file, envs_required=True) - assert str(exception_info.value) == "Missing required environment variable \"UNDEFINED\"" - - def test_missing_envs_strict_mode(self): - with open(self.config_file, "w") as config_file: - config_file.write( - "[section]\n" - "undefined=${UNDEFINED}\n" - ) - - self.config.set_strict(True) - with raises(ValueError) as exception_info: - self.config.from_ini(self.config_file) - assert str(exception_info.value) == "Missing required environment variable \"UNDEFINED\"" - - def test_option_missing_envs_not_required(self): - del os.environ["CONFIG_TEST_ENV"] - del os.environ["CONFIG_TEST_PATH"] - - self.config.option.from_ini(self.config_file) - - assert self.config.option() == { - "section1": { - "value1": "", - "value2": "/path", - }, - } - assert self.config.option.section1() == { - "value1": "", - "value2": "/path", - } - assert self.config.option.section1.value1() == "" - assert self.config.option.section1.value2() == "/path" - - def test_option_missing_envs_required(self): - with open(self.config_file, "w") as config_file: - config_file.write( - "[section]\n" - "undefined=${UNDEFINED}\n" - ) - - with raises(ValueError) as exception_info: - self.config.option.from_ini(self.config_file, envs_required=True) - assert str(exception_info.value) == "Missing required environment variable \"UNDEFINED\"" - - def test_option_missing_envs_strict_mode(self): - with open(self.config_file, "w") as config_file: - config_file.write( - "[section]\n" - "undefined=${UNDEFINED}\n" - ) - - self.config.set_strict(True) - with raises(ValueError) as exception_info: - self.config.option.from_ini(self.config_file) - assert str(exception_info.value) == "Missing required environment variable \"UNDEFINED\"" - - def test_default_values(self): - os.environ["DEFINED"] = "defined" - self.addCleanup(os.environ.pop, "DEFINED") - - with open(self.config_file, "w") as config_file: - config_file.write( - "[section]\n" - "defined_with_default=${DEFINED:default}\n" - "undefined_with_default=${UNDEFINED:default}\n" - "complex=${DEFINED}/path/${DEFINED:default}/${UNDEFINED}/${UNDEFINED:default}\n" - ) - - self.config.from_ini(self.config_file) - - assert self.config.section() == { - "defined_with_default": "defined", - "undefined_with_default": "default", - "complex": "defined/path/defined//default", - } - - -class ConfigFromYamlTests(unittest.TestCase): - - def setUp(self): - self.config = providers.Configuration(name="config") - - _, self.config_file_1 = tempfile.mkstemp() - with open(self.config_file_1, "w") as config_file: - config_file.write( - "section1:\n" - " value1: 1\n" - "\n" - "section2:\n" - " value2: 2\n" - ) - - _, self.config_file_2 = tempfile.mkstemp() - with open(self.config_file_2, "w") as config_file: - config_file.write( - "section1:\n" - " value1: 11\n" - " value11: 11\n" - "section3:\n" - " value3: 3\n" - ) - - def tearDown(self): - del self.config - os.unlink(self.config_file_1) - os.unlink(self.config_file_2) - - def test(self): - self.config.from_yaml(self.config_file_1) - - assert self.config() == {"section1": {"value1": 1}, "section2": {"value2": 2}} - assert self.config.section1() == {"value1": 1} - assert self.config.section1.value1() == 1 - assert self.config.section2() == {"value2": 2} - assert self.config.section2.value2() == 2 - - def test_merge(self): - self.config.from_yaml(self.config_file_1) - self.config.from_yaml(self.config_file_2) - - assert self.config() == { - "section1": { - "value1": 11, - "value11": 11, - }, - "section2": { - "value2": 2, - }, - "section3": { - "value3": 3, - }, - } - assert self.config.section1() == {"value1": 11, "value11": 11} - assert self.config.section1.value1() == 11 - assert self.config.section1.value11() == 11 - assert self.config.section2() == {"value2": 2} - assert self.config.section2.value2() == 2 - assert self.config.section3() == {"value3": 3} - assert self.config.section3.value3() == 3 - - def test_file_does_not_exist(self): - self.config.from_yaml("./does_not_exist.yml") - assert self.config() == {} - - def test_file_does_not_exist_strict_mode(self): - self.config = providers.Configuration(strict=True) - with raises(IOError): - self.config.from_yaml("./does_not_exist.yml") - - def test_option_file_does_not_exist(self): - self.config.option.from_yaml("./does_not_exist.yml") - assert self.config.option() is None - - def test_option_file_does_not_exist_strict_mode(self): - self.config = providers.Configuration(strict=True) - with raises(IOError): - self.config.option.from_yaml("./does_not_exist.yml") - - def test_required_file_does_not_exist(self): - with raises(IOError): - self.config.from_yaml("./does_not_exist.yml", required=True) - - def test_required_option_file_does_not_exist(self): - with raises(IOError): - self.config.option.from_yaml("./does_not_exist.yml", required=True) - - def test_not_required_file_does_not_exist_strict_mode(self): - self.config = providers.Configuration(strict=True) - self.config.from_yaml("./does_not_exist.yml", required=False) - assert self.config() == {} - - def test_not_required_option_file_does_not_exist_strict_mode(self): - self.config = providers.Configuration(strict=True) - self.config.option.from_yaml("./does_not_exist.yml", required=False) - with raises(errors.Error): - self.config.option() - - def test_no_yaml_installed(self): - @contextlib.contextmanager - def no_yaml_module(): - yaml = providers.yaml - providers.yaml = None - - yield - - providers.yaml = yaml - - with no_yaml_module(): - with raises(errors.Error) as error: - self.config.from_yaml(self.config_file_1) - - assert error.value.args[0] == ( - "Unable to load yaml configuration - PyYAML is not installed. " - "Install PyYAML or install Dependency Injector with yaml extras: " - "\"pip install dependency-injector[yaml]\"" - ) - - def test_option_no_yaml_installed(self): - @contextlib.contextmanager - def no_yaml_module(): - yaml = providers.yaml - providers.yaml = None - - yield - - providers.yaml = yaml - - with no_yaml_module(): - with raises(errors.Error) as error: - self.config.option.from_yaml(self.config_file_1) - - assert error.value.args[0] == ( - "Unable to load yaml configuration - PyYAML is not installed. " - "Install PyYAML or install Dependency Injector with yaml extras: " - "\"pip install dependency-injector[yaml]\"" - ) - - -class ConfigFromYamlWithEnvInterpolationTests(unittest.TestCase): - - def setUp(self): - self.config = providers.Configuration(name="config") - - os.environ["CONFIG_TEST_ENV"] = "test-value" - os.environ["CONFIG_TEST_PATH"] = "test-path" - - _, self.config_file = tempfile.mkstemp() - with open(self.config_file, "w") as config_file: - config_file.write( - "section1:\n" - " value1: ${CONFIG_TEST_ENV}\n" - " value2: ${CONFIG_TEST_PATH}/path\n" - ) - - def tearDown(self): - del self.config - os.environ.pop("CONFIG_TEST_ENV", None) - os.environ.pop("CONFIG_TEST_PATH", None) - os.unlink(self.config_file) - - def test_env_variable_interpolation(self): - self.config.from_yaml(self.config_file) - - assert self.config() == { - "section1": { - "value1": "test-value", - "value2": "test-path/path", - }, - } - assert self.config.section1() == { - "value1": "test-value", - "value2": "test-path/path", - } - assert self.config.section1.value1() == "test-value" - assert self.config.section1.value2() == "test-path/path" - - def test_missing_envs_not_required(self): - del os.environ["CONFIG_TEST_ENV"] - del os.environ["CONFIG_TEST_PATH"] - - self.config.from_yaml(self.config_file) - - assert self.config() == { - "section1": { - "value1": None, - "value2": "/path", - }, - } - assert self.config.section1() == { - "value1": None, - "value2": "/path", - } - assert self.config.section1.value1() is None - assert self.config.section1.value2() == "/path" - - def test_missing_envs_required(self): - with open(self.config_file, "w") as config_file: - config_file.write( - "section:\n" - " undefined: ${UNDEFINED}\n" - ) - - with raises(ValueError) as context: - self.config.from_yaml(self.config_file, envs_required=True) - assert str(context.value) == "Missing required environment variable \"UNDEFINED\"" - - def test_missing_envs_strict_mode(self): - with open(self.config_file, "w") as config_file: - config_file.write( - "section:\n" - " undefined: ${UNDEFINED}\n" - ) - - self.config.set_strict(True) - with raises(ValueError) as context: - self.config.from_yaml(self.config_file) - assert str(context.value) == "Missing required environment variable \"UNDEFINED\"" - - def test_option_missing_envs_not_required(self): - del os.environ["CONFIG_TEST_ENV"] - del os.environ["CONFIG_TEST_PATH"] - - self.config.option.from_yaml(self.config_file) - - assert self.config.option() == { - "section1": { - "value1": None, - "value2": "/path", - }, - } - assert self.config.option.section1() == { - "value1": None, - "value2": "/path", - } - assert self.config.option.section1.value1() is None - assert self.config.option.section1.value2() == "/path" - - def test_option_missing_envs_required(self): - with open(self.config_file, "w") as config_file: - config_file.write( - "section:\n" - " undefined: ${UNDEFINED}\n" - ) - - with raises(ValueError) as context: - self.config.option.from_yaml(self.config_file, envs_required=True) - assert str(context.value) == "Missing required environment variable \"UNDEFINED\"" - - def test_option_missing_envs_strict_mode(self): - with open(self.config_file, "w") as config_file: - config_file.write( - "section:\n" - " undefined: ${UNDEFINED}\n" - ) - - self.config.set_strict(True) - with raises(ValueError) as context: - self.config.option.from_yaml(self.config_file) - assert str(context.value) == "Missing required environment variable \"UNDEFINED\"" - - def test_default_values(self): - os.environ["DEFINED"] = "defined" - self.addCleanup(os.environ.pop, "DEFINED") - - with open(self.config_file, "w") as config_file: - config_file.write( - "section:\n" - " defined_with_default: ${DEFINED:default}\n" - " undefined_with_default: ${UNDEFINED:default}\n" - " complex: ${DEFINED}/path/${DEFINED:default}/${UNDEFINED}/${UNDEFINED:default}\n" - ) - - self.config.from_yaml(self.config_file) - - assert self.config.section() == { - "defined_with_default": "defined", - "undefined_with_default": "default", - "complex": "defined/path/defined//default", - } - - def test_option_env_variable_interpolation(self): - self.config.option.from_yaml(self.config_file) - - assert self.config.option() == { - "section1": { - "value1": "test-value", - "value2": "test-path/path", - }, - } - assert self.config.option.section1() == { - "value1": "test-value", - "value2": "test-path/path", - } - assert self.config.option.section1.value1() == "test-value" - assert self.config.option.section1.value2() == "test-path/path" - - def test_env_variable_interpolation_custom_loader(self): - self.config.from_yaml(self.config_file, loader=yaml.UnsafeLoader) - - assert self.config.section1() == { - "value1": "test-value", - "value2": "test-path/path", - } - assert self.config.section1.value1() == "test-value" - assert self.config.section1.value2() == "test-path/path" - - def test_option_env_variable_interpolation_custom_loader(self): - self.config.option.from_yaml(self.config_file, loader=yaml.UnsafeLoader) - - assert self.config.option.section1() == { - "value1": "test-value", - "value2": "test-path/path", - } - assert self.config.option.section1.value1() == "test-value" - assert self.config.option.section1.value2() == "test-path/path" - - -class ConfigFromPydanticTests(unittest.TestCase): - - def setUp(self): - self.config = providers.Configuration(name="config") - - class Section11(pydantic.BaseModel): - value1 = 1 - - class Section12(pydantic.BaseModel): - value2 = 2 - - class Settings1(pydantic.BaseSettings): - section1 = Section11() - section2 = Section12() - - self.Settings1 = Settings1 - - class Section21(pydantic.BaseModel): - value1 = 11 - value11 = 11 - - class Section3(pydantic.BaseModel): - value3 = 3 - - class Settings2(pydantic.BaseSettings): - section1 = Section21() - section3 = Section3() - - self.Settings2 = Settings2 - - @unittest.skipIf(sys.version_info[:2] < (3, 6), "Pydantic supports Python 3.6+") - def test(self): - self.config.from_pydantic(self.Settings1()) - - assert self.config() == {"section1": {"value1": 1}, "section2": {"value2": 2}} - assert self.config.section1() == {"value1": 1} - assert self.config.section1.value1() == 1 - assert self.config.section2() == {"value2": 2} - assert self.config.section2.value2() == 2 - - @unittest.skipIf(sys.version_info[:2] < (3, 6), "Pydantic supports Python 3.6+") - def test_kwarg(self): - self.config.from_pydantic(self.Settings1(), exclude={"section2"}) - - assert self.config() == {"section1": {"value1": 1}} - assert self.config.section1() == {"value1": 1} - assert self.config.section1.value1() == 1 - - @unittest.skipIf(sys.version_info[:2] < (3, 6), "Pydantic supports Python 3.6+") - def test_merge(self): - self.config.from_pydantic(self.Settings1()) - self.config.from_pydantic(self.Settings2()) - - assert self.config() == { - "section1": { - "value1": 11, - "value11": 11, - }, - "section2": { - "value2": 2, - }, - "section3": { - "value3": 3, - }, - } - assert self.config.section1() == {"value1": 11, "value11": 11} - assert self.config.section1.value1() == 11 - assert self.config.section1.value11() == 11 - assert self.config.section2() == {"value2": 2} - assert self.config.section2.value2() == 2 - assert self.config.section3() == {"value3": 3} - assert self.config.section3.value3() == 3 - - @unittest.skipIf(sys.version_info[:2] < (3, 6), "Pydantic supports Python 3.6+") - def test_empty_settings(self): - self.config.from_pydantic(pydantic.BaseSettings()) - assert self.config() == {} - - @unittest.skipIf(sys.version_info[:2] < (3, 6), "Pydantic supports Python 3.6+") - def test_empty_settings_strict_mode(self): - self.config = providers.Configuration(strict=True) - with raises(ValueError): - self.config.from_pydantic(pydantic.BaseSettings()) - - @unittest.skipIf(sys.version_info[:2] < (3, 6), "Pydantic supports Python 3.6+") - def test_option_empty_settings(self): - self.config.option.from_pydantic(pydantic.BaseSettings()) - assert self.config.option() == {} - - @unittest.skipIf(sys.version_info[:2] < (3, 6), "Pydantic supports Python 3.6+") - def test_option_empty_settings_strict_mode(self): - self.config = providers.Configuration(strict=True) - with raises(ValueError): - self.config.option.from_pydantic(pydantic.BaseSettings()) - - @unittest.skipIf(sys.version_info[:2] < (3, 6), "Pydantic supports Python 3.6+") - def test_required_empty_settings(self): - with raises(ValueError): - self.config.from_pydantic(pydantic.BaseSettings(), required=True) - - @unittest.skipIf(sys.version_info[:2] < (3, 6), "Pydantic supports Python 3.6+") - def test_required_option_empty_settings(self): - with raises(ValueError): - self.config.option.from_pydantic(pydantic.BaseSettings(), required=True) - - @unittest.skipIf(sys.version_info[:2] < (3, 6), "Pydantic supports Python 3.6+") - def test_not_required_empty_settings_strict_mode(self): - self.config = providers.Configuration(strict=True) - self.config.from_pydantic(pydantic.BaseSettings(), required=False) - assert self.config() == {} - - @unittest.skipIf(sys.version_info[:2] < (3, 6), "Pydantic supports Python 3.6+") - def test_not_required_option_empty_settings_strict_mode(self): - self.config = providers.Configuration(strict=True) - self.config.option.from_pydantic(pydantic.BaseSettings(), required=False) - assert self.config.option() == {} - assert self.config() == {"option": {}} - - @unittest.skipIf(sys.version_info[:2] < (3, 6), "Pydantic supports Python 3.6+") - def test_not_instance_of_settings(self): - with raises(errors.Error) as error: - self.config.from_pydantic({}) - - assert error.value.args[0] == ( - "Unable to recognize settings instance, expect \"pydantic.BaseSettings\", " - "got {0} instead".format({}) - ) - - @unittest.skipIf(sys.version_info[:2] < (3, 6), "Pydantic supports Python 3.6+") - def test_option_not_instance_of_settings(self): - with raises(errors.Error) as error: - self.config.option.from_pydantic({}) - - assert error.value.args[0] == ( - "Unable to recognize settings instance, expect \"pydantic.BaseSettings\", " - "got {0} instead".format({}) - ) - - @unittest.skipIf(sys.version_info[:2] < (3, 6), "Pydantic supports Python 3.6+") - def test_subclass_instead_of_instance(self): - with raises(errors.Error) as error: - self.config.from_pydantic(self.Settings1) - - assert error.value.args[0] == ( - "Got settings class, but expect instance: " - "instead \"Settings1\" use \"Settings1()\"" - ) - - @unittest.skipIf(sys.version_info[:2] < (3, 6), "Pydantic supports Python 3.6+") - def test_option_subclass_instead_of_instance(self): - with raises(errors.Error) as error: - self.config.option.from_pydantic(self.Settings1) - - assert error.value.args[0] == ( - "Got settings class, but expect instance: " - "instead \"Settings1\" use \"Settings1()\"" - ) - - @unittest.skipIf(sys.version_info[:2] < (3, 6), "Pydantic supports Python 3.6+") - def test_no_pydantic_installed(self): - @contextlib.contextmanager - def no_pydantic_module(): - pydantic = providers.pydantic - providers.pydantic = None - - yield - - providers.pydantic = pydantic - - with no_pydantic_module(): - with raises(errors.Error) as error: - self.config.from_pydantic(self.Settings1()) - - assert error.value.args[0] == ( - "Unable to load pydantic configuration - pydantic is not installed. " - "Install pydantic or install Dependency Injector with pydantic extras: " - "\"pip install dependency-injector[pydantic]\"" - ) - - @unittest.skipIf(sys.version_info[:2] < (3, 6), "Pydantic supports Python 3.6+") - def test_option_no_pydantic_installed(self): - @contextlib.contextmanager - def no_pydantic_module(): - pydantic = providers.pydantic - providers.pydantic = None - - yield - - providers.pydantic = pydantic - - with no_pydantic_module(): - with raises(errors.Error) as error: - self.config.option.from_pydantic(self.Settings1()) - - assert error.value.args[0] == ( - "Unable to load pydantic configuration - pydantic is not installed. " - "Install pydantic or install Dependency Injector with pydantic extras: " - "\"pip install dependency-injector[pydantic]\"" - ) - - -class ConfigFromDict(unittest.TestCase): - - def setUp(self): - self.config = providers.Configuration(name="config") - - self.config_options_1 = { - "section1": { - "value1": "1", - }, - "section2": { - "value2": "2", - }, - } - self.config_options_2 = { - "section1": { - "value1": "11", - "value11": "11", - }, - "section3": { - "value3": "3", - }, - } - - def test(self): - self.config.from_dict(self.config_options_1) - - assert self.config() == {"section1": {"value1": "1"}, "section2": {"value2": "2"}} - assert self.config.section1() == {"value1": "1"} - assert self.config.section1.value1() == "1" - assert self.config.section2() == {"value2": "2"} - assert self.config.section2.value2() == "2" - - def test_merge(self): - self.config.from_dict(self.config_options_1) - self.config.from_dict(self.config_options_2) - - assert self.config() == { - "section1": { - "value1": "11", - "value11": "11", - }, - "section2": { - "value2": "2", - }, - "section3": { - "value3": "3", - }, - } - assert self.config.section1() == {"value1": "11", "value11": "11"} - assert self.config.section1.value1() == "11" - assert self.config.section1.value11() == "11" - assert self.config.section2() == {"value2": "2"} - assert self.config.section2.value2() == "2" - assert self.config.section3() == {"value3": "3"} - assert self.config.section3.value3() == "3" - - def test_empty_dict(self): - self.config.from_dict({}) - assert self.config() == {} - - def test_option_empty_dict(self): - self.config.option.from_dict({}) - assert self.config.option() == {} - - def test_empty_dict_in_strict_mode(self): - self.config = providers.Configuration(strict=True) - with raises(ValueError): - self.config.from_dict({}) - - def test_option_empty_dict_in_strict_mode(self): - self.config = providers.Configuration(strict=True) - with raises(ValueError): - self.config.option.from_dict({}) - - def test_required_empty_dict(self): - with raises(ValueError): - self.config.from_dict({}, required=True) - - def test_required_option_empty_dict(self): - with raises(ValueError): - self.config.option.from_dict({}, required=True) - - def test_not_required_empty_dict_strict_mode(self): - self.config = providers.Configuration(strict=True) - self.config.from_dict({}, required=False) - assert self.config() == {} - - def test_not_required_option_empty_dict_strict_mode(self): - self.config = providers.Configuration(strict=True) - self.config.option.from_dict({}, required=False) - assert self.config.option() == {} - assert self.config() == {"option": {}} - - -class ConfigFromEnvTests(unittest.TestCase): - - def setUp(self): - self.config = providers.Configuration(name="config") - os.environ["CONFIG_TEST_ENV"] = "test-value" - - def tearDown(self): - del self.config - del os.environ["CONFIG_TEST_ENV"] - - def test(self): - self.config.from_env("CONFIG_TEST_ENV") - assert self.config() == "test-value" - - def test_with_children(self): - self.config.section1.value1.from_env("CONFIG_TEST_ENV") - - assert self.config() == {"section1": {"value1": "test-value"}} - assert self.config.section1() == {"value1": "test-value"} - assert self.config.section1.value1() == "test-value" - - def test_default(self): - self.config.from_env("UNDEFINED_ENV", "default-value") - assert self.config() == "default-value" - - def test_default_none(self): - self.config.from_env("UNDEFINED_ENV") - assert self.config() is None - - def test_option_default_none(self): - self.config.option.from_env("UNDEFINED_ENV") - assert self.config.option() is None - - def test_undefined_in_strict_mode(self): - self.config = providers.Configuration(strict=True) - with raises(ValueError): - self.config.from_env("UNDEFINED_ENV") - - def test_option_undefined_in_strict_mode(self): - self.config = providers.Configuration(strict=True) - with raises(ValueError): - self.config.option.from_env("UNDEFINED_ENV") - - def test_undefined_in_strict_mode_with_default(self): - self.config = providers.Configuration(strict=True) - self.config.from_env("UNDEFINED_ENV", "default-value") - assert self.config() == "default-value" - - def test_option_undefined_in_strict_mode_with_default(self): - self.config = providers.Configuration(strict=True) - self.config.option.from_env("UNDEFINED_ENV", "default-value") - assert self.config.option() == "default-value" - - def test_required_undefined(self): - with raises(ValueError): - self.config.from_env("UNDEFINED_ENV", required=True) - - def test_required_undefined_with_default(self): - self.config.from_env("UNDEFINED_ENV", default="default-value", required=True) - assert self.config() == "default-value" - - def test_option_required_undefined(self): - with raises(ValueError): - self.config.option.from_env("UNDEFINED_ENV", required=True) - - def test_option_required_undefined_with_default(self): - self.config.option.from_env("UNDEFINED_ENV", default="default-value", required=True) - assert self.config.option() == "default-value" - - def test_not_required_undefined_in_strict_mode(self): - self.config = providers.Configuration(strict=True) - self.config.from_env("UNDEFINED_ENV", required=False) - assert self.config() is None - - def test_option_not_required_undefined_in_strict_mode(self): - self.config = providers.Configuration(strict=True) - self.config.option.from_env("UNDEFINED_ENV", required=False) - assert self.config.option() is None - - def test_not_required_undefined_with_default_in_strict_mode(self): - self.config = providers.Configuration(strict=True) - self.config.from_env("UNDEFINED_ENV", default="default-value", required=False) - assert self.config() == "default-value" - - def test_option_not_required_undefined_with_default_in_strict_mode(self): - self.config = providers.Configuration(strict=True) - self.config.option.from_env("UNDEFINED_ENV", default="default-value", required=False) - assert self.config.option() == "default-value" - - -class ConfigFromValueTests(unittest.TestCase): - - def setUp(self): - self.config = providers.Configuration(name="config") - - def test_from_value(self): - test_value = 123321 - self.config.from_value(test_value) - assert self.config() == test_value - - def test_option_from_value(self): - test_value_1 = 123 - test_value_2 = 321 - - self.config.option1.from_value(test_value_1) - self.config.option2.from_value(test_value_2) - - assert self.config() == {"option1": test_value_1, "option2": test_value_2} - assert self.config.option1() == test_value_1 - assert self.config.option2() == test_value_2