Skip to content

Commit e39f20a

Browse files
[PR #9762/c5d6b84f backport][3.11] Refactor payload registry to avoid linear searches for common types (#9767)
Co-authored-by: J. Nick Koston <[email protected]> closes #9419
1 parent 51145aa commit e39f20a

File tree

1 file changed

+22
-8
lines changed

1 file changed

+22
-8
lines changed

aiohttp/payload.py

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ def __init__(self) -> None:
101101
self._first: List[_PayloadRegistryItem] = []
102102
self._normal: List[_PayloadRegistryItem] = []
103103
self._last: List[_PayloadRegistryItem] = []
104+
self._normal_lookup: Dict[Any, PayloadType] = {}
104105

105106
def get(
106107
self,
@@ -109,12 +110,20 @@ def get(
109110
_CHAIN: "Type[chain[_PayloadRegistryItem]]" = chain,
110111
**kwargs: Any,
111112
) -> "Payload":
113+
if self._first:
114+
for factory, type_ in self._first:
115+
if isinstance(data, type_):
116+
return factory(data, *args, **kwargs)
117+
# Try the fast lookup first
118+
if lookup_factory := self._normal_lookup.get(type(data)):
119+
return lookup_factory(data, *args, **kwargs)
120+
# Bail early if its already a Payload
112121
if isinstance(data, Payload):
113122
return data
114-
for factory, type in _CHAIN(self._first, self._normal, self._last):
115-
if isinstance(data, type):
123+
# Fallback to the slower linear search
124+
for factory, type_ in _CHAIN(self._normal, self._last):
125+
if isinstance(data, type_):
116126
return factory(data, *args, **kwargs)
117-
118127
raise LookupError()
119128

120129
def register(
@@ -124,6 +133,11 @@ def register(
124133
self._first.append((factory, type))
125134
elif order is Order.normal:
126135
self._normal.append((factory, type))
136+
if isinstance(type, Iterable):
137+
for t in type:
138+
self._normal_lookup[t] = factory
139+
else:
140+
self._normal_lookup[type] = factory
127141
elif order is Order.try_last:
128142
self._last.append((factory, type))
129143
else:
@@ -159,7 +173,8 @@ def __init__(
159173
self._headers[hdrs.CONTENT_TYPE] = content_type
160174
else:
161175
self._headers[hdrs.CONTENT_TYPE] = self._default_content_type
162-
self._headers.update(headers or {})
176+
if headers:
177+
self._headers.update(headers)
163178

164179
@property
165180
def size(self) -> Optional[int]:
@@ -228,18 +243,17 @@ class BytesPayload(Payload):
228243
def __init__(
229244
self, value: Union[bytes, bytearray, memoryview], *args: Any, **kwargs: Any
230245
) -> None:
231-
if not isinstance(value, (bytes, bytearray, memoryview)):
232-
raise TypeError(f"value argument must be byte-ish, not {type(value)!r}")
233-
234246
if "content_type" not in kwargs:
235247
kwargs["content_type"] = "application/octet-stream"
236248

237249
super().__init__(value, *args, **kwargs)
238250

239251
if isinstance(value, memoryview):
240252
self._size = value.nbytes
241-
else:
253+
elif isinstance(value, (bytes, bytearray)):
242254
self._size = len(value)
255+
else:
256+
raise TypeError(f"value argument must be byte-ish, not {type(value)!r}")
243257

244258
if self._size > TOO_LARGE_BYTES_BODY:
245259
kwargs = {"source": self}

0 commit comments

Comments
 (0)