Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,4 @@ jobs:
if [[ "${{ matrix.python-version }}" =~ t$ ]]; then
export PYTHON_GIL=0
fi
uv run pytest tests/integration/standard/ tests/integration/cqlengine/
uv run pytest -v tests/integration/standard/ tests/integration/cqlengine/
12 changes: 10 additions & 2 deletions cassandra/cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -3439,7 +3439,7 @@ def pool_finished_setting_keyspace(pool, host_errors):
errors[pool.host] = host_errors

if not remaining_callbacks:
callback(host_errors)
callback(errors)

for pool in tuple(self._pools.values()):
pool._set_keyspace_for_all_conns(keyspace, pool_finished_setting_keyspace)
Expand Down Expand Up @@ -3482,6 +3482,14 @@ def wait_for_schema_agreement(self, wait_time: Optional[float] = None,
if wait_time is not None and wait_time <= 0:
raise ValueError("wait_time must be greater than 0")

try:
scope = SchemaAgreementScope(scope)
except ValueError:
raise ValueError(
"scope must be one of %s" % (
[s.value for s in SchemaAgreementScope],)
)

total_timeout = wait_time if wait_time is not None else self.cluster.max_schema_agreement_wait
if total_timeout <= 0:
raise ValueError("total_timeout must be greater than 0")
Expand Down Expand Up @@ -5325,7 +5333,7 @@ def _execute_after_prepare(self, host, connection, pool, response):
new_metadata_id = response.result_metadata_id
if new_metadata_id is not None:
self.prepared_statement.result_metadata_id = new_metadata_id

# use self._query to re-use the same host and
# at the same time properly borrow the connection
if pool is None and connection is not None and connection.is_control_connection:
Expand Down
34 changes: 30 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ dev = [
"gevent",
"eventlet>=0.33.3",
"cython>=3.2",
"setuptools",
"packaging>=25.0",
"futurist",
"pyyaml",
Expand Down Expand Up @@ -156,21 +157,46 @@ enable = ["pypy"]
[tool.cibuildwheel.linux]

before-build = "rm -rf ~/.pyxbld && rpm --import https://repo.almalinux.org/almalinux/RPM-GPG-KEY-AlmaLinux && yum install -y libffi-devel libev libev-devel openssl openssl-devel"
# Install the optional lz4 compression dependency so the lz4 segment tests run
# (and fail loudly under CASS_DRIVER_NO_SKIP) instead of skipping silently.
test-extras = ["compress-lz4"]
# Extensions are mandatory on Linux (CASS_DRIVER_BUILD_EXTENSIONS_ARE_MUST=yes),
# so skipping is disabled (CASS_DRIVER_NO_SKIP=1): a missing dependency such as
# libev fails loudly instead of being silently skipped. Tests that cannot run in
# the default configuration are listed explicitly:
# * event-loop reactor tests are run separately with the matching
# EVENT_LOOP_MANAGER (gevent/eventlet/asyncio);
# * asyncore is deprecated and unavailable on modern Python, so it is ignored;
# * column_encryption is disabled upstream (scylladb/python-driver#365);
# * test_deserialize_date_range_month is disabled upstream (PYTHON-912).
# eventlet is skipped on PyPy (@notpypy), so CASS_DRIVER_NO_SKIP is not set for it.
test-command = [
"pytest {package}/tests/unit",
"EVENT_LOOP_MANAGER=gevent pytest {package}/tests/unit/io/test_geventreactor.py",
"CASS_DRIVER_NO_SKIP=1 pytest {package}/tests/unit -v --ignore={package}/tests/unit/column_encryption --ignore={package}/tests/unit/io/test_geventreactor.py --ignore={package}/tests/unit/io/test_eventletreactor.py --ignore={package}/tests/unit/io/test_asyncioreactor.py --ignore={package}/tests/unit/io/test_asyncorereactor.py -k 'not test_deserialize_date_range_month'",
"EVENT_LOOP_MANAGER=gevent CASS_DRIVER_NO_SKIP=1 pytest {package}/tests/unit/io/test_geventreactor.py -v",
"EVENT_LOOP_MANAGER=asyncio CASS_DRIVER_NO_SKIP=1 pytest {package}/tests/unit/io/test_asyncioreactor.py -v",
"EVENT_LOOP_MANAGER=eventlet pytest {package}/tests/unit/io/test_eventletreactor.py -v",
]

[tool.cibuildwheel.macos]
build-frontend = "build"
# Install lz4 so the lz4 segment tests run instead of skipping (see Linux note).
test-extras = ["compress-lz4"]
# Same policy as Linux (extensions are mandatory here too, libev comes from
# Homebrew). The extra -k exclusions are timing-sensitive tests that are flaky
# on macOS runners. The gevent/eventlet/asyncio reactor test files only contain
# those timing-sensitive timer tests, so they are not run separately here.
test-command = [
"pytest {project}/tests/unit -k 'not (test_multi_timer_validation or test_empty_connections or test_timer_cancellation)'",
"CASS_DRIVER_NO_SKIP=1 pytest {project}/tests/unit -v --ignore={project}/tests/unit/column_encryption --ignore={project}/tests/unit/io/test_geventreactor.py --ignore={project}/tests/unit/io/test_eventletreactor.py --ignore={project}/tests/unit/io/test_asyncioreactor.py --ignore={project}/tests/unit/io/test_asyncorereactor.py -k 'not (test_multi_timer_validation or test_empty_connections or test_timer_cancellation or test_deserialize_date_range_month)'",
]

[tool.cibuildwheel.windows]
build-frontend = "build"
# On Windows the C extensions are optional (CASS_DRIVER_BUILD_EXTENSIONS_ARE_MUST
# is overridden to "no" below), so extension-dependent tests (e.g. libev) are
# legitimately skipped here. CASS_DRIVER_NO_SKIP is therefore NOT enabled on
# Windows; we only add -v so skips are visible in the log.
test-command = [
"pytest {project}/tests/unit -k \"not (test_deserialize_date_range_year or test_datetype or test_libevreactor)\"",
"pytest {project}/tests/unit -v -k \"not (test_deserialize_date_range_year or test_datetype or test_libevreactor)\"",
]

# TODO: set CASS_DRIVER_BUILD_EXTENSIONS_ARE_MUST to yes when https://github.com/scylladb/python-driver/issues/429 is fixed
Expand Down
34 changes: 34 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,43 @@
import os
import warnings

import pytest

# Directory containing the Cython-compiled driver modules.
_CASSANDRA_DIR = os.path.join(os.path.dirname(__file__), os.pardir, "cassandra")

# When set (e.g. in CI) a skipped test is turned into a failure. Tests skip
# themselves when their requirements are missing (a library is not installed,
# the wrong event loop is selected, ...). That is convenient locally, but in CI
# it is a footgun: a test may be silently skipped because we forgot to install
# something. Enabling this forces every skip to be explicit on the command line
# (via -k / --ignore / --deselect) instead of being hidden in the output.
_NO_SKIP = bool(os.environ.get("CASS_DRIVER_NO_SKIP"))


@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
"""Turn skips into failures when CASS_DRIVER_NO_SKIP is set.

xfailed tests (which are reported as skipped) are left untouched so that
``xfail_strict`` keeps working as configured.
"""
outcome = yield
if not _NO_SKIP:
return
report = outcome.get_result()
if report.skipped and not hasattr(report, "wasxfail"):
reason = ""
if isinstance(report.longrepr, tuple) and len(report.longrepr) == 3:
reason = report.longrepr[2]
report.outcome = "failed"
report.longrepr = (
"Test was skipped but skipping is disabled in this environment "
"(CASS_DRIVER_NO_SKIP is set). Run it in a suitable configuration "
"or deselect it explicitly on the command line. "
"Original skip reason: {!r}".format(reason)
)


def pytest_configure(config):
"""Warn when a compiled Cython extension is older than its .py source.
Expand Down
4 changes: 4 additions & 0 deletions tests/unit/io/test_libevreactor.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ def test_watchers_are_finished(self):
assert conn._read_watcher.stop.mock_calls

_global_loop._shutdown = False
# _cleanup stopped the prepare watcher; restart it so the shared
# singleton loop is left in a working state for subsequent tests
# (otherwise timers would never be scheduled and tests would hang).
_global_loop._preparer.start()


class LibevTimerPatcher(unittest.TestCase):
Expand Down
Loading