kaxil opened a new pull request, #74759:
URL: https://github.com/apache/airflow/pull/74759
A `HookToolset` method with a `datetime` parameter, such as
`S3Hook.list_keys(from_datetime=...)`, was handed the string the model sent, so
the hook failed comparing it and the task failed with it:
```
TypeError: '<=' not supported between instances of 'datetime.datetime' and
'str'
```
The existing `HookToolset` example exposes `list_keys`, and this came up in
a real run of the example in #74736. A `datetime` or `datetime | None`
parameter is now offered to the model as `{"type": "string", "format":
"date-time"}`, with a description asking for ISO 8601 with a UTC offset, and
the value is parsed into a timezone-aware `datetime` before the method is
called. A value with no offset, or one that does not parse, is sent back to the
model as a retry. That check runs while the call's arguments are validated, so
an approval gate is never asked about a call that cannot run.
A real `S3Hook.list_keys` on a moto bucket, driven by a scripted model,
fails on the first call without this change. With it:
```
call {'prefix': '2026/09/', 'from_datetime': '2000-01-01T00:00:00'}
retry Parameter 'from_datetime' must be an ISO 8601 date-time with a UTC
offset: '2000-01-01T00:00:00' has no UTC offset.
call {'prefix': '2026/09/', 'from_datetime': '2000-01-01T00:00:00Z'}
return ["2026/09/a.csv", "2026/09/b.csv"]
call {'prefix': '2026/09/', 'from_datetime': '2099-01-01T00:00:00+00:00'}
return []
```
## Design rationale
**A naive value is refused, not read as UTC.** Hooks disagree about what one
means: `S3Hook.list_keys` and `GCSHook.list_by_timespan` raise `TypeError`
comparing it with an aware time, while `GCSHook.is_updated_after` treats it as
UTC. The `date-time` format already asks for an offset, so a model that follows
the schema sends one, and one that does not is corrected in a single retry
instead of the toolset picking a timezone for it.
**The `bytes` handling is now a table that `datetime` joins.** #70433 gave
`bytes` parameters a detection rule, a description note and a decoder that also
runs in the args validator. `datetime` needs the same three things, so both are
entries in `_STRING_ENCODINGS` rather than two parallel code paths. As for
`bytes`, only `T` and `T | None` are decoded; a `datetime` inside a list or a
wider union is passed through as before, and is not advertised as `date-time`
either. The parsed value is not kept in the validated arguments, so what the
durable journal fingerprints stays JSON.
**Why not a pydantic `TypeAdapter` per parameter.** It would cover
`timedelta`, `UUID` and the rest in one go, but it cannot build a schema for
the arbitrary classes hook signatures use (it raises
`PydanticSchemaGenerationError`, so each parameter would need a fallback), it
accepts epoch numbers for an aware datetime, and its JSON `bytes` are UTF-8
rather than base64. The table changes only the parameters it names. `timedelta`
parameters, such as `execution_timeout` of `DatabricksSqlHook.run`, are still
passed through unconverted.
--
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]