I am using typed python in AWS. (mypy and boto3 stubs)
I am new at typing, so I will need your advice and explanation.
I have one function like this:
def select_time_range() -> dict[str, datetime]:
try:
current_datetime = datetime.now()
datetime_one_day_before = current_datetime - timedelta(days=1)
datetime_24_hours_ago = current_datetime - timedelta(hours=24)
report_timerange_choice = {
'previous_day': {
'from': datetime(datetime_one_day_before.year, datetime_one_day_before.month, datetime_one_day_before.day,
0, 0),
'to': datetime(current_datetime.year, current_datetime.month, current_datetime.day, 0, 0)
},
'last_24_hours': {
'from': datetime(datetime_24_hours_ago.year, datetime_24_hours_ago.month, datetime_24_hours_ago.day, datetime_24_hours_ago.hour, datetime_24_hours_ago.minute),
'to': datetime(current_datetime.year, current_datetime.month, current_datetime.day, current_datetime.hour, current_datetime.minute),
}
}
except Exception as e:
LOGGER.error(e)
raise e
return report_timerange_choice[TIMEFRAME_PICKER]
This function is returning dict like this:
{'from': datetime.datetime(2024, 3, 21, 0, 0)
'to':
datetime.datetime(2024, 3, 22, 0, 0)}
Then I have function like this where I am passing this dict to the function as a parameter. The thing is, describe_events() function is expecting *timeframe as a *type DateTimeRangeTypeDef, but it is getting dict[str, datetime]". How can I "change a type" or what I need to do to write correctly?
def get_account_events(timeframe: dict[str, datetime]) -> DescribeEventsResponseTypeDef:
try:
#DAILY
daily_events = health_client.describe_events(
filter={
'startTimes': [timeframe]
}
)
except Exception as e:
LOGGER.error(e)
raise e
return daily_events
When I change the type from
dict[str, datetime]
to
DateTimeRangeTypeDef
in first function, it is complaining that it is a type dict[str, datetime]
(to clarify, I assume that we're talking about health_client = boto3.client('health')
)
In stubs it is defined as
TimestampTypeDef = Union[datetime, str]
...
DateTimeRangeTypeDef = TypedDict(
"DateTimeRangeTypeDef",
{
"from": NotRequired[TimestampTypeDef],
"to": NotRequired[TimestampTypeDef],
},
)
The easiest way to see the stubs is to pip install
corresponding stubs package (e.g. pip install 'boto3-stubs[health]'
) and examine the contents of its folder (e.g. grep -rn DateTimeRangeTypeDef ./venv/lib/python3.XX/site-packages/mypy_boto3_health/
), then go to the file where it is defined and watch. It works for any service, replace health
with a service name - the one you use to instantiate a client - in the text above.
So just use DateTimeRangeTypeDef
instead of dict[str, datetime]
. You may need to annotate the outer dictionary. The following passes type checking cleanly (I took the liberty to fix a couple of almost-definitely-mistakes like raise e
inside handler, but beware that catch-all except Exception
is weird here, and LOGGER.error
does not print traceback by default, you may want to use LOGGER.exception
or LOGGER.error(..., exc_info=True)
instead).
from __future__ import annotations
import logging
from datetime import datetime, timedelta
from typing import TYPE_CHECKING, Final, Literal, TypeAlias
import boto3
if TYPE_CHECKING:
from mypy_boto3_health.type_defs import (
DateTimeRangeTypeDef,
DescribeEventsResponseTypeDef,
)
health_client: Final = boto3.client("health")
LOGGER: Final = logging.getLogger(__name__)
TimeFramePickerT: TypeAlias = Literal["previous_day", "last_24_hours"]
TIMEFRAME_PICKER: Final[TimeFramePickerT] = "last_24_hours"
def select_time_range() -> DateTimeRangeTypeDef:
try:
current_datetime = datetime.now()
datetime_one_day_before = current_datetime - timedelta(days=1)
datetime_24_hours_ago = current_datetime - timedelta(hours=24)
report_timerange_choice: dict[TimeFramePickerT, DateTimeRangeTypeDef] = {
"previous_day": {
"from": datetime(
datetime_one_day_before.year,
datetime_one_day_before.month,
datetime_one_day_before.day,
0,
0,
),
"to": datetime(
current_datetime.year,
current_datetime.month,
current_datetime.day,
0,
0,
),
},
"last_24_hours": {
"from": datetime(
datetime_24_hours_ago.year,
datetime_24_hours_ago.month,
datetime_24_hours_ago.day,
datetime_24_hours_ago.hour,
datetime_24_hours_ago.minute,
),
"to": datetime(
current_datetime.year,
current_datetime.month,
current_datetime.day,
current_datetime.hour,
current_datetime.minute,
),
},
}
except Exception as e:
LOGGER.exception(e)
raise
return report_timerange_choice[TIMEFRAME_PICKER]
def get_account_events(
timeframe: DateTimeRangeTypeDef,
) -> DescribeEventsResponseTypeDef:
try:
daily_events = health_client.describe_events(
filter={
"startTimes": [timeframe],
}
)
except Exception as e:
LOGGER.error(e)
raise
return daily_events