SEPURI-SAI-KRISHNA opened a new pull request, #74843:
URL: https://github.com/apache/airflow/pull/74843

   Passing a `botocore.config.Config` object as `botocore_config` works for an 
Amazon operator or sensor that runs to completion, and fails the task once it 
defers.
   
   Reproduced on current main with `SsmRunCommandOperator`:
   
   ```python
   from botocore.config import Config
   
   SsmRunCommandOperator(
       task_id="run",
       document_name="AWS-RunShellScript",
       run_command_kwargs={"InstanceIds": ["i-1234567890abcdef0"]},
       botocore_config=Config(read_timeout=120, retries={"max_attempts": 5}),
       deferrable=True,
   )
   ```
   
   `execute` calls `send_command` first and defers afterwards, so by the time 
the trigger argument is serialized the command is already running on the 
instance. Serialization then raises `TypeError: Cannot serialize object of type 
<class 'botocore.config.Config'>` and the task fails, leaving the command 
running with nothing watching it or reporting its result. The non deferrable 
form of the same task succeeds, and so does the same task with 
`botocore_config={"read_timeout": 120, "retries": {"max_attempts": 5}}`.
   
   The mapping is what the interface declares. Every operator and sensor 
documents the parameter as `botocore_config: dict | None` and as a 
"Configuration dictionary (key-values)", and the trigger signatures repeat it. 
Only the hooks widen it, declaring `config: Config | None`, which is how a 
`Config` gets this far at all. Both forms produce the same client, verified by 
building an SSM client each way and reading back `read_timeout=120`.
   
   This resolves the parameter to the declared mapping where the hook 
parameters are assembled, so the value a trigger receives is one it can carry. 
Nothing that works today changes behaviour: a dictionary and `None` are 
returned untouched, and a `Config` yields the options it was constructed with, 
which the hook rebuilds into an equivalent `Config`.
   
   All 39 operator and sensor modules that forward `botocore_config` to a 
trigger derive from `AwsBaseOperator` or `AwsBaseSensor`, and all three 
constructors that resolve these parameters route through 
`AwsHookParams.from_constructor`, so the single place covers the whole surface.
   
   Only the options the caller actually passed are carried over, so the result 
does not pin whichever defaults the installed botocore happens to carry. If a 
future botocore stops exposing them, the value is passed through unchanged and 
the behaviour is the one in place today rather than a new failure.
   
   Verified locally on Python 3.12 against SQLite:
   
   - The new tests pass, and with the production change reverted the two that 
cover it fail, the operator-level one reporting a `Config` object where the 
dictionary is expected.
   - `providers/amazon/tests/unit/amazon/aws/utils/test_mixins.py`, 
`operators/test_base_aws.py`, `sensors/test_base_aws.py` and 
`utils/test_connection_wrapper.py`: 126 passed.
   - `hooks/test_base_aws.py`, `operators/test_ssm.py`, `sensors/test_ssm.py` 
and the whole `triggers/` directory: 599 passed, with 1 failure and 2 errors 
that are present without this change as well and come from provider-version and 
waiter-config fixtures unrelated to it.
   - `scripts/ci/prek/check_deferred_hook_configuration.py`: clean.
   - `ruff format` and `ruff check` on both changed files: clean.
   
   mypy was not run; it needs the Breeze image, which this machine cannot start.
   
   ---
   
   ##### Was generative AI tooling used to co-author this PR?
   
   - [X] Yes - Claude Code (Opus 5)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to