15
15
import asyncio
16
16
import concurrent .futures as futures
17
17
from typing import Any , Callable , Coroutine , Dict , Type
18
+ from urllib .parse import urlparse
18
19
19
20
from .....utils import implements , classproperty
20
21
from .base import Channel , ChannelType , Server , Client
21
22
from .core import register_client , register_server
22
23
from .errors import ChannelClosed
23
24
24
- DUMMY_ADDRESS = 'dummy://'
25
+ DEFAULT_DUMMY_ADDRESS = 'dummy://0 '
25
26
26
27
27
28
class DummyChannel (Channel ):
@@ -81,7 +82,7 @@ def closed(self) -> bool:
81
82
class DummyServer (Server ):
82
83
__slots__ = '_closed' ,
83
84
84
- _instance = None
85
+ _address_to_instances : Dict [ str , "DummyServer" ] = dict ()
85
86
scheme = 'dummy'
86
87
87
88
def __init__ (self ,
@@ -91,8 +92,8 @@ def __init__(self,
91
92
self ._closed = asyncio .Event ()
92
93
93
94
@classmethod
94
- def get_instance (cls ):
95
- return cls ._instance
95
+ def get_instance (cls , address : str ):
96
+ return cls ._address_to_instances [ address ]
96
97
97
98
@classproperty
98
99
@implements (Server .client_type )
@@ -108,23 +109,22 @@ def channel_type(self) -> ChannelType:
108
109
@implements (Server .create )
109
110
async def create (config : Dict ) -> "DummyServer" :
110
111
config = config .copy ()
111
- address = config .pop ('address' , DUMMY_ADDRESS )
112
+ address = config .pop ('address' , DEFAULT_DUMMY_ADDRESS )
112
113
handle_channel = config .pop ('handle_channel' )
113
- if address != DUMMY_ADDRESS : # pragma: no cover
114
+ if urlparse ( address ). scheme != DummyServer . scheme : # pragma: no cover
114
115
raise ValueError (f'Address for DummyServer '
115
- f'should be { DUMMY_ADDRESS } , '
116
+ f'should be starts with "dummy://" , '
116
117
f'got { address } ' )
117
118
if config : # pragma: no cover
118
119
raise TypeError (f'Creating DummyServer got unexpected '
119
120
f'arguments: { "," .join (config )} ' )
120
121
121
- # DummyServer is singleton
122
- if DummyServer ._instance is not None :
123
- return DummyServer ._instance
124
-
125
- server = DummyServer (DUMMY_ADDRESS , handle_channel )
126
- DummyServer ._instance = server
127
- return server
122
+ try :
123
+ return DummyServer .get_instance (address )
124
+ except KeyError :
125
+ server = DummyServer (address , handle_channel )
126
+ DummyServer ._address_to_instances [address ] = server
127
+ return server
128
128
129
129
@implements (Server .start )
130
130
async def start (self ):
@@ -151,7 +151,7 @@ async def on_connected(self, *args, **kwargs):
151
151
@implements (Server .stop )
152
152
async def stop (self ):
153
153
self ._closed .set ()
154
- DummyServer ._instance = None
154
+ del DummyServer ._address_to_instances [ self . address ]
155
155
156
156
@property
157
157
@implements (Server .stopped )
@@ -179,10 +179,10 @@ def __init__(self,
179
179
async def connect (dest_address : str ,
180
180
local_address : str = None ,
181
181
** kwargs ) -> "Client" :
182
- if dest_address != DUMMY_ADDRESS : # pragma: no cover
183
- raise ValueError (f'Destination address has to be "dummy://" '
182
+ if urlparse ( dest_address ). scheme != DummyServer . scheme : # pragma: no cover
183
+ raise ValueError (f'Destination address should start with "dummy://" '
184
184
f'for DummyClient, got { dest_address } ' )
185
- server = DummyServer .get_instance ()
185
+ server = DummyServer .get_instance (dest_address )
186
186
if server is None : # pragma: no cover
187
187
raise RuntimeError ('DummyServer needs to be created '
188
188
'first before DummyClient' )
@@ -200,6 +200,5 @@ async def connect(dest_address: str,
200
200
@implements (Client .close )
201
201
async def close (self ):
202
202
await super ().close ()
203
- DummyClient ._instance = None
204
203
self ._task .cancel ()
205
204
self ._task = None
0 commit comments