Skip to content

Commit d3b1d15

Browse files
committed
Fix pyright errors
1 parent 91e8208 commit d3b1d15

File tree

3 files changed

+70
-64
lines changed

3 files changed

+70
-64
lines changed

packages/smithy-aws-core/src/smithy_aws_core/interceptors/user_agent.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from smithy_http.aio import HTTPRequest
1818

1919

20-
class UserAgentInterceptor(Interceptor):
20+
class UserAgentInterceptor(Interceptor[Request, None, HTTPRequest, None]):
2121
"""Adds UserAgent header to the Request before signing."""
2222

2323
def __init__(

packages/smithy-aws-core/src/smithy_aws_core/user_agent.py

Lines changed: 68 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import os
1515
import platform
1616
from string import ascii_letters, digits
17-
from typing import NamedTuple, Optional, Self
17+
from typing import NamedTuple, Optional, Self, Union, List
1818

1919
from smithy_http.aio.crt import HAS_CRT
2020

@@ -33,16 +33,58 @@
3333
_USERAGENT_SDK_NAME = "aws-sdk-python"
3434

3535

36+
class UserAgentComponent(NamedTuple):
37+
"""Component of a User-Agent header string in the standard format.
38+
39+
Each component consists of a prefix, a name, and a value. In the string
40+
representation these are combined in the format ``prefix/name#value``.
41+
42+
This class is considered private and is subject to abrupt breaking changes.
43+
"""
44+
45+
prefix: str
46+
name: str
47+
value: Optional[str] = None
48+
49+
def to_string(self):
50+
"""Create string like 'prefix/name#value' from a UserAgentComponent."""
51+
clean_prefix = sanitize_user_agent_string_component(
52+
self.prefix, allow_hash=True
53+
)
54+
clean_name = sanitize_user_agent_string_component(self.name, allow_hash=False)
55+
if self.value is None or self.value == "":
56+
return f"{clean_prefix}/{clean_name}"
57+
clean_value = sanitize_user_agent_string_component(self.value, allow_hash=True)
58+
return f"{clean_prefix}/{clean_name}#{clean_value}"
59+
60+
61+
class RawStringUserAgentComponent:
62+
"""UserAgentComponent interface wrapper around ``str``.
63+
64+
Use for User-Agent header components that are not constructed from prefix+name+value
65+
but instead are provided as strings. No sanitization is performed.
66+
"""
67+
68+
def __init__(self, value: str):
69+
self._value = value
70+
71+
def to_string(self) -> str:
72+
return self._value
73+
74+
75+
_UAComponent = Union[UserAgentComponent, RawStringUserAgentComponent]
76+
77+
3678
class UserAgent:
3779
def __init__(
3880
self,
39-
platform_name,
40-
platform_version,
41-
platform_machine,
42-
python_version,
43-
python_implementation,
44-
execution_env,
45-
crt_version=None,
81+
platform_name: str | None,
82+
platform_version: str | None,
83+
platform_machine: str | None,
84+
python_version: str | None,
85+
python_implementation: str | None,
86+
execution_env: str | None,
87+
crt_version: str | None,
4688
) -> None:
4789
self._platform_name = platform_name
4890
self._platform_version = platform_version
@@ -74,16 +116,16 @@ def from_environment(cls) -> Self:
74116

75117
def with_config(
76118
self,
77-
ua_suffix: str | None = None,
78-
ua_app_id: str | None = None,
79-
sdk_version: str | None = None,
119+
ua_suffix: str | None,
120+
ua_app_id: str | None,
121+
sdk_version: str | None,
80122
) -> Self:
81123
self._user_agent_suffix = ua_suffix
82124
self._user_agent_app_id = ua_app_id
83125
self._sdk_version = sdk_version
84126
return self
85127

86-
def to_string(self):
128+
def to_string(self) -> str:
87129
"""Build User-Agent header string from the object's properties."""
88130
components = [
89131
*self._build_sdk_metadata(),
@@ -99,20 +141,22 @@ def to_string(self):
99141

100142
return " ".join([comp.to_string() for comp in components])
101143

102-
def _build_sdk_metadata(self):
144+
def _build_sdk_metadata(self) -> List[UserAgentComponent]:
103145
"""Build the SDK name and version component of the User-Agent header.
104146
105147
Includes CRT version if available.
106148
"""
107-
sdk_md = []
108-
sdk_md.append(UserAgentComponent(_USERAGENT_SDK_NAME, self._sdk_version))
149+
sdk_version = self._sdk_version if self._sdk_version else "Unknown"
150+
sdk_md: List[UserAgentComponent] = [
151+
UserAgentComponent(_USERAGENT_SDK_NAME, sdk_version)
152+
]
109153

110154
if self._crt_version is not None:
111155
sdk_md.append(UserAgentComponent("md", "awscrt", self._crt_version))
112156

113157
return sdk_md
114158

115-
def _build_os_metadata(self):
159+
def _build_os_metadata(self) -> List[UserAgentComponent]:
116160
"""Build the OS/platform components of the User-Agent header string.
117161
118162
For recognized platform names that match or map to an entry in the list
@@ -145,7 +189,7 @@ def _build_os_metadata(self):
145189
UserAgentComponent("md", self._platform_name, self._platform_version),
146190
]
147191

148-
def _build_architecture_metadata(self):
192+
def _build_architecture_metadata(self) -> List[UserAgentComponent]:
149193
"""Build architecture component of the User-Agent header string.
150194
151195
Returns the machine type with prefix "md" and name "arch", if one is available.
@@ -155,7 +199,7 @@ def _build_architecture_metadata(self):
155199
return [UserAgentComponent("md", "arch", self._platform_machine.lower())]
156200
return []
157201

158-
def _build_language_metadata(self):
202+
def _build_language_metadata(self) -> List[UserAgentComponent]:
159203
"""Build the language components of the User-Agent header string.
160204
161205
Returns the Python version in a component with prefix "lang" and name
@@ -174,7 +218,7 @@ def _build_language_metadata(self):
174218
)
175219
return lang_md
176220

177-
def _build_execution_env_metadata(self):
221+
def _build_execution_env_metadata(self) -> List[UserAgentComponent]:
178222
"""Build the execution environment component of the User-Agent header.
179223
180224
Returns a single component prefixed with "exec-env", usually sourced from the
@@ -185,28 +229,28 @@ def _build_execution_env_metadata(self):
185229
else:
186230
return []
187231

188-
def _build_feature_metadata(self):
232+
def _build_feature_metadata(self) -> List[UserAgentComponent]:
189233
"""Build the features components of the User-Agent header string.
190234
191235
TODO: These should be sourced from property bag set on context.
192236
"""
193237
return []
194238

195-
def _build_app_id(self):
239+
def _build_app_id(self) -> List[UserAgentComponent]:
196240
"""Build app component of the User-Agent header string."""
197241
if self._user_agent_app_id:
198242
return [UserAgentComponent("app", self._user_agent_app_id)]
199243
else:
200244
return []
201245

202-
def _build_suffix(self):
246+
def _build_suffix(self) -> List[_UAComponent]:
203247
if self._user_agent_suffix:
204248
return [RawStringUserAgentComponent(self._user_agent_suffix)]
205249
else:
206250
return []
207251

208252

209-
def sanitize_user_agent_string_component(raw_str, allow_hash):
253+
def sanitize_user_agent_string_component(raw_str: str, allow_hash: bool = False) -> str:
210254
"""Replaces all not allowed characters in the string with a dash ("-").
211255
212256
Allowed characters are ASCII alphanumerics and ``!$%&'*+-.^_`|~``. If
@@ -224,46 +268,7 @@ def sanitize_user_agent_string_component(raw_str, allow_hash):
224268
)
225269

226270

227-
class UserAgentComponent(NamedTuple):
228-
"""Component of a User-Agent header string in the standard format.
229-
230-
Each component consists of a prefix, a name, and a value. In the string
231-
representation these are combined in the format ``prefix/name#value``.
232-
233-
This class is considered private and is subject to abrupt breaking changes.
234-
"""
235-
236-
prefix: str
237-
name: str
238-
value: Optional[str] = None
239-
240-
def to_string(self):
241-
"""Create string like 'prefix/name#value' from a UserAgentComponent."""
242-
clean_prefix = sanitize_user_agent_string_component(
243-
self.prefix, allow_hash=True
244-
)
245-
clean_name = sanitize_user_agent_string_component(self.name, allow_hash=False)
246-
if self.value is None or self.value == "":
247-
return f"{clean_prefix}/{clean_name}"
248-
clean_value = sanitize_user_agent_string_component(self.value, allow_hash=True)
249-
return f"{clean_prefix}/{clean_name}#{clean_value}"
250-
251-
252-
class RawStringUserAgentComponent:
253-
"""UserAgentComponent interface wrapper around ``str``.
254-
255-
Use for User-Agent header components that are not constructed from prefix+name+value
256-
but instead are provided as strings. No sanitization is performed.
257-
"""
258-
259-
def __init__(self, value):
260-
self._value = value
261-
262-
def to_string(self):
263-
return self._value
264-
265-
266-
def _get_crt_version():
271+
def _get_crt_version() -> str | None:
267272
"""This function is considered private and is subject to abrupt breaking changes."""
268273
try:
269274
import awscrt

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ aws_event_stream = { workspace = true }
3333

3434
[tool.pyright]
3535
typeCheckingMode = "strict"
36+
reportMissingTypeStubs = false # TODO: Remove once awscrt supplies stubs/types
3637

3738
[tool.pytest.ini_options]
3839
asyncio_mode = "auto" # makes pytest run async tests without having to be marked with the @pytest.mark.asyncio decorator

0 commit comments

Comments
 (0)