Toggle Light / Dark / Auto color theme
Toggle table of contents sidebar
Source code for boto3_dataclass.parsers.client_parser
# -*- coding: utf-8 -*-
"""
Parse ``mypy_boto3_${aws_service}/client.pyi`` stub file to extract all client methods
.. note::
这个模块中的所有 ``*Parser`` 类都使用了 Command Pattern, 也就是虽然是一个类,
但是它被用来当成一个函数来使用, 主函数只有 ``.parse()`` 这一个. 在整个生命周期内,
把需要共享的数据作为属性放在 ``_attr_name`` 的属性中, 使得代码更加简洁清晰.
"""
import ast
import dataclasses
from ..constants import BASE_CLIENT , TYPE_DEF
from ..models.caster import CasterMethod , CasterModule
from .base import StubFileParser
# DEBUG = True
DEBUG = False
[docs]
@dataclasses . dataclass
class ClientModuleParser ( StubFileParser ):
"""
从 ``mypy_boto3_${aws_service}/client.pyi`` stub file 中解析出所有 client 方法,
提取出返回 TypedDict 的方法, 为生成 dataclass 转换器做准备.
"""
_caster_module : CasterModule = dataclasses . field ( init = False )
@property
def caster_module ( self ) -> CasterModule :
return self . _caster_module
@property
def service_name ( self ) -> str :
return self . path_stub_file . parent . name . removeprefix ( "mypy_boto3_" )
[docs]
def parse ( self ):
"""
解析 AST 模块, 查找并解析 client 类, 提取所有返回 TypedDict 的方法.
"""
# 遍历模块的所有顶级节点,寻找 client 类定义
for i , node in enumerate ( self . module . body , start = 1 ):
if self . is_client_class_node ( node ):
self . parse_client_class ( node )
return self . caster_module
raise ValueError ( "No client class found in the stub file." ) # pragma: no cover
[docs]
def is_client_class_node ( self , node ) -> bool :
"""
判断给定的 AST 节点是否是 client 类定义.
client 类必须继承自 BaseClient.
"""
if isinstance ( node , ast . ClassDef ):
# 基类有且只有一个, 并且是 BaseClient
if len ( node . bases ) == 1 :
base = node . bases [ 0 ]
if isinstance ( base , ast . Name ):
if base . id == BASE_CLIENT :
return True
return False
[docs]
def parse_client_class ( self , node_cd : ast . ClassDef ):
"""
解析 client 类定义, 提取所有返回 TypedDict 的方法.
:param node_cd: client 类的 AST ClassDef 节点
"""
name = node_cd . name
if DEBUG : # pragma: no cover
lineno = str ( node_cd . lineno ) . zfill ( self . zfill )
print ( f " { lineno } class { name } : # <--- parse this" ) # for debug only
methods = []
# 遍历类中的所有方法定义
for i , node in enumerate ( node_cd . body , start = 1 ):
if self . is_client_method_node ( node ):
client_method = self . parse_client_method ( node )
methods . append ( client_method )
self . _caster_module = CasterModule (
service_name = self . service_name ,
cms = methods ,
)
[docs]
def is_client_method_node ( self , node ) -> bool :
"""
判断给定的 AST 节点是否是我们关心的 client 方法.
我们只关心返回类型是 TypedDict (以 'TypeDef' 结尾) 的方法.
"""
if isinstance ( node , ast . FunctionDef ):
# 检查返回类型注解是否存在且为 Name 类型
if isinstance ( node . returns , ast . Name ):
# 返回类型名必须以 'TypeDef' 结尾
if node . returns . id . endswith ( TYPE_DEF ):
return True
return False
[docs]
def parse_client_method ( self , node : ast . FunctionDef ) -> CasterMethod :
"""
解析 client 方法定义, 提取方法名和返回类型信息.
:param node: 方法的 AST FunctionDef 节点
:return: 解析后的 CasterMethod 对象
"""
method_name = node . name
return_type = node . returns . id # boto3-stubs 中的 TypedDict 类型名
if DEBUG : # pragma: no cover
lineno = str ( node . lineno ) . zfill ( self . zfill )
text = f " { lineno } def { method_name } (self, ...) -> { return_type } # <--- parse this"
print ( text )
# 创建 CasterMethod 对象,将 boto3-stubs 类型映射到 dataclass 类型
caster_method = CasterMethod (
method_name = node . name ,
boto3_stubs_type_name = return_type ,
# 移除 'TypeDef' 后缀得到对应的 dataclass 类型名
boto3_dataclass_type_name = return_type . removesuffix ( TYPE_DEF ),
)
return caster_method