Skip to content

Add async resolver for AWS config values - #751

Merged
ubaskota merged 5 commits into
smithy-lang:developfrom
ubaskota:config_resolver_implementation
Jul 28, 2026
Merged

Add async resolver for AWS config values#751
ubaskota merged 5 commits into
smithy-lang:developfrom
ubaskota:config_resolver_implementation

Conversation

@ubaskota

@ubaskota ubaskota commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Issue #, if available:

Description of changes:
This change implements the async config resolution pipeline with AsyncAwsConfig using a resolve() classmethod as the only construction path. It adds SharedConfigContext for holding memoized config file data, FieldSpec for per-field resolution metadata, and provenance tracking via ConfigSource. Resolvers for region and retry_strategy_options are included along with validators for both fields.

Testing:

  • Added unit and end-to-end tests covering env var resolution, profile resolution, explicit overrides, defaults, provenance tracking, FileSystem, validator enforcement, and construction blocking, and verified that they all pass.
  • All existing tests pass

Note: This is a revision to the old PR (#738) that was automatically closed after the feature branch was merged.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Key design points:

  • AsyncAwsConfig cannot be instantiated directly. Calling AsyncAwsConfig() raises a ConfigError. All construction must go through await AsyncAwsConfig.resolve(), which ensures every field is resolved and validated before the object is returned.
  • All config fields are resolved upfront in one call. There is no lazy resolution.
  • Resolution follows a strict precedence: explicit overrides > environment variables > config file profile > default value.
  • Every field tracks where its value came from (provenance), queryable via config.source_of("field_name").

Usage

Direct instantiation is blocked:

from smithy_aws_core.config import AsyncAwsConfig

config = AsyncAwsConfig()
# raises ConfigError: "AsyncAwsConfig cannot be constructed directly.
#   Use `await AsyncAwsConfig.resolve(...)` instead."

Resolve from environment variables:

If AWS_REGION=us-west-2 is set in the environment:

config = await AsyncAwsConfig.resolve()
print(config.region)  # "us-west-2"

Resolve from config file:

If ~/.aws/config contains:

[profile default]
region = us-east-1
retry_mode = standard
max_attempts = 10

And no environment variables override these:

config = await AsyncAwsConfig.resolve()
print(config.region)        # "us-east-1"
print(config.retry_mode)    # "standard"
print(config.max_attempts)  # 10

Override specific values:

Overrides passed to resolve() take highest precedence — the value is validated but skips the env/file resolution chain entirely:

config = await AsyncAwsConfig.resolve(region="eu-west-1", max_attempts=5)
print(config.region)  # "eu-west-1" (not from env or file)

Use a named profile:

config = await AsyncAwsConfig.resolve(profile="some_name")

If the profile doesn't exist:

config = await AsyncAwsConfig.resolve(profile="nonexistent")
# raises ProfileNotFoundError: "Profile 'nonexistent' (from the profile argument)
#   not found in config file."

Provenance tracking:

config = await AsyncAwsConfig.resolve()
config.source_of("region")        # ConfigSource.ENV
config.source_of("retry_mode")    # ConfigSource.DEFAULT
config.source_of("max_attempts")  # ConfigSource.PROFILE

Override values after resolution:

Values can be changed after resolution using plugins. The source updates to OVERRIDE and the new value is validated:

config = await AsyncAwsConfig.resolve()
config.region = "ap-southeast-1"
config.source_of("region")  # ConfigSource.OVERRIDE

config.region = "some-value!"  # raises ConfigValidationError

ubaskota added 2 commits July 23, 2026 18:28
# Conflicts:
#	packages/smithy-aws-core/src/smithy_aws_core/config/exceptions.py
@ubaskota
ubaskota requested a review from a team as a code owner July 23, 2026 23:38
Comment thread packages/smithy-aws-core/src/smithy_aws_core/config/validators.py
Comment thread packages/smithy-aws-core/src/smithy_aws_core/config/validators.py Outdated
Comment thread packages/smithy-aws-core/src/smithy_aws_core/config/validators.py
Comment thread packages/smithy-aws-core/src/smithy_aws_core/config/aws_config.py
Comment thread packages/smithy-aws-core/src/smithy_aws_core/config/aws_config.py Outdated
Comment thread packages/smithy-core/src/smithy_core/exceptions.py Outdated
Comment thread packages/smithy-aws-core/src/smithy_aws_core/config/aws_config.py
Comment thread packages/smithy-aws-core/src/smithy_aws_core/config/aws_config.py
Comment thread packages/smithy-aws-core/src/smithy_aws_core/config/aws_config.py
Comment thread packages/smithy-aws-core/src/smithy_aws_core/config/resolvers.py Outdated

@jonathan343 jonathan343 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks Ujjwal.

There are some UX things we'll want to clean up. But this is a good start and we have other work that depends on this, so I'm approving.

@ubaskota
ubaskota merged commit b99a055 into smithy-lang:develop Jul 28, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants