Skip to content

Commit 8ecaadb

Browse files
committed
Refactor ClientParser and other parsers
1 parent e4c2e86 commit 8ecaadb

File tree

6 files changed

+130
-96
lines changed

6 files changed

+130
-96
lines changed

mypy_boto3_builder/parsers/client.py

Lines changed: 0 additions & 76 deletions
This file was deleted.
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
"""
2+
Parser for botocore Client, produces `structures.Client`.
3+
4+
Copyright 2025 Vlad Emelianov
5+
"""
6+
7+
from botocore.client import ClientMeta
8+
from botocore.errorfactory import ClientExceptionsFactory
9+
from botocore.exceptions import ClientError
10+
11+
from mypy_boto3_builder.constants import CLIENT
12+
from mypy_boto3_builder.logger import get_logger
13+
from mypy_boto3_builder.parsers.shape_parser import ShapeParser
14+
from mypy_boto3_builder.service_name import ServiceName
15+
from mypy_boto3_builder.structures.attribute import Attribute
16+
from mypy_boto3_builder.structures.client import Client
17+
from mypy_boto3_builder.structures.method import Method
18+
from mypy_boto3_builder.type_annotations.external_import import ExternalImport
19+
from mypy_boto3_builder.type_annotations.type import Type
20+
from mypy_boto3_builder.type_annotations.type_subscript import TypeSubscript
21+
from mypy_boto3_builder.type_maps.service_stub_map import get_stub_method_map
22+
from mypy_boto3_builder.utils.strings import get_class_prefix
23+
24+
25+
class ClientParser:
26+
"""
27+
Parser for botocore Client, produces `structures.Client`.
28+
29+
Arguments:
30+
service_name -- Target service name.
31+
shape_parser -- ShapeParser instance.
32+
"""
33+
34+
def __init__(
35+
self,
36+
service_name: ServiceName,
37+
shape_parser: ShapeParser,
38+
) -> None:
39+
self.name = CLIENT
40+
self.alias = service_name.get_client_name()
41+
self.service_name = service_name
42+
self.shape_parser = shape_parser
43+
self._logger = get_logger()
44+
45+
def _parse_method_map(self) -> dict[str, Method]:
46+
shape_method_map = self.shape_parser.get_client_method_map()
47+
stub_method_map = get_stub_method_map(self.service_name, self.name)
48+
for method in stub_method_map.values():
49+
self.shape_parser.create_request_type_annotation(
50+
method=method,
51+
name=f"{self.name}{get_class_prefix(method.name)}Request",
52+
)
53+
return {**shape_method_map, **stub_method_map}
54+
55+
def _parse_exception_class_attributes(self) -> list[Attribute]:
56+
result: list[Attribute] = []
57+
client_exceptions = ClientExceptionsFactory().create_client_exceptions(
58+
self.shape_parser.service_model
59+
)
60+
for exception_class_name in dir(client_exceptions):
61+
if exception_class_name.startswith("_"):
62+
continue
63+
if not exception_class_name[0].isupper():
64+
continue
65+
result.append(
66+
Attribute(
67+
exception_class_name,
68+
TypeSubscript(
69+
Type.Type,
70+
[ExternalImport.from_class(ClientError, alias="BotocoreClientError")],
71+
),
72+
),
73+
)
74+
return result
75+
76+
def parse(self) -> Client:
77+
"""
78+
Parse botocore Client.
79+
80+
Returns:
81+
Client structure.
82+
"""
83+
self._logger.debug(f"Parsing {self.alias}", tags=self.alias)
84+
85+
result = Client(
86+
name=self.alias,
87+
service_name=self.service_name,
88+
)
89+
90+
method_map = self._parse_method_map()
91+
result.methods.append(result.get_exceptions_property())
92+
result.methods.extend(method_map.values())
93+
94+
result.attributes.append(Attribute("meta", ExternalImport.from_class(ClientMeta)))
95+
96+
result.exceptions_class.attributes.extend(self._parse_exception_class_attributes())
97+
98+
return result

mypy_boto3_builder/parsers/resource_parser.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
Parser for Boto3 ServiceResource sub-resource, produces `structures.Resource`.
2+
Parser for botocore ServiceResource sub-resource, produces `structures.Resource`.
33
44
Copyright 2024 Vlad Emelianov
55
"""
@@ -24,12 +24,12 @@
2424

2525
class ResourceParser:
2626
"""
27-
Parser for Boto3 ServiceResource sub-resource, produces `structures.Resource`.
27+
Parser for botocore ServiceResource sub-resource, produces `structures.Resource`.
2828
2929
Arguments:
3030
name -- Resource name.
3131
service_name -- Target service name.
32-
shape_parser - ShapeParser instance.
32+
shape_parser -- ShapeParser instance.
3333
"""
3434

3535
def __init__(
@@ -120,10 +120,7 @@ def _parse_collections(self) -> list[Collection]:
120120

121121
def parse(self) -> ResourceRecord:
122122
"""
123-
Parse boto3 sub Resource data.
124-
125-
Arguments:
126-
resource -- Original boto3 resource.
123+
Parse botocore sub Resource data.
127124
128125
Returns:
129126
Resource structure.

mypy_boto3_builder/parsers/service_package_parser.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from mypy_boto3_builder.logger import get_logger
1010
from mypy_boto3_builder.package_data import BasePackageData
11-
from mypy_boto3_builder.parsers.client import parse_client
11+
from mypy_boto3_builder.parsers.client_parser import ClientParser
1212
from mypy_boto3_builder.parsers.service_resource_parser import ServiceResourceParser
1313
from mypy_boto3_builder.parsers.shape_parser import ShapeParser
1414
from mypy_boto3_builder.service_name import ServiceName, ServiceNameCatalog
@@ -79,11 +79,15 @@ def parse(self) -> ServicePackage:
7979
return result
8080

8181
def _parse_service_package(self) -> ServicePackage:
82-
client = parse_client(self.service_name, self.shape_parser)
8382
service_resource_parser = ServiceResourceParser(
8483
self.service_name,
8584
self.shape_parser,
8685
)
86+
client_parser = ClientParser(
87+
self.service_name,
88+
self.shape_parser,
89+
)
90+
client = client_parser.parse()
8791
service_resource = service_resource_parser.parse_service_resource()
8892

8993
return ServicePackage(

mypy_boto3_builder/parsers/service_resource_parser.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
Parser for Boto3 ServiceResource, produces `structures.ServiceResource`.
2+
Parser for botocore ServiceResource, produces `structures.ServiceResource`.
33
44
Copyright 2024 Vlad Emelianov
55
"""
@@ -21,11 +21,11 @@
2121

2222
class ServiceResourceParser(ResourceParser):
2323
"""
24-
Parser for boto3 ServiceResource data.
24+
Parser for botocore ServiceResource, produces `structures.ServiceResource`.
2525
2626
Arguments:
2727
service_name -- Target service name.
28-
shape_parser - ShapeParser instance.
28+
shape_parser -- ShapeParser instance.
2929
"""
3030

3131
def __init__(
@@ -38,6 +38,7 @@ def __init__(
3838
service_name=service_name,
3939
shape_parser=shape_parser,
4040
)
41+
self.alias = self.service_name.get_service_resource_name()
4142

4243
def _get_meta_attribute(self, name: str) -> Attribute:
4344
return Attribute(
@@ -65,24 +66,23 @@ def _get_resource_model(self) -> ResourceModel:
6566

6667
def parse_service_resource(self) -> ServiceResource | None:
6768
"""
68-
Parse main boto3 ServiceResource.
69+
Parse main botocore ServiceResource.
6970
"""
7071
if not self.shape_parser.has_service_resource():
7172
return None
7273

73-
name = self.service_name.get_service_resource_name()
74-
self._logger.debug(f"Parsing {name}", tags=name)
74+
self._logger.debug(f"Parsing {self.alias}", tags=self.alias)
7575
result = ServiceResource(
76-
name=name,
76+
name=self.alias,
7777
service_name=self.service_name,
7878
)
7979
meta_attribute = self._get_meta_attribute(result.resource_meta_class.name)
8080

81-
self._logger.debug(f"Parsing {name} methods", tags=name)
81+
self._logger.debug(f"Parsing {self.alias} methods", tags=self.alias)
8282
method_map = self._parse_method_map()
8383
result.methods.extend(list(method_map.values()))
8484

85-
self._logger.debug(f"Parsing {name} attributes and collections", tags=name)
85+
self._logger.debug(f"Parsing {self.alias} attributes and collections", tags=self.alias)
8686
collections = self._parse_collections()
8787
result.attributes.append(meta_attribute)
8888
result.attributes.extend(self._parse_attributes(collections))

tests/parsers/test_service_package.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66

77
class TestServicePackageParser:
8-
@patch("mypy_boto3_builder.parsers.client.ClientExceptionsFactory")
8+
@patch("mypy_boto3_builder.parsers.client_parser.ClientExceptionsFactory")
99
def test_parse(
1010
self,
1111
ClientExceptionsFactoryMock: MagicMock,
@@ -14,12 +14,23 @@ def test_parse(
1414
service_name_mock = MagicMock()
1515
botocore_session_mock().resource.return_value = None
1616
service_name_mock.boto3_name = "s3"
17-
ClientExceptionsFactoryMock.create_client_exceptions.return_value = []
17+
18+
class FakeExceptions:
19+
_internal = True
20+
__magic = True
21+
skip_lowecase = True
22+
RealException = True
23+
RealExceptionToo = True
24+
25+
ClientExceptionsFactoryMock().create_client_exceptions.return_value = FakeExceptions()
1826

1927
parser = ServicePackageParser(
2028
service_name_mock,
2129
package_data=Boto3StubsPackageData(),
2230
version="1.2.3",
2331
)
2432
result = parser.parse()
33+
assert len(result.client.exceptions_class.attributes) == 2
34+
assert result.client.exceptions_class.attributes[0].name == "RealException"
35+
assert result.client.exceptions_class.attributes[1].name == "RealExceptionToo"
2536
assert result.service_name == service_name_mock

0 commit comments

Comments
 (0)