使用mypy和boto3存根时出现类型错误
我在AWS上使用带类型的Python。(使用mypy和boto3的类型定义)
我对类型系统还不太熟悉,所以需要你们的建议和解释。
我有一个这样的函数:
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]
这个函数返回的字典大概是这样的:
{'from': datetime.datetime(2024, 3, 21, 0, 0)
'to':
datetime.datetime(2024, 3, 22, 0, 0)}
然后我有另一个函数,我把这个字典作为参数传给它。问题是,describe_events()这个函数期待接收一个类型为DateTimeRangeTypeDef的timeframe,但它却收到了dict[str, datetime]。我该如何“改变类型”或者我需要做什么才能正确写呢?
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
当我把第一个函数中的类型从
dict[str, datetime]
改成
DateTimeRangeTypeDef
时,它却提示我说类型是dict[str, datetime]。
1 个回答
为了更清楚,我假设我们在讨论的是 health_client = boto3.client('health')
这段代码。
在代码的简化版本中,它是这样定义的:
TimestampTypeDef = Union[datetime, str]
...
DateTimeRangeTypeDef = TypedDict(
"DateTimeRangeTypeDef",
{
"from": NotRequired[TimestampTypeDef],
"to": NotRequired[TimestampTypeDef],
},
)
查看这些简化版本的最简单方法是使用 pip install
安装相应的简化包(比如 pip install 'boto3-stubs[health]'
),然后查看它的文件夹内容(例如 grep -rn DateTimeRangeTypeDef ./venv/lib/python3.XX/site-packages/mypy_boto3_health/
)。接着,找到定义这个内容的文件,看看里面的内容。这个方法适用于任何服务,只需将上面文本中的 health
替换为你用来创建客户端的服务名称即可。
所以,直接用 DateTimeRangeTypeDef
替代 dict[str, datetime]
。你可能需要给外层的字典加上注释。下面的代码在类型检查时没有问题(我稍微修改了一些几乎肯定是错误的地方,比如在处理程序里面的 raise e
,但要注意,这里使用的 except Exception
是有点奇怪的,而且 LOGGER.error
默认不会打印错误追踪信息,你可能想用 LOGGER.exception
或者 LOGGER.error(..., exc_info=True)
来替代)。
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