Skip to content

Commit d598845

Browse files
brettabamonteabsurdfarce
authored andcommitted
CASSPYTHON-13: Remove eventlet, gevent and twisted event loops
patch by Brett Abamonte; reviewed by Bret McGuire
1 parent ec21afc commit d598845

31 files changed

Lines changed: 57 additions & 1479 deletions

Jenkinsfile

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Matrix Types:
2121
2222
Parameters:
2323
24-
EVENT_LOOP: 'LIBEV' (Default), 'GEVENT', 'EVENTLET', 'ASYNCIO', 'ASYNCORE', 'TWISTED'
24+
EVENT_LOOP: 'LIBEV' (Default), 'ASYNCIO', 'ASYNCORE'
2525
CYTHON: Default, 'True', 'False'
2626
2727
*/
@@ -296,8 +296,6 @@ def executeStandardTests() {
296296
297297
failure=0
298298
EVENT_LOOP=${EVENT_LOOP} VERIFY_CYTHON=${CYTHON_ENABLED} JVM_EXTRA_OPTS="$JVM_EXTRA_OPTS -Xss384k" pytest -s -v --log-format="[%(levelname)s] %(asctime)s %(thread)d: %(message)s" --junit-xml=unit_results.xml tests/unit/ || failure=1
299-
EVENT_LOOP_MANAGER=eventlet VERIFY_CYTHON=${CYTHON_ENABLED} JVM_EXTRA_OPTS="$JVM_EXTRA_OPTS -Xss384k" pytest -s -v --log-format="[%(levelname)s] %(asctime)s %(thread)d: %(message)s" --junit-xml=unit_eventlet_results.xml tests/unit/io/test_eventletreactor.py || failure=1
300-
EVENT_LOOP_MANAGER=gevent VERIFY_CYTHON=${CYTHON_ENABLED} JVM_EXTRA_OPTS="$JVM_EXTRA_OPTS -Xss384k" pytest -s -v --log-format="[%(levelname)s] %(asctime)s %(thread)d: %(message)s" --junit-xml=unit_gevent_results.xml tests/unit/io/test_geventreactor.py || failure=1
301299
exit $failure
302300
'''
303301
} catch (err) {
@@ -670,7 +668,7 @@ pipeline {
670668
</table>''')
671669
choice(
672670
name: 'EVENT_LOOP',
673-
choices: ['LIBEV', 'GEVENT', 'EVENTLET', 'ASYNCIO', 'ASYNCORE', 'TWISTED'],
671+
choices: ['LIBEV', 'ASYNCIO', 'ASYNCORE'],
674672
description: '''<p>Event loop manager to utilize for scheduled or adhoc builds</p>
675673
<table style="width:100%">
676674
<col width="25%">
@@ -683,14 +681,6 @@ pipeline {
683681
<td><strong>LIBEV</strong></td>
684682
<td>A full-featured and high-performance event loop that is loosely modeled after libevent, but without its limitations and bugs</td>
685683
</tr>
686-
<tr>
687-
<td><strong>GEVENT</strong></td>
688-
<td>A co-routine -based Python networking library that uses greenlet to provide a high-level synchronous API on top of the libev or libuv event loop</td>
689-
</tr>
690-
<tr>
691-
<td><strong>EVENTLET</strong></td>
692-
<td>A concurrent networking library for Python that allows you to change how you run your code, not how you write it</td>
693-
</tr>
694684
<tr>
695685
<td><strong>ASYNCIO</strong></td>
696686
<td>A library to write concurrent code using the async/await syntax</td>
@@ -699,10 +689,6 @@ pipeline {
699689
<td><strong>ASYNCORE</strong></td>
700690
<td>A module provides the basic infrastructure for writing asynchronous socket service clients and servers</td>
701691
</tr>
702-
<tr>
703-
<td><strong>TWISTED</strong></td>
704-
<td>An event-driven networking engine written in Python and licensed under the open source MIT license</td>
705-
</tr>
706692
</table>''')
707693
choice(
708694
name: 'CI_SCHEDULE',

benchmarks/base.py

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,6 @@
6767
except (ImportError, SyntaxError):
6868
pass
6969

70-
have_twisted = False
71-
try:
72-
from cassandra.io.twistedreactor import TwistedConnection
73-
have_twisted = True
74-
supported_reactors.append(TwistedConnection)
75-
except ImportError as exc:
76-
log.exception("Error importing twisted")
77-
pass
78-
7970
KEYSPACE = "testkeyspace" + str(int(time.time()))
8071
TABLE = "testtable"
8172

@@ -230,8 +221,6 @@ def parse_options():
230221
help='only benchmark with asyncio connections')
231222
parser.add_option('--libev-only', action='store_true', dest='libev_only',
232223
help='only benchmark with libev connections')
233-
parser.add_option('--twisted-only', action='store_true', dest='twisted_only',
234-
help='only benchmark with Twisted connections')
235224
parser.add_option('-m', '--metrics', action='store_true', dest='enable_metrics',
236225
help='enable and print metrics for operations')
237226
parser.add_option('-l', '--log-level', default='info',
@@ -271,11 +260,6 @@ def parse_options():
271260
log.error("libev is not available")
272261
sys.exit(1)
273262
options.supported_reactors = [LibevConnection]
274-
elif options.twisted_only:
275-
if not have_twisted:
276-
log.error("Twisted is not available")
277-
sys.exit(1)
278-
options.supported_reactors = [TwistedConnection]
279263
else:
280264
options.supported_reactors = supported_reactors
281265
if not have_libev:

cassandra/cluster.py

Lines changed: 2 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -93,55 +93,11 @@
9393
from cassandra.datastax.graph.query import _request_timeout_key, _GraphSONContextRowFactory
9494
from cassandra.datastax import cloud as dscloud
9595

96-
try:
97-
from cassandra.io.twistedreactor import TwistedConnection
98-
except ImportError:
99-
TwistedConnection = None
100-
101-
try:
102-
from cassandra.io.eventletreactor import EventletConnection
103-
# PYTHON-1364
104-
#
105-
# At the moment eventlet initialization is chucking AttributeErrors due to its dependence on pyOpenSSL
106-
# and some changes in Python 3.12 which have some knock-on effects there.
107-
except (ImportError, AttributeError):
108-
EventletConnection = None
109-
11096
try:
11197
from weakref import WeakSet
11298
except ImportError:
11399
from cassandra.util import WeakSet # NOQA
114100

115-
def _is_gevent_monkey_patched():
116-
if 'gevent.monkey' not in sys.modules:
117-
return False
118-
import gevent.socket
119-
return socket.socket is gevent.socket.socket
120-
121-
def _try_gevent_import():
122-
if _is_gevent_monkey_patched():
123-
from cassandra.io.geventreactor import GeventConnection
124-
return (GeventConnection,None)
125-
else:
126-
return (None,None)
127-
128-
def _is_eventlet_monkey_patched():
129-
if 'eventlet.patcher' not in sys.modules:
130-
return False
131-
try:
132-
import eventlet.patcher
133-
return eventlet.patcher.is_monkey_patched('socket')
134-
# Another case related to PYTHON-1364
135-
except AttributeError:
136-
return False
137-
138-
def _try_eventlet_import():
139-
if _is_eventlet_monkey_patched():
140-
from cassandra.io.eventletreactor import EventletConnection
141-
return (EventletConnection,None)
142-
else:
143-
return (None,None)
144-
145101
def _try_libev_import():
146102
try:
147103
from cassandra.io.libevreactor import LibevConnection
@@ -168,7 +124,7 @@ def _connection_reduce_fn(val,import_fn):
168124

169125
log = logging.getLogger(__name__)
170126

171-
conn_fns = (_try_gevent_import, _try_eventlet_import, _try_libev_import, _try_asyncore_import)
127+
conn_fns = (_try_libev_import, _try_asyncore_import)
172128
(conn_class, excs) = reduce(_connection_reduce_fn, conn_fns, (None,[]))
173129
if not conn_class:
174130
raise DependencyException("Unable to load a default connection class", excs)
@@ -878,19 +834,13 @@ def default_retry_policy(self, policy):
878834
879835
* :class:`cassandra.io.asyncorereactor.AsyncoreConnection`
880836
* :class:`cassandra.io.libevreactor.LibevConnection`
881-
* :class:`cassandra.io.eventletreactor.EventletConnection` (requires monkey-patching - see doc for details)
882-
* :class:`cassandra.io.geventreactor.GeventConnection` (requires monkey-patching - see doc for details)
883-
* :class:`cassandra.io.twistedreactor.TwistedConnection`
884837
* EXPERIMENTAL: :class:`cassandra.io.asyncioreactor.AsyncioConnection`
885838
886839
By default, ``AsyncoreConnection`` will be used, which uses
887840
the ``asyncore`` module in the Python standard library.
888841
889842
If ``libev`` is installed, ``LibevConnection`` will be used instead.
890843
891-
If ``gevent`` or ``eventlet`` monkey-patching is detected, the corresponding
892-
connection class will be used automatically.
893-
894844
``AsyncioConnection``, which uses the ``asyncio`` module in the Python
895845
standard library, is also available, but currently experimental. Note that
896846
it requires ``asyncio`` features that were only introduced in the 3.4 line
@@ -1168,9 +1118,7 @@ def __init__(self,
11681118
raise ValueError("contact_points, endpoint_factory, ssl_context, and ssl_options "
11691119
"cannot be specified with a cloud configuration")
11701120

1171-
uses_twisted = TwistedConnection and issubclass(self.connection_class, TwistedConnection)
1172-
uses_eventlet = EventletConnection and issubclass(self.connection_class, EventletConnection)
1173-
cloud_config = dscloud.get_cloud_config(cloud, create_pyopenssl_context=uses_twisted or uses_eventlet)
1121+
cloud_config = dscloud.get_cloud_config(cloud)
11741122

11751123
ssl_context = cloud_config.ssl_context
11761124
ssl_options = {'check_hostname': True}

cassandra/connection.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,7 @@
2929
import weakref
3030

3131

32-
if 'gevent.monkey' in sys.modules:
33-
from gevent.queue import Queue, Empty
34-
else:
35-
from queue import Queue, Empty # noqa
32+
from queue import Queue, Empty # noqa
3633

3734
from cassandra import ConsistencyLevel, AuthenticationFailed, OperationTimedOut, ProtocolVersion
3835
from cassandra.marshal import int32_pack

cassandra/datastax/cloud/__init__.py

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -78,38 +78,36 @@ def from_dict(cls, d):
7878
return c
7979

8080

81-
def get_cloud_config(cloud_config, create_pyopenssl_context=False):
81+
def get_cloud_config(cloud_config):
8282
if not _HAS_SSL:
8383
raise DriverException("A Python installation with SSL is required to connect to a cloud cluster.")
8484

8585
if 'secure_connect_bundle' not in cloud_config:
8686
raise ValueError("The cloud config doesn't have a secure_connect_bundle specified.")
8787

8888
try:
89-
config = read_cloud_config_from_zip(cloud_config, create_pyopenssl_context)
89+
config = read_cloud_config_from_zip(cloud_config)
9090
except BadZipFile:
9191
raise ValueError("Unable to open the zip file for the cloud config. Check your secure connect bundle.")
9292

9393
config = read_metadata_info(config, cloud_config)
94-
if create_pyopenssl_context:
95-
config.ssl_context = config.pyopenssl_context
9694
return config
9795

9896

99-
def read_cloud_config_from_zip(cloud_config, create_pyopenssl_context):
97+
def read_cloud_config_from_zip(cloud_config):
10098
secure_bundle = cloud_config['secure_connect_bundle']
10199
use_default_tempdir = cloud_config.get('use_default_tempdir', None)
102100
with ZipFile(secure_bundle) as zipfile:
103101
base_dir = tempfile.gettempdir() if use_default_tempdir else os.path.dirname(secure_bundle)
104102
tmp_dir = tempfile.mkdtemp(dir=base_dir)
105103
try:
106104
zipfile.extractall(path=tmp_dir)
107-
return parse_cloud_config(os.path.join(tmp_dir, 'config.json'), cloud_config, create_pyopenssl_context)
105+
return parse_cloud_config(os.path.join(tmp_dir, 'config.json'), cloud_config)
108106
finally:
109107
shutil.rmtree(tmp_dir)
110108

111109

112-
def parse_cloud_config(path, cloud_config, create_pyopenssl_context):
110+
def parse_cloud_config(path, cloud_config):
113111
with open(path, 'r') as stream:
114112
data = json.load(stream)
115113

@@ -123,11 +121,7 @@ def parse_cloud_config(path, cloud_config, create_pyopenssl_context):
123121
ca_cert_location = os.path.join(config_dir, 'ca.crt')
124122
cert_location = os.path.join(config_dir, 'cert')
125123
key_location = os.path.join(config_dir, 'key')
126-
# Regardless of if we create a pyopenssl context, we still need the builtin one
127-
# to connect to the metadata service
128124
config.ssl_context = _ssl_context_from_cert(ca_cert_location, cert_location, key_location)
129-
if create_pyopenssl_context:
130-
config.pyopenssl_context = _pyopenssl_context_from_cert(ca_cert_location, cert_location, key_location)
131125

132126
return config
133127

@@ -178,18 +172,3 @@ def _ssl_context_from_cert(ca_cert_location, cert_location, key_location):
178172

179173
return ssl_context
180174

181-
182-
def _pyopenssl_context_from_cert(ca_cert_location, cert_location, key_location):
183-
try:
184-
from OpenSSL import SSL
185-
except ImportError as e:
186-
raise ImportError(
187-
"PyOpenSSL must be installed to connect to Astra with the Eventlet or Twisted event loops")\
188-
.with_traceback(e.__traceback__)
189-
ssl_context = SSL.Context(SSL.TLSv1_METHOD)
190-
ssl_context.set_verify(SSL.VERIFY_PEER, callback=lambda _1, _2, _3, _4, ok: ok)
191-
ssl_context.use_certificate_file(cert_location)
192-
ssl_context.use_privatekey_file(key_location)
193-
ssl_context.load_verify_locations(ca_cert_location)
194-
195-
return ssl_context

cassandra/datastax/insights/reporter.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,7 @@ def _get_startup_data(self):
144144
cert_validation = None
145145
try:
146146
if self._session.cluster.ssl_context:
147-
if isinstance(self._session.cluster.ssl_context, ssl.SSLContext):
148-
cert_validation = self._session.cluster.ssl_context.verify_mode == ssl.CERT_REQUIRED
149-
else: # pyopenssl
150-
from OpenSSL import SSL
151-
cert_validation = self._session.cluster.ssl_context.get_verify_mode() != SSL.VERIFY_NONE
147+
cert_validation = self._session.cluster.ssl_context.verify_mode == ssl.CERT_REQUIRED
152148
elif self._session.cluster.ssl_options:
153149
cert_validation = self._session.cluster.ssl_options.get('cert_reqs') == ssl.CERT_REQUIRED
154150
except Exception as e:

0 commit comments

Comments
 (0)