Skip to content

Commit 668275b

Browse files
committed
tests: detect stale Cython .so files at test startup
Add a pytest_configure hook in tests/conftest.py that compares mtime of each compiled .so against its .py source and warns when the source is newer. This prevents silently testing stale compiled code after editing a Cython-compiled module without rebuilding. Also document the rebuild requirement in CONTRIBUTING.rst.
1 parent efdc08a commit 668275b

2 files changed

Lines changed: 71 additions & 0 deletions

File tree

CONTRIBUTING.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,16 @@ When modifying driver files, rebuilding Cython modules is often necessary.
4040
Without caching, each such rebuild may take over a minute. Caching usually brings it
4141
down to about 2-3 seconds.
4242

43+
**Important:** After modifying any ``.py`` file under ``cassandra/`` that is
44+
Cython-compiled (such as ``query.py``, ``protocol.py``, ``cluster.py``, etc.),
45+
you **must** rebuild extensions before running tests::
46+
47+
python setup.py build_ext --inplace
48+
49+
Without rebuilding, Python will load the stale compiled extension (``.so`` / ``.pyd``)
50+
instead of your modified ``.py`` source, and your changes will not actually be tested.
51+
The test suite will emit a warning if it detects this situation.
52+
4353
Building the Docs
4454
=================
4555

tests/conftest.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Copyright ScyllaDB, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import glob
16+
import importlib.machinery
17+
import os
18+
import warnings
19+
20+
# Directory containing the Cython-compiled driver modules.
21+
_CASSANDRA_DIR = os.path.join(os.path.dirname(__file__), os.pardir, "cassandra")
22+
23+
24+
def pytest_configure(config):
25+
"""Warn when a compiled Cython extension is older than its .py source.
26+
27+
Python's import system prefers compiled extensions (.so / .pyd) over pure
28+
Python (.py) files. If a developer edits a .py file without rebuilding
29+
the Cython extensions (``python setup.py build_ext --inplace``), the tests
30+
will silently run the *old* compiled code, masking any regressions in the
31+
Python source.
32+
33+
This hook detects such staleness at test-session startup so the developer
34+
is alerted immediately.
35+
"""
36+
seen = set()
37+
stale = []
38+
# Use the current interpreter's extension suffixes so we only check
39+
# extensions that would actually be loaded (correct ABI tag), and
40+
# handle both .so (POSIX) and .pyd (Windows) automatically.
41+
for suffix in importlib.machinery.EXTENSION_SUFFIXES:
42+
for ext_path in glob.glob(os.path.join(_CASSANDRA_DIR, f"*{suffix}")):
43+
module_name = os.path.basename(ext_path).split(".")[0]
44+
if module_name in seen:
45+
continue
46+
py_path = os.path.join(_CASSANDRA_DIR, module_name + ".py")
47+
if os.path.exists(py_path) and os.path.getmtime(py_path) > os.path.getmtime(
48+
ext_path
49+
):
50+
seen.add(module_name)
51+
stale.append((module_name, ext_path, py_path))
52+
53+
if stale:
54+
names = ", ".join(m for m, _, _ in stale)
55+
warnings.warn(
56+
f"Stale Cython extension(s) detected: {names}. "
57+
f"The .py source is newer than the compiled extension — tests "
58+
f"will run the OLD compiled code, not your latest changes. "
59+
f"Rebuild with: python setup.py build_ext --inplace",
60+
stacklevel=1,
61+
)

0 commit comments

Comments
 (0)