Skip to content

Add config file support for config resolution#729

Open
ubaskota wants to merge 4 commits into
smithy-lang:config_integration_implementationfrom
ubaskota:config_file_support
Open

Add config file support for config resolution#729
ubaskota wants to merge 4 commits into
smithy-lang:config_integration_implementationfrom
ubaskota:config_file_support

Conversation

@ubaskota

Copy link
Copy Markdown
Contributor

Issue #, if available:

Description of changes:
Add support for parsing and merging AWS config and credentials files (~/.aws/config and ~/.aws/credentials) as part of the config resolution pipeline.

Changes:

  • Custom line-by-line parser that handles inline comments, sub-properties, and property continuations
  • Standardizer that normalizes section prefixes, handles [default] vs [profile default] precedence, and separates
    profiles/sso-sessions/services
  • ParsedConfigFile class for querying the merged result
  • Async config loading with path resolution from environment variables and defaults
  • Added support to run conformance tests (config-file-parser-tests.json and config-file-location-tests.json)+ unit tests for the query layer and file I/O edge cases

Testing:

  • Verified that the newly added and existing tests pass
  • Manually tested against a real ~/.aws/config file to verify end-to-end parsing behavior

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

@ubaskota
ubaskota requested a review from a team as a code owner June 24, 2026 18:56
Comment thread packages/smithy-aws-core/src/smithy_aws_core/config/__init__.py Outdated
Comment thread packages/smithy-aws-core/src/smithy_aws_core/config/__init__.py Outdated
Comment thread packages/smithy-aws-core/src/smithy_aws_core/config/file_parser.py Outdated
Comment thread packages/smithy-aws-core/src/smithy_aws_core/config/__init__.py
Comment thread packages/smithy-aws-core/src/smithy_aws_core/config/file_parser.py Outdated
Comment thread packages/smithy-aws-core/src/smithy_aws_core/config/file_parser.py
Comment thread packages/smithy-aws-core/src/smithy_aws_core/config/file_parser.py Outdated
Comment thread packages/smithy-aws-core/src/smithy_aws_core/config/file_parser.py Outdated
@ubaskota
ubaskota requested a review from a team as a code owner July 10, 2026 02:08
Comment thread packages/smithy-aws-core/src/smithy_aws_core/config/file_parser.py
Comment thread packages/smithy-aws-core/src/smithy_aws_core/config/file_parser.py Outdated
Comment thread packages/smithy-aws-core/tests/unit/config/test_merged_config.py
Comment thread packages/smithy-aws-core/tests/unit/config/test_config_file_io.py Outdated

@SamRemis SamRemis 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.

My bad, clicked approve by mistake. Only the test comments are blocking

@ubaskota
ubaskota force-pushed the config_file_support branch from cb70998 to 27cbdd5 Compare July 11, 2026 21:04
@ubaskota
ubaskota changed the base branch from develop to config_integration_implementation July 11, 2026 21:20
Comment thread packages/smithy-aws-core/tests/unit/config/test_merged_config.py

@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.

Thanks Ujjwal. Commenting on a few edge cases that seem like bugs to me. Still getting through others, but this is what I have so far.

sub_properties={k: dict(v) for k, v in profile.sub_properties.items()},
)

for name, profile in credentials_profiles.items():

@jonathan343 jonathan343 Jul 17, 2026

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.

Could we represent each section with a single standardized mapping? Storing scalar and grouped values separately allows the same property to hold conflicting values after merging.

For example:

# ~/.aws/config
[profile p]
x = config
# ~/.aws/credentials
[p]
x =
  nested = credentials

Running this through the current implementation produces:

Section(
    properties={"x": "config"},
    sub_properties={"x": {"nested": "credentials"}},
)

The credentials value should replace the config value entirely. Botocore’s final merged profile is:

{'x': {'nested': 'credentials'}}

Credentials should replace the complete x property, so get("p", "x") should return None. Botocore produces {"x": {"nested": "credentials"}} for this case. A single mapping such as dict[str, str | dict[str, str]] would prevent this conflicting state and make whole-property precedence straightforward.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yup, addressed.

Comment on lines +328 to +333
if stripped.startswith("profile"):
return
if stripped.startswith("sso-session"):
return
if stripped.startswith("services"):
return

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.

These checks incorrectly discard valid credentials profiles such as:

[services-prod]
aws_access_key_id = key

Could we remove the three startswith() checks and rely directly on _is_valid_identifier() before merging the profile? This is simpler, matches botocore, and avoids dropping valid names that merely begin with a reserved term.

"""Config related exceptions"""


class ConfigParseError(Exception):

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.

This is a general error comment, not specific to this file


Parse errors should identify which source file failed. load_config() parses both the config and credentials files, but the current exception only includes the line number.

For example, using an unclosed section header in either file:

[profile p

produces the same error in both cases:

ConfigParseError: Line 1: Section definition must end with ']'

Botocore distinguishes the files:

ConfigParseError: Unable to parse config file: /.../config
ConfigParseError: Unable to parse config file: /.../credentials

Could parse_config_file() add file_path when re-raising errors from _parse_content()? This would preserve the current line-specific detail while identifying the file:

Unable to parse config file '/.../credentials':
Line 1: Section definition must end with ']'

return inner


def _parse_property_line(line: str, line_num: int) -> tuple[str | None, str]:

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.

Ignoring an invalid property currently allows its continuation lines to modify the previous valid property.

For example:

[profile p]
region = us-east-1
invalid key = ignored
  continuation

produces:

Section(
    properties={"region": "us-east-1\ncontinuation"},
    sub_properties={},
)

The expected result is:

Section(
    properties={"region": "us-east-1"},
    sub_properties={},
)

Botocore does not filter the invalid property name, but it correctly associates continuation with invalid key rather than region.

Could we track when an invalid property has been ignored and also discard its continuation lines? This preserves our spec's silent-ignore behavior without corrupting the previous property.

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.

3 participants