[FEATURE] Add a great-expectations user-agent suffix to S3 clients - #11937
[FEATURE] Add a great-expectations user-agent suffix to S3 clients#11937goanpeca wants to merge 4 commits into
Conversation
👷 Deploy request for niobium-lead-7998 pending review.Visit the deploys page to approve it
|
a8bc1ca to
af5e94a
Compare
af5e94a to
3f4760f
Compare
There was a problem hiding this comment.
Pull request overview
This PR adds a Great Expectations–specific great-expectations/<version> suffix to the boto3 S3 client user agent to make S3 traffic attributable (and easier to support/debug) across AWS S3 and S3-compatible object stores, while preserving any caller-provided boto3 options and agent strings.
Changes:
- Added
get_s3_boto3_options(...)ingreat_expectations/compatibility/aws.pyto inject/appenduser_agent_extrausing botocoreConfig. - Routed S3 client construction in the Pandas and Spark S3 datasources through the new helper.
- Routed
PandasExecutionEngineS3 client instantiation through the new helper.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| great_expectations/compatibility/aws.py | Adds helper that appends great-expectations/<version> to botocore Config.user_agent_extra for S3 clients. |
| great_expectations/execution_engine/pandas_execution_engine.py | Uses the helper when instantiating the execution engine’s internal S3 client. |
| great_expectations/datasource/fluent/pandas_s3_datasource.py | Uses the helper when constructing the datasource S3 client from boto3_options. |
| great_expectations/datasource/fluent/spark_s3_datasource.py | Uses the helper when constructing the datasource S3 client from boto3_options. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
3f4760f to
161c009
Compare
|
Thanks for the PR, @goanpeca -- the |
|
All committers have signed the CLA. ✅ |
|
@cla-bot check |
Signed-off-by: Gonzalo Peña-Castellanos <goanpeca@gmail.com>
c11ce22 to
6ead077
Compare
|
Thanks, @joshua-stauffer I’ve rebased the branch onto the latest develop and confirmed the static-analysis checks and full local unit suite pass. The CLA check is also passing now. It looks like the remaining CI jobs are currently blocked by the fork-permission workflow, so please let me know if there’s anything else needed from my side. |
joshua-stauffer
left a comment
There was a problem hiding this comment.
Thanks @goanpeca, I left a couple comments, but the core logic here is solid.
| def _get_distribution_version() -> str: | ||
| try: | ||
| return metadata.version("great_expectations") | ||
| except metadata.PackageNotFoundError: | ||
| return "dev" |
There was a problem hiding this comment.
I'd like to see this promoted to a public method (no leading _) and tested. On my machine this currently returns a minor version off, presumably due to editable install. Better would be to use the canonical __version__ constant. Since that's a top level export of GX, we should use a local import within the function to avoid circular references.__version__ also has the benefit of being always available, so no need for a fallback.
| return options | ||
|
|
||
| existing = getattr(config, "user_agent_extra", None) if config is not None else None | ||
| user_agent_extra = f"{existing} {suffix}" if existing else suffix |
There was a problem hiding this comment.
this isn't idempotent - calling twice in a row would result in the suffix appended twice. Not currently a bug, but future surface area for one.
Why
Great Expectations already lets a datasource pass
boto3_options(includingendpoint_urland a custombotocore.config.Config) to the underlying boto3 S3 client, so it works with Amazon S3 and any S3-compatible object store. Two gaps today: the S3 client goes out with only the default boto3/botocore user agent, so operators and providers cannot see which requests originate from Great Expectations; and the docs documentendpoint_urlbut do not make clear that setting it is how you connect to a non-AWS S3-compatible store.This change adds a
great-expectations/<version>suffix to the S3 client user agent and clarifies that docs option. It follows the commonlibrary/versionconvention that libraries use to attribute their S3 traffic, and helps with usage attribution, support, and debugging on Amazon S3 as well as on any S3-compatible object store (for example Backblaze B2, Cloudflare R2, or MinIO). The change is intentionally small and additive: it does not alter datasource configuration, credentials handling, or request behavior.What
get_s3_boto3_options(boto3_options)togreat_expectations/compatibility/aws.py. It returns the caller's options with auser_agent_extraofgreat-expectations/<distribution version>. If the caller already supplied aConfigwith auser_agent_extra, the suffix is appended rather than replacing it (viaConfig.merge), so a user's own agent string and anyendpoint_urlare preserved."dev"version when the installed distribution metadata is unavailable, and is a no-op that returns the options unchanged when botocore'sConfigis not importable, so environments without the AWS extras are unaffected.PandasS3Datasource,SparkS3Datasource, andPandasExecutionEngine._instantiate_s3_client. No call signatures or public APIs change.endpoint_urloption on the S3 Data Source page (docs/docusaurus/docs/core/connect_to_data/filesystem_data/_create_a_data_source/_s3/_s3.md) to note that it connects Great Expectations to an S3-compatible object store (Amazon S3, or a compatible provider such as Backblaze B2, Cloudflare R2, or MinIO), and to leave it unset for Amazon S3.Scope
This is a minimal, additive change: the AWS compatibility module, the three call sites that build a boto3 S3 client, and a one-line docs clarification. There is no change to configuration schema, type stubs, credentials handling, or the requests that Great Expectations issues. The framing is deliberately generic: Amazon S3 is named first and S3-compatible providers appear only as alphabetical examples; the user-agent suffix is appended to, never a replacement for, a user-provided agent string.
Test plan
Locally, against the
unitmarker:get_s3_boto3_options({})returns options whoseconfig.user_agent_extraequalsgreat-expectations/<version>.Config(user_agent_extra="my-app/1.0")is passed in, the result ismy-app/1.0 great-expectations/<version>(append, not overwrite), and an accompanyingendpoint_urlin the options is preserved.mock_boto3_optionsunit fixtures intests/datasource/fluent/test_pandas_s3_datasource.py.I have not committed a new test in this branch yet. If the reviewers would like the behavior locked in, I am happy to add a small
@pytest.mark.unittest forget_s3_boto3_optionsintests/(append vs set, andendpoint_urlpreservation) before merge.invoke lint(usesruff format+ruff check)endpoint_urlclarification); no unit test committed yet, see the Test plan above. I can add a@pytest.mark.unittest before merge if desired.