25
25
from collections .abc import Mapping
26
26
from concurrent .futures import ThreadPoolExecutor , FIRST_COMPLETED , wait as wait_futures
27
27
from copy import copy
28
- from functools import partial , wraps
28
+ from functools import partial , reduce , wraps
29
29
from itertools import groupby , count , chain
30
30
import json
31
31
import logging
32
+ from typing import NamedTuple , Type , Optional
32
33
from warnings import warn
33
34
from random import random
34
35
import re
45
46
from cassandra import (ConsistencyLevel , AuthenticationFailed , InvalidRequest ,
46
47
OperationTimedOut , UnsupportedOperation ,
47
48
SchemaTargetType , DriverException , ProtocolVersion ,
48
- UnresolvableContactPoints )
49
+ UnresolvableContactPoints , DependencyException )
49
50
from cassandra .auth import _proxy_execute_key , PlainTextAuthProvider
50
51
from cassandra .connection import (ConnectionException , ConnectionShutdown ,
51
52
ConnectionHeartbeat , ProtocolVersionUnsupported ,
52
53
EndPoint , DefaultEndPoint , DefaultEndPointFactory ,
53
- ContinuousPagingState , SniEndPointFactory , ConnectionBusy )
54
+ ContinuousPagingState , SniEndPointFactory , ConnectionBusy , Connection )
54
55
from cassandra .cqltypes import UserType
55
56
import cassandra .cqltypes as types
56
57
from cassandra .encoder import Encoder
98
99
99
100
try :
100
101
from cassandra .io .twistedreactor import TwistedConnection
101
- except ImportError :
102
+ except DependencyException :
102
103
TwistedConnection = None
103
104
104
105
try :
105
106
from cassandra .io .eventletreactor import EventletConnection
106
- except ( ImportError , AttributeError ) :
107
+ except DependencyException :
107
108
# AttributeError was add for handling python 3.12 https://github.com/eventlet/eventlet/issues/812
108
109
# TODO: remove it when eventlet issue would be fixed
109
110
EventletConnection = None
113
114
except ImportError :
114
115
from cassandra .util import WeakSet # NOQA
115
116
117
+
118
+ class ClassImportResult (NamedTuple ):
119
+ name : str
120
+ exception : Optional [Exception ]
121
+ connection_class : Optional [Type [Connection ]]
122
+
123
+
124
+ def _is_gevent_monkey_patched ():
125
+ if 'gevent.monkey' not in sys .modules :
126
+ return False
127
+ try :
128
+ import gevent .socket
129
+ return socket .socket is gevent .socket .socket
130
+ except (ModuleNotFoundError , ImportError , AttributeError ):
131
+ return False
132
+
133
+ def _try_gevent_import ():
134
+ if _is_gevent_monkey_patched ():
135
+ try :
136
+ from cassandra .io .geventreactor import GeventConnection
137
+ return ClassImportResult (name = "GeventConnection" , connection_class = GeventConnection , exception = None )
138
+ except DependencyException as e :
139
+ return ClassImportResult (name = "GeventConnection" , connection_class = None , exception = e )
140
+ else :
141
+ return ClassImportResult (name = "GeventConnection" , connection_class = None , exception = DependencyException ("gevent is not patched" ))
142
+
143
+
116
144
def _is_eventlet_monkey_patched ():
117
145
if 'eventlet.patcher' not in sys .modules :
118
146
return False
@@ -124,32 +152,63 @@ def _is_eventlet_monkey_patched():
124
152
# TODO: remove it when eventlet issue would be fixed
125
153
return False
126
154
127
- def _is_gevent_monkey_patched ():
128
- if 'gevent.monkey' not in sys .modules :
129
- return False
155
+ def _try_eventlet_import ():
130
156
try :
131
- import eventlet .patcher
132
- return eventlet .patcher .is_monkey_patched ('socket' )
133
- # Another case related to PYTHON-1364
134
- except AttributeError :
135
- return False
157
+ from cassandra .io .eventletreactor import EventletConnection
158
+ except DependencyException as e :
159
+ return ClassImportResult (name = "EventletConnection" , connection_class = None , exception = e )
160
+ if _is_eventlet_monkey_patched ():
161
+ return ClassImportResult (name = "EventletConnection" , connection_class = EventletConnection , exception = None )
162
+ return ClassImportResult (name = "EventletConnection" , connection_class = None , exception = DependencyException ("eventlet is not patched" ))
163
+
164
+ def _try_libev_import ():
165
+ try :
166
+ from cassandra .io .libevreactor import LibevConnection
167
+ return ClassImportResult (name = "LibevConnection" , connection_class = LibevConnection , exception = None )
168
+ except DependencyException as e :
169
+ return ClassImportResult (name = "LibevConnection" , connection_class = None , exception = e )
136
170
171
+ def _try_asyncore_import ():
172
+ try :
173
+ from cassandra .io .asyncorereactor import AsyncoreConnection
174
+ return ClassImportResult (name = "AsyncoreConnection" , connection_class = AsyncoreConnection , exception = None )
175
+ except DependencyException as e :
176
+ return ClassImportResult (name = "AsyncoreConnection" , connection_class = None , exception = e )
137
177
138
- # default to gevent when we are monkey patched with gevent, eventlet when
139
- # monkey patched with eventlet, otherwise if libev is available, use that as
140
- # the default because it's fastest. Otherwise, use asyncore.
141
- if _is_gevent_monkey_patched ():
142
- from cassandra .io .geventreactor import GeventConnection as DefaultConnection
143
- elif _is_eventlet_monkey_patched ():
144
- from cassandra .io .eventletreactor import EventletConnection as DefaultConnection
145
- else :
178
+ def _try_twisted_import ():
146
179
try :
147
- from cassandra .io .libevreactor import LibevConnection as DefaultConnection # NOQA
148
- except ImportError :
149
- try :
150
- from cassandra .io .asyncorereactor import AsyncoreConnection as DefaultConnection # NOQA
151
- except ImportError :
152
- from cassandra .io .asyncioreactor import AsyncioConnection as DefaultConnection # NOQA
180
+ from cassandra .io .twistedreactor import TwistedConnection
181
+ return ClassImportResult (name = "TwistedConnection" , connection_class = TwistedConnection , exception = None )
182
+ except DependencyException as e :
183
+ return ClassImportResult (name = "TwistedConnection" , connection_class = None , exception = e )
184
+
185
+
186
+ log = logging .getLogger (__name__ )
187
+
188
+
189
+ def load_all_connections_classes ():
190
+ results = []
191
+ for try_fn in (_try_gevent_import , _try_eventlet_import , _try_libev_import , _try_asyncore_import , _try_twisted_import ):
192
+ results .append (try_fn ())
193
+ return tuple (results )
194
+
195
+ def get_all_supported_connections_classes ():
196
+ return [res .connection_class for res in load_all_connections_classes () if res .connection_class ]
197
+
198
+ def get_default_connection_class ():
199
+ excs = []
200
+ for try_fn in (_try_gevent_import , _try_eventlet_import , _try_libev_import , _try_asyncore_import , _try_twisted_import ):
201
+ res = try_fn ()
202
+ if res .connection_class :
203
+ return res .connection_class , excs
204
+ excs .append (res .exception )
205
+ return None , tuple (excs )
206
+
207
+
208
+ (conn_class , excs ) = get_default_connection_class ()
209
+ if not conn_class :
210
+ raise DependencyException ("Unable to load a default connection class" , excs )
211
+ DefaultConnection = conn_class
153
212
154
213
# Forces load of utf8 encoding module to avoid deadlock that occurs
155
214
# if code that is being imported tries to import the module in a seperate
0 commit comments