diff --git a/codegen/core/src/main/java/software/amazon/smithy/python/codegen/generators/ConfigGenerator.java b/codegen/core/src/main/java/software/amazon/smithy/python/codegen/generators/ConfigGenerator.java index e073ead8f..92aa857e5 100644 --- a/codegen/core/src/main/java/software/amazon/smithy/python/codegen/generators/ConfigGenerator.java +++ b/codegen/core/src/main/java/software/amazon/smithy/python/codegen/generators/ConfigGenerator.java @@ -343,9 +343,9 @@ private void generateConfig(GenerationContext context, PythonWriter writer) { // Only add config resolution imports if there are descriptor properties if (hasDescriptors) { - writer.addDependency(SmithyPythonDependency.SMITHY_CORE); - writer.addImport("smithy_core.config.property", "ConfigProperty"); - writer.addImport("smithy_core.config.resolver", "ConfigResolver"); + writer.addDependency(SmithyPythonDependency.SMITHY_AWS_CORE); + writer.addImport("smithy_aws_core.config.property", "ConfigProperty"); + writer.addImport("smithy_aws_core.config.resolver", "ConfigResolver"); writer.addImport("smithy_aws_core.config.sources", "EnvironmentSource"); // Add validator and resolver imports for properties that use descriptors @@ -580,17 +580,19 @@ public void write(PythonWriter writer, String previousText, ConfigSection sectio } writer.write(previousText); + writer.addImport("smithy_aws_core.config.source_info", "SourceInfo"); writer.write(""" - def get_source(self, key: str) -> str | None: + def get_source(self, key: str) -> SourceInfo | None: \"""Get the source that provided a configuration value. Args: key: The configuration key (e.g., 'region', 'retry_strategy') Returns: - The source name ('instance', 'environment', etc.), + The source info (SimpleSource('source_name') or + ComplexSource({"retry_mode": "source1", "max_attempts": "source2"})), or None if the key hasn't been resolved yet. \""" cached = self.__dict__.get(f'_cache_{key}') diff --git a/packages/smithy-aws-core/src/smithy_aws_core/config/custom_resolvers.py b/packages/smithy-aws-core/src/smithy_aws_core/config/custom_resolvers.py index 3aa87cc15..5aa7de408 100644 --- a/packages/smithy-aws-core/src/smithy_aws_core/config/custom_resolvers.py +++ b/packages/smithy-aws-core/src/smithy_aws_core/config/custom_resolvers.py @@ -1,15 +1,16 @@ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 -from smithy_core.config.resolver import ConfigResolver from smithy_core.retries import RetryStrategyOptions +from smithy_aws_core.config.resolver import ConfigResolver +from smithy_aws_core.config.source_info import ComplexSource, SourceName from smithy_aws_core.config.validators import validate_max_attempts, validate_retry_mode def resolve_retry_strategy( resolver: ConfigResolver, -) -> tuple[RetryStrategyOptions | None, str | None]: +) -> tuple[RetryStrategyOptions | None, ComplexSource | None]: """Resolve retry strategy from multiple config keys. Resolves both retry_mode and max_attempts from sources and constructs @@ -23,7 +24,7 @@ def resolve_retry_strategy( are resolved. Returns (None, None) if both values are missing. For mixed sources, the source name includes both component sources: - "retry_mode=environment, max_attempts=config_file" + {"retry_mode": "environment", "max_attempts": "default"} """ retry_mode, mode_source = resolver.get("retry_mode") @@ -45,6 +46,13 @@ def resolve_retry_strategy( ) # Construct mixed source string showing where each component came from - source = f"retry_mode={mode_source or 'default'}, max_attempts={attempts_source or 'default'}" + source = ComplexSource( + { + "retry_mode": mode_source.name if mode_source else SourceName.DEFAULT, + "max_attempts": attempts_source.name + if attempts_source + else SourceName.DEFAULT, + } + ) return (options, source) diff --git a/packages/smithy-core/src/smithy_core/config/property.py b/packages/smithy-aws-core/src/smithy_aws_core/config/property.py similarity index 87% rename from packages/smithy-core/src/smithy_core/config/property.py rename to packages/smithy-aws-core/src/smithy_aws_core/config/property.py index 71a3f413c..56e136981 100644 --- a/packages/smithy-core/src/smithy_core/config/property.py +++ b/packages/smithy-aws-core/src/smithy_aws_core/config/property.py @@ -3,7 +3,8 @@ from collections.abc import Callable from typing import Any -from smithy_core.config.resolver import ConfigResolver +from smithy_aws_core.config.resolver import ConfigResolver +from smithy_aws_core.config.source_info import SimpleSource, SourceInfo, SourceName class ConfigProperty: @@ -27,8 +28,9 @@ def __init__(self): def __init__( self, key: str, - validator: Callable[[Any, str | None], Any] | None = None, - resolver_func: Callable[[ConfigResolver], tuple[Any, str | None]] | None = None, + validator: Callable[[Any, SourceInfo | None], Any] | None = None, + resolver_func: Callable[[ConfigResolver], tuple[Any, SourceInfo | None]] + | None = None, default_value: Any = None, ): """Initialize config property descriptor. @@ -78,7 +80,7 @@ def __get__(self, obj: Any, objtype: type | None = None) -> Any: if value is None: value = self.default_value - source = "default" + source = SimpleSource(SourceName.DEFAULT) if self.validator: value = self.validator(value, source) @@ -99,7 +101,11 @@ def __set__(self, obj: Any, value: Any) -> None: # Determine source based on when the value was set # If cache already exists, it means it was not set during initialization # In that case source will be set to in-code - source = "in-code" if hasattr(obj, self.cache_attr) else "instance" + source = ( + SimpleSource(SourceName.IN_CODE) + if hasattr(obj, self.cache_attr) + else SimpleSource(SourceName.INSTANCE) + ) if self.validator: value = self.validator(value, source) diff --git a/packages/smithy-core/src/smithy_core/config/resolver.py b/packages/smithy-aws-core/src/smithy_aws_core/config/resolver.py similarity index 86% rename from packages/smithy-core/src/smithy_core/config/resolver.py rename to packages/smithy-aws-core/src/smithy_aws_core/config/resolver.py index 7a82ca2b6..a17a36b93 100644 --- a/packages/smithy-core/src/smithy_core/config/resolver.py +++ b/packages/smithy-aws-core/src/smithy_aws_core/config/resolver.py @@ -5,6 +5,8 @@ from smithy_core.interfaces.config import ConfigSource +from smithy_aws_core.config.source_info import SimpleSource + class ConfigResolver: """Resolves configuration values from multiple sources. @@ -21,7 +23,7 @@ def __init__(self, sources: Sequence[ConfigSource]) -> None: """ self._sources = sources - def get(self, key: str) -> tuple[Any, str | None]: + def get(self, key: str) -> tuple[Any, SimpleSource | None]: """Resolve a configuration value from sources by iterating through them in precedence order. :param key: The configuration key to resolve (e.g., 'retry_mode') @@ -32,5 +34,5 @@ def get(self, key: str) -> tuple[Any, str | None]: for source in self._sources: value = source.get(key) if value is not None: - return (value, source.name) + return (value, SimpleSource(source.name)) return (None, None) diff --git a/packages/smithy-aws-core/src/smithy_aws_core/config/source_info.py b/packages/smithy-aws-core/src/smithy_aws_core/config/source_info.py new file mode 100644 index 000000000..64e5d0cc2 --- /dev/null +++ b/packages/smithy-aws-core/src/smithy_aws_core/config/source_info.py @@ -0,0 +1,48 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +from dataclasses import dataclass +from enum import StrEnum + + +class SourceName(StrEnum): + """Known source names for config value provenance tracking.""" + + INSTANCE = "instance" # value provided via Config constructor + + IN_CODE = "in-code" # value set via setter after Config construction + + ENVIRONMENT = "environment" # value resolved from environment variable + + DEFAULT = "default" # value fall back to default + + +@dataclass(frozen=True) +class SimpleSource: + """Source info for a config value resolved from a single source. + + Examples: region from environment, max_attempts from config file. + """ + + # TODO: Currently only environment variable is implemented as a config + # source. Tests use raw strings (e.g., "environment", "config_file") as + # source names to simulate multi-source scenarios. Once additional + # config sources are implemented, update the `name` parameter type + # from `str` to `SourceName` and replace raw strings in tests with + # the corresponding enum values. + name: str + + +@dataclass(frozen=True) +class ComplexSource: + """Source info for a config value resolved from multiple sources. + + Used when a config property is composed of multiple sources. + Example: retry_strategy is composed of retry_mode and max_attempts and they both + could be from different sources: {"retry_mode": "environment", "max_attempts": "config_file"} + """ + + components: dict[str, str] + + +SourceInfo = SimpleSource | ComplexSource diff --git a/packages/smithy-aws-core/src/smithy_aws_core/config/sources.py b/packages/smithy-aws-core/src/smithy_aws_core/config/sources.py index f5e5195de..26c5f3550 100644 --- a/packages/smithy-aws-core/src/smithy_aws_core/config/sources.py +++ b/packages/smithy-aws-core/src/smithy_aws_core/config/sources.py @@ -2,6 +2,8 @@ # SPDX-License-Identifier: Apache-2.0 import os +from smithy_aws_core.config.source_info import SourceName + class EnvironmentSource: """Configuration from environment variables.""" @@ -16,7 +18,7 @@ def __init__(self, prefix: str = "AWS_"): @property def name(self) -> str: """Returns the source name.""" - return "environment" + return SourceName.ENVIRONMENT def get(self, key: str) -> str | None: """Returns a configuration value from environment variables. diff --git a/packages/smithy-aws-core/src/smithy_aws_core/config/validators.py b/packages/smithy-aws-core/src/smithy_aws_core/config/validators.py index eef62275b..41b670e02 100644 --- a/packages/smithy-aws-core/src/smithy_aws_core/config/validators.py +++ b/packages/smithy-aws-core/src/smithy_aws_core/config/validators.py @@ -7,11 +7,15 @@ from smithy_core.interfaces.retries import RetryStrategy from smithy_core.retries import RetryStrategyOptions, RetryStrategyType +from smithy_aws_core.config.source_info import SourceInfo + class ConfigValidationError(ValueError): """Raised when a configuration value fails validation.""" - def __init__(self, key: str, value: Any, reason: str, source: str | None = None): + def __init__( + self, key: str, value: Any, reason: str, source: SourceInfo | None = None + ): self.key = key self.value = value self.reason = reason @@ -23,7 +27,7 @@ def __init__(self, key: str, value: Any, reason: str, source: str | None = None) super().__init__(msg) -def validate_region(region: str | None, source: str | None = None) -> str: +def validate_region(region: str | None, source: SourceInfo | None = None) -> str: """Validate region name format. :param region: The value to validate @@ -53,7 +57,7 @@ def validate_region(region: str | None, source: str | None = None) -> str: return region -def validate_retry_mode(retry_mode: str, source: str | None = None) -> str: +def validate_retry_mode(retry_mode: str, source: SourceInfo | None = None) -> str: """Validate retry mode. Valid values: 'standard' @@ -85,7 +89,9 @@ def validate_retry_mode(retry_mode: str, source: str | None = None) -> str: return retry_mode -def validate_max_attempts(max_attempts: str | int, source: str | None = None) -> int: +def validate_max_attempts( + max_attempts: str | int, source: SourceInfo | None = None +) -> int: """Validate and convert max_attempts to integer. :param max_attempts: The max attempts value (string or int) @@ -117,7 +123,7 @@ def validate_max_attempts(max_attempts: str | int, source: str | None = None) -> def validate_retry_strategy( - value: Any, source: str | None = None + value: Any, source: SourceInfo | None = None ) -> RetryStrategy | RetryStrategyOptions: """Validate retry strategy configuration. diff --git a/packages/smithy-aws-core/tests/unit/config/test_custom_resolver.py b/packages/smithy-aws-core/tests/unit/config/test_custom_resolver.py index 623614649..37657598a 100644 --- a/packages/smithy-aws-core/tests/unit/config/test_custom_resolver.py +++ b/packages/smithy-aws-core/tests/unit/config/test_custom_resolver.py @@ -4,7 +4,8 @@ from typing import Any from smithy_aws_core.config.custom_resolvers import resolve_retry_strategy -from smithy_core.config.resolver import ConfigResolver +from smithy_aws_core.config.resolver import ConfigResolver +from smithy_aws_core.config.source_info import ComplexSource from smithy_core.retries import RetryStrategyOptions @@ -39,7 +40,9 @@ def test_resolves_from_both_values(self) -> None: assert isinstance(result, RetryStrategyOptions) assert result.retry_mode == "standard" assert result.max_attempts == 3 - assert source_name == "retry_mode=environment, max_attempts=environment" + assert source_name == ComplexSource( + {"retry_mode": "environment", "max_attempts": "environment"} + ) def test_tracks_different_sources_for_each_component(self) -> None: source1 = StubSource("environment", {"retry_mode": "standard"}) @@ -51,7 +54,9 @@ def test_tracks_different_sources_for_each_component(self) -> None: assert isinstance(result, RetryStrategyOptions) assert result.retry_mode == "standard" assert result.max_attempts == 5 - assert source_name == "retry_mode=environment, max_attempts=config_file" + assert source_name == ComplexSource( + {"retry_mode": "environment", "max_attempts": "config_file"} + ) def test_converts_max_attempts_string_to_int(self) -> None: source = StubSource( @@ -76,7 +81,9 @@ def test_returns_strategy_when_only_retry_mode_set(self) -> None: # None for max_attempts means the RetryStrategy will use its # own default max_attempts value for the set retry_mode assert result.max_attempts is None - assert source_name == "retry_mode=environment, max_attempts=default" + assert source_name == ComplexSource( + {"retry_mode": "environment", "max_attempts": "default"} + ) def test_returns_strategy_when_only_max_attempts_set(self) -> None: source = StubSource("environment", {"max_attempts": "5"}) @@ -87,7 +94,9 @@ def test_returns_strategy_when_only_max_attempts_set(self) -> None: assert isinstance(result, RetryStrategyOptions) assert result.max_attempts == 5 assert result.retry_mode == "standard" - assert source_name == "retry_mode=default, max_attempts=environment" + assert source_name == ComplexSource( + {"retry_mode": "default", "max_attempts": "environment"} + ) def test_returns_none_when_both_values_missing(self) -> None: source = StubSource("environment", {}) diff --git a/packages/smithy-core/tests/unit/config/test_property.py b/packages/smithy-aws-core/tests/unit/config/test_property.py similarity index 69% rename from packages/smithy-core/tests/unit/config/test_property.py rename to packages/smithy-aws-core/tests/unit/config/test_property.py index 88ae990a6..42ea31291 100644 --- a/packages/smithy-core/tests/unit/config/test_property.py +++ b/packages/smithy-aws-core/tests/unit/config/test_property.py @@ -5,8 +5,10 @@ from typing import Any, NoReturn import pytest -from smithy_core.config.property import ConfigProperty -from smithy_core.config.resolver import ConfigResolver +from smithy_aws_core.config.property import ConfigProperty +from smithy_aws_core.config.resolver import ConfigResolver +from smithy_aws_core.config.source_info import SimpleSource, SourceInfo +from smithy_core.retries import RetryStrategyOptions class StubSource: @@ -60,7 +62,10 @@ def test_caches_resolved_value(self) -> None: def test_uses_default_value_when_unresolved(self) -> None: class ConfigWithDefault: - region = ConfigProperty("region", default_value="us-east-1") + retry_strategy = ConfigProperty( + "retry_strategy", + default_value=RetryStrategyOptions(retry_mode="standard"), + ) def __init__(self, resolver: ConfigResolver) -> None: self._resolver = resolver @@ -69,10 +74,14 @@ def __init__(self, resolver: ConfigResolver) -> None: resolver = ConfigResolver(sources=[source]) config = ConfigWithDefault(resolver) - result = config.region + result = config.retry_strategy - assert result == "us-east-1" - assert getattr(config, "_cache_region") == ("us-east-1", "default") + assert result.retry_mode == "standard" + assert result.max_attempts is None + assert getattr(config, "_cache_retry_strategy") == ( + RetryStrategyOptions(retry_mode="standard"), + SimpleSource("default"), + ) def test_different_properties_resolve_independently(self) -> None: source = StubSource( @@ -92,12 +101,13 @@ class TestConfigPropertyValidation: """Test suite for ConfigProperty validation behavior.""" def _create_config_with_validator( - self, validator: Callable[[Any, str | None], Any] + self, validator: Callable[[Any, SourceInfo | None], Any] ) -> type[Any]: """Helper to create a config class with a specific validator.""" class ConfigWithValidator: region = ConfigProperty("region", validator=validator) + retry_strategy = ConfigProperty("retry_strategy", validator=validator) def __init__(self, resolver: ConfigResolver) -> None: self._resolver = resolver @@ -105,9 +115,9 @@ def __init__(self, resolver: ConfigResolver) -> None: return ConfigWithValidator def test_calls_validator_on_resolution(self) -> None: - call_log: list[tuple[Any, str | None]] = [] + call_log: list[tuple[Any, SourceInfo | None]] = [] - def mock_validator(value: Any, source: str | None) -> Any: + def mock_validator(value: Any, source: SourceInfo | None) -> Any: call_log.append((value, source)) return value @@ -120,10 +130,10 @@ def mock_validator(value: Any, source: str | None) -> Any: assert result == "us-west-2" assert len(call_log) == 1 - assert call_log[0] == ("us-west-2", "environment") + assert call_log[0] == ("us-west-2", SimpleSource("environment")) def test_validator_exception_propagates(self) -> None: - def failing_validator(value: Any, source: str | None) -> NoReturn: + def failing_validator(value: Any, source: SourceInfo | None) -> NoReturn: raise ValueError("Invalid value") ConfigWithValidator = self._create_config_with_validator(failing_validator) @@ -134,10 +144,40 @@ def failing_validator(value: Any, source: str | None) -> NoReturn: with pytest.raises(ValueError, match="Invalid value"): config.region + def test_complex_resolver_falls_back_to_default(self) -> None: + def mock_resolver(resolver: ConfigResolver) -> tuple[None, None]: + # Simulates resolve_retry_strategy returning (None, None) when no sources have values + return (None, None) + + class ConfigWithComplexResolver: + retry_strategy = ConfigProperty( + "retry_strategy", + resolver_func=mock_resolver, + default_value=RetryStrategyOptions( + retry_mode="standard", max_attempts=3 + ), + ) + + def __init__(self, resolver: ConfigResolver) -> None: + self._resolver = resolver + + source = StubSource("environment", {}) + resolver = ConfigResolver(sources=[source]) + config = ConfigWithComplexResolver(resolver) + + result = config.retry_strategy + cached = getattr(config, "_cache_retry_strategy", None) + source_info = cached[1] if cached else None + + assert isinstance(result, RetryStrategyOptions) + assert result.retry_mode == "standard" + assert result.max_attempts == 3 + assert source_info == SimpleSource("default") + def test_validator_not_called_on_cached_access(self) -> None: call_count = 0 - def counting_validator(value: Any, source: str | None) -> Any: + def counting_validator(value: Any, source: SourceInfo | None) -> Any: nonlocal call_count call_count += 1 return value @@ -167,7 +207,10 @@ def test_set_value_marks_source_as_instance(self) -> None: config.region = "eu-west-1" # Check the cached tuple - assert getattr(config, "_cache_region") == ("eu-west-1", "instance") + assert getattr(config, "_cache_region") == ( + "eu-west-1", + SimpleSource("instance"), + ) def test_value_set_after_resolution_marks_source_as_in_code(self) -> None: source = StubSource("environment", {"region": "us-west-2"}) @@ -184,12 +227,15 @@ def test_value_set_after_resolution_marks_source_as_in_code(self) -> None: assert config.region == "eu-west-1" # Verify source is marked as 'in-code' # Any config value modified after initialization will have 'in-code' for source - assert getattr(config, "_cache_region") == ("eu-west-1", "in-code") + assert getattr(config, "_cache_region") == ( + "eu-west-1", + SimpleSource("in-code"), + ) def test_validator_is_called_when_setting_values(self) -> None: - call_log: list[tuple[Any, str | None]] = [] + call_log: list[tuple[Any, SourceInfo | None]] = [] - def mock_validator(value: Any, source: str | None) -> Any: + def mock_validator(value: Any, source: SourceInfo | None) -> Any: call_log.append((value, source)) return value @@ -207,10 +253,10 @@ def __init__(self, resolver: ConfigResolver) -> None: assert config.region == "us-west-2" assert len(call_log) == 1 - assert call_log[0] == ("us-west-2", "instance") + assert call_log[0] == ("us-west-2", SimpleSource("instance")) def test_validator_throws_exception_when_setting_invalid_value(self) -> None: - def mock_failing_validation(value: Any, source: str | None) -> NoReturn: + def mock_failing_validation(value: Any, source: SourceInfo | None) -> NoReturn: raise ValueError("Invalid value") class ConfigWithValidator: @@ -251,18 +297,20 @@ def test_cache_stores_value_and_source_as_tuple(self) -> None: config.region cached: Any = getattr(config, "_cache_region") - assert cached == ("us-west-2", "environment") + assert cached == ("us-west-2", SimpleSource("environment")) def test_validator_called_on_default_value(self) -> None: - call_log: list[tuple[Any, str | None]] = [] + call_log: list[tuple[Any, SourceInfo | None]] = [] - def mock_validator(value: Any, source: str | None) -> Any: + def mock_validator(value: Any, source: SourceInfo | None) -> Any: call_log.append((value, source)) return value class ConfigWithDefault: - region = ConfigProperty( - "region", default_value="us-default-1", validator=mock_validator + retry_strategy = ConfigProperty( + "retry_strategy", + default_value=RetryStrategyOptions(retry_mode="standard"), + validator=mock_validator, ) def __init__(self, resolver: ConfigResolver) -> None: @@ -272,6 +320,8 @@ def __init__(self, resolver: ConfigResolver) -> None: resolver = ConfigResolver(sources=[source]) config = ConfigWithDefault(resolver) - config.region + config.retry_strategy - assert call_log == [("us-default-1", "default")] + assert call_log == [ + (RetryStrategyOptions(retry_mode="standard"), SimpleSource("default")) + ] diff --git a/packages/smithy-core/tests/unit/config/test_resolver.py b/packages/smithy-aws-core/tests/unit/config/test_resolver.py similarity index 79% rename from packages/smithy-core/tests/unit/config/test_resolver.py rename to packages/smithy-aws-core/tests/unit/config/test_resolver.py index bf016e1d2..55180b1b4 100644 --- a/packages/smithy-core/tests/unit/config/test_resolver.py +++ b/packages/smithy-aws-core/tests/unit/config/test_resolver.py @@ -2,7 +2,8 @@ # SPDX-License-Identifier: Apache-2.0 from typing import Any -from smithy_core.config.resolver import ConfigResolver +from smithy_aws_core.config.resolver import ConfigResolver +from smithy_aws_core.config.source_info import SimpleSource class StubSource: @@ -31,7 +32,7 @@ def test_returns_value_from_single_source(self): result = resolver.get("region") - assert result == ("us-west-2", "environment") + assert result == ("us-west-2", SimpleSource("environment")) def test_returns_None_when_source_has_no_value(self): source = StubSource("environment", {}) @@ -57,7 +58,7 @@ def test_first_source_takes_precedence(self): result = resolver.get("region") - assert result == ("us-east-1", "source_one") + assert result == ("us-east-1", SimpleSource("source_one")) def test_skips_source_returning_none_and_uses_next(self): empty_source = StubSource("source_one", {}) @@ -66,7 +67,7 @@ def test_skips_source_returning_none_and_uses_next(self): result = resolver.get("region") - assert result == ("ap-south-1", "source_two") + assert result == ("ap-south-1", SimpleSource("source_two")) def test_resolves_different_keys_from_different_sources(self): instance = StubSource("source_one", {"region": "us-west-2"}) @@ -76,8 +77,8 @@ def test_resolves_different_keys_from_different_sources(self): region = resolver.get("region") retry_mode = resolver.get("retry_mode") - assert region == ("us-west-2", "source_one") - assert retry_mode == ("adaptive", "source_two") + assert region == ("us-west-2", SimpleSource("source_one")) + assert retry_mode == ("adaptive", SimpleSource("source_two")) def test_returns_non_string_values(self): source = StubSource( @@ -89,8 +90,8 @@ def test_returns_non_string_values(self): ) resolver = ConfigResolver(sources=[source]) - assert resolver.get("max_retries") == (3, "default") - assert resolver.get("use_ssl") == (True, "default") + assert resolver.get("max_retries") == (3, SimpleSource("default")) + assert resolver.get("use_ssl") == (True, SimpleSource("default")) def test_get_is_idempotent(self): source = StubSource("environment", {"region": "us-west-2"}) @@ -100,7 +101,9 @@ def test_get_is_idempotent(self): result2 = resolver.get("region") result3 = resolver.get("region") - assert result1 == result2 == result3 == ("us-west-2", "environment") + assert ( + result1 == result2 == result3 == ("us-west-2", SimpleSource("environment")) + ) def test_treats_empty_string_as_valid_value(self): source = StubSource("test", {"region": ""}) @@ -109,4 +112,4 @@ def test_treats_empty_string_as_valid_value(self): value, source_name = resolver.get("region") assert value == "" - assert source_name == "test" + assert source_name == SimpleSource("test") diff --git a/packages/smithy-core/src/smithy_core/config/__init__.py b/packages/smithy-core/src/smithy_core/config/__init__.py deleted file mode 100644 index 2a8cc831d..000000000 --- a/packages/smithy-core/src/smithy_core/config/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0 - -from .resolver import ConfigResolver - -__all__ = [ - "ConfigResolver", -] diff --git a/packages/smithy-core/tests/unit/config/__init__.py b/packages/smithy-core/tests/unit/config/__init__.py deleted file mode 100644 index 04f8b7b76..000000000 --- a/packages/smithy-core/tests/unit/config/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0