1
1
# This contains the main Connection class. Everything in h11 revolves around
2
2
# this.
3
+ from enum import auto , Enum
3
4
from typing import Any , Callable , cast , Dict , List , Optional , Tuple , Type , Union
4
5
5
6
from ._events import (
22
23
DONE ,
23
24
ERROR ,
24
25
MIGHT_SWITCH_PROTOCOL ,
26
+ Role ,
25
27
SEND_BODY ,
26
28
SERVER ,
29
+ State ,
27
30
SWITCHED_PROTOCOL ,
31
+ SwitchState ,
32
+ SwitchType ,
28
33
)
29
- from ._util import ( # Import the internal things we need
30
- LocalProtocolError ,
31
- RemoteProtocolError ,
32
- Sentinel ,
33
- )
34
+ from ._util import LocalProtocolError # Import the internal things we need
35
+ from ._util import RemoteProtocolError
34
36
from ._writers import WRITERS , WritersType
35
37
36
38
# Everything in __all__ gets re-exported as part of the h11 public API.
37
39
__all__ = ["Connection" , "NEED_DATA" , "PAUSED" ]
38
40
39
41
40
- class NEED_DATA (Sentinel , metaclass = Sentinel ):
41
- pass
42
+ class PseudoEvent (Enum ):
43
+ NEED_DATA = auto ()
44
+ PAUSED = auto ()
42
45
43
46
44
- class PAUSED ( Sentinel , metaclass = Sentinel ):
45
- pass
47
+ NEED_DATA = PseudoEvent . NEED_DATA
48
+ PAUSED = PseudoEvent . PAUSED
46
49
47
50
48
51
# If we ever have this much buffered without it making a complete parseable
@@ -154,15 +157,15 @@ class Connection:
154
157
155
158
def __init__ (
156
159
self ,
157
- our_role : Type [ Sentinel ] ,
160
+ our_role : Role ,
158
161
max_incomplete_event_size : int = DEFAULT_MAX_INCOMPLETE_EVENT_SIZE ,
159
162
) -> None :
160
163
self ._max_incomplete_event_size = max_incomplete_event_size
161
164
# State and role tracking
162
165
if our_role not in (CLIENT , SERVER ):
163
166
raise ValueError ("expected CLIENT or SERVER, not {!r}" .format (our_role ))
164
167
self .our_role = our_role
165
- self .their_role : Type [ Sentinel ]
168
+ self .their_role : Role
166
169
if our_role is CLIENT :
167
170
self .their_role = SERVER
168
171
else :
@@ -192,7 +195,7 @@ def __init__(
192
195
self .client_is_waiting_for_100_continue = False
193
196
194
197
@property
195
- def states (self ) -> Dict [Type [ Sentinel ], Type [ Sentinel ]]:
198
+ def states (self ) -> Dict [Role , Union [ State , SwitchState ]]:
196
199
"""A dictionary like::
197
200
198
201
{CLIENT: <client state>, SERVER: <server state>}
@@ -203,14 +206,14 @@ def states(self) -> Dict[Type[Sentinel], Type[Sentinel]]:
203
206
return dict (self ._cstate .states )
204
207
205
208
@property
206
- def our_state (self ) -> Type [ Sentinel ]:
209
+ def our_state (self ) -> Union [ State , SwitchState ]:
207
210
"""The current state of whichever role we are playing. See
208
211
:ref:`state-machine` for details.
209
212
"""
210
213
return self ._cstate .states [self .our_role ]
211
214
212
215
@property
213
- def their_state (self ) -> Type [ Sentinel ]:
216
+ def their_state (self ) -> Union [ State , SwitchState ]:
214
217
"""The current state of whichever role we are NOT playing. See
215
218
:ref:`state-machine` for details.
216
219
"""
@@ -240,12 +243,12 @@ def start_next_cycle(self) -> None:
240
243
assert not self .client_is_waiting_for_100_continue
241
244
self ._respond_to_state_changes (old_states )
242
245
243
- def _process_error (self , role : Type [ Sentinel ] ) -> None :
246
+ def _process_error (self , role : Role ) -> None :
244
247
old_states = dict (self ._cstate .states )
245
248
self ._cstate .process_error (role )
246
249
self ._respond_to_state_changes (old_states )
247
250
248
- def _server_switch_event (self , event : Event ) -> Optional [Type [ Sentinel ] ]:
251
+ def _server_switch_event (self , event : Event ) -> Optional [SwitchType ]:
249
252
if type (event ) is InformationalResponse and event .status_code == 101 :
250
253
return _SWITCH_UPGRADE
251
254
if type (event ) is Response :
@@ -257,7 +260,7 @@ def _server_switch_event(self, event: Event) -> Optional[Type[Sentinel]]:
257
260
return None
258
261
259
262
# All events go through here
260
- def _process_event (self , role : Type [ Sentinel ] , event : Event ) -> None :
263
+ def _process_event (self , role : Role , event : Event ) -> None :
261
264
# First, pass the event through the state machine to make sure it
262
265
# succeeds.
263
266
old_states = dict (self ._cstate .states )
@@ -307,7 +310,7 @@ def _process_event(self, role: Type[Sentinel], event: Event) -> None:
307
310
308
311
def _get_io_object (
309
312
self ,
310
- role : Type [ Sentinel ] ,
313
+ role : Role ,
311
314
event : Optional [Event ],
312
315
io_dict : Union [ReadersType , WritersType ],
313
316
) -> Optional [Callable [..., Any ]]:
@@ -323,13 +326,13 @@ def _get_io_object(
323
326
else :
324
327
# General case: the io_dict just has the appropriate reader/writer
325
328
# for this state
326
- return io_dict .get ((role , state )) # type: ignore[return-value]
329
+ return io_dict .get ((role , state )) # type: ignore[arg-type, return-value]
327
330
328
331
# This must be called after any action that might have caused
329
332
# self._cstate.states to change.
330
333
def _respond_to_state_changes (
331
334
self ,
332
- old_states : Dict [Type [ Sentinel ], Type [ Sentinel ]],
335
+ old_states : Dict [Role , Union [ State , SwitchState ]],
333
336
event : Optional [Event ] = None ,
334
337
) -> None :
335
338
# Update reader/writer
@@ -397,7 +400,7 @@ def receive_data(self, data: bytes) -> None:
397
400
398
401
def _extract_next_receive_event (
399
402
self ,
400
- ) -> Union [Event , Type [ NEED_DATA ], Type [ PAUSED ] ]:
403
+ ) -> Union [Event , PseudoEvent ]:
401
404
state = self .their_state
402
405
# We don't pause immediately when they enter DONE, because even in
403
406
# DONE state we can still process a ConnectionClosed() event. But
@@ -423,7 +426,7 @@ def _extract_next_receive_event(
423
426
event = NEED_DATA
424
427
return event # type: ignore[no-any-return]
425
428
426
- def next_event (self ) -> Union [Event , Type [ NEED_DATA ], Type [ PAUSED ] ]:
429
+ def next_event (self ) -> Union [Event , PseudoEvent ]:
427
430
"""Parse the next event out of our receive buffer, update our internal
428
431
state, and return it.
429
432
0 commit comments