Skip to content

(cleanup) remove Python 2 remaining items - #727

Draft
mykaul wants to merge 11 commits into
scylladb:masterfrom
mykaul:python_2_no_more
Draft

(cleanup) remove Python 2 remaining items#727
mykaul wants to merge 11 commits into
scylladb:masterfrom
mykaul:python_2_no_more

Conversation

@mykaul

Copy link
Copy Markdown

Pre-review checklist

This is 100% OpenCode's work. So take it with a grain of salt, and I need to go over it. I can also cherry-pick each one separately. I've asked it to separate as much as possible to independent items.

  • I have split my patch into logically separate commits.
  • All commit messages clearly explain what they change and why.
  • I added relevant tests for new features and bug fixes.
  • All commits compile, pass static checks and pass test.
  • PR description sums up the changes and reasons why they should be introduced.
  • I have provided docstrings for the public items that I want to introduce.
  • I have adjusted the documentation in ./docs/source/.
  • I added appropriate Fixes: annotations to PR description.

@mykaul
mykaul marked this pull request as draft March 4, 2026 19:57
@mykaul
mykaul requested a review from CopilotMarch 4, 2026 19:57

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR continues the Python 2 cleanup by removing remaining unicode/UnicodeMixin compatibility shims and updating tests/docs/code paths to assume Python 3-only semantics (the project now requires Python >=3.9).

Changes:

  • Removes Python 2-era unicode patterns (u'', __unicode__, UnicodeMixin) and normalizes string handling across driver and cqlengine.
  • Simplifies Python-version conditionals/fallback imports (e.g., WeakSet imports) and applies formatting-only refactors in several modules/tests.
  • Updates unit/integration tests and Sphinx config to reflect Python 3-only behavior and representations.

Reviewed changes

Copilot reviewed 34 out of 37 changed files in this pull request and generated 6 comments.

Show a summary per file
FileDescription
tests/unit/test_types.pyReplaces u'' literals with plain str in type read/write tests.
tests/unit/test_row_factories.pyRemoves Python 3.0–3.6 conditional expectations for namedtuple creation.
tests/unit/test_orderedmap.pyUpdates unicode-key tests to Python 3 str keys.
tests/unit/test_metadata.pyReplaces u'' literals with str in metadata CQL export tests.
tests/unit/test_marshalling.pyUpdates UTF8/unicode expectations to Python 3 str and cleans up ordered map inserts.
tests/unit/advanced/test_insights.pyRemoves Python-version-specific namespace logic and reformats expected dicts.
tests/integration/standard/test_query.pyUpdates unicode query strings/column names to Python 3 str.
tests/integration/standard/test_cluster.pyUpdates expected row tuples to Python 3 str.
tests/integration/cqlengine/model/test_udts.pyUpdates unicode literals to Python 3 str.
tests/integration/cqlengine/model/test_model_io.pyUpdates unicode literals to Python 3 str in model IO assertions.
tests/integration/cqlengine/model/test_class_construction.pyMostly formatting + Python 3 iterator usage (next(iter(...))) and string literal normalization.
tests/integration/cqlengine/columns/test_validation.pyRemoves old Python-version branches and normalizes string usage/formatting in validation tests.
setup.pyRemoves Python 2-era subprocess gating and refactors extension/doc build setup logic.
docs/conf.pyNormalizes string literals and formatting in Sphinx configuration.
cassandra/query.pyPython 3 string/formatting cleanup; keeps namedtuple fallback paths but modernizes literals/layout.
cassandra/pool.pyRemoves legacy WeakSet fallback and reformats/shard-aware related code blocks.
cassandra/io/asyncorereactor.pyRemoves legacy WeakSet fallback and modernizes literals/formatting.
cassandra/encoder.pyDeprecates Python 2 “unicode” semantics and standardizes encoding/quoting behavior for Python 3.
cassandra/datastax/graph/query.pyPython 3 string/formatting cleanup and minor readability refactors.
cassandra/datastax/graph/graphson.pyPython 3 cleanup + formatting; updates docs/comments describing supported Python types.
cassandra/datastax/graph/fluent/_query.pyPython 3 string/formatting cleanup and improves readability of traversal query generation.
cassandra/cqlengine/statements.pyRemoves UnicodeMixin usage and converts __unicode__ implementations to __str__.
cassandra/cqlengine/operators.pyRemoves UnicodeMixin usage and converts operator stringification to __str__.
cassandra/cqlengine/named.pyConverts __unicode__ to __str__ and normalizes string literals.
cassandra/cqlengine/models.pyRemoves UnicodeMixin usage and normalizes string literals/formatting across model machinery.
cassandra/cqlengine/functions.pyRemoves UnicodeMixin usage and converts __unicode__ to __str__.
cassandra/cqlengine/init.pyRemoves UnicodeMixin definition entirely.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadsetup.py Outdated
Comment threadsetup.py Outdated
Comment threadsetup.py Outdated
Comment threadcassandra/datastax/graph/graphson.py Outdated
Comment threadtests/unit/test_row_factories.py
Comment threadtests/unit/test_orderedmap.py
@mykaul

Copy link
Copy Markdown
Author

Fixed all comments.

@mykaul

Copy link
Copy Markdown
Author

@copilot code review[agent] - please re-review

@mykaul

Copy link
Copy Markdown
Author

Review: Rebased onto master, Python 3 expert review

Rebase Status

Rebased all 18 commits onto current master. One conflict in cassandra/cluster.py (tablets routing ctype caching vs. quote-style change) — resolved by keeping master's caching pattern with the PR's double-quote formatting. All unit tests pass.

Review Findings

The 18 commits are semantically correct. The bulk of the diff is formatting (quote normalization, import reformatting). All major semantic changes verified:

  • __unicode____str__: Complete. No remaining __unicode__, UnicodeMixin, or __nonzero__ references.
  • WeakSet removal: All 3 import sites correctly use from weakref import WeakSet. Custom implementation fully removed.
  • filter() materialization: All 3 sites correctly wrap filter() in list() where the result needs indexing or .pop().
  • u'' prefix removal: Complete.
  • from __future__ import absolute_import: Complete.
  • cql_encode_unicode fix: Old code cql_quote(val.encode('utf-8')) would produce broken results on Py3 (quoting bytes). Fix to cql_quote(val) is correct.

Issues Found

#FileLineSeverityFinding
1cassandra/cluster.py5704MediumPre-existing bug: query_id.encode("hex") is a Python 2 idiom that raises TypeError on Python 3 (bytes has no .encode() that accepts "hex"). Should be query_id.hex(). This was not introduced by this PR (only reformatted), but since this PR is specifically about Py3 cleanup, it should be fixed here.
2cassandra/util.py1624LowMissed stale TODO: TODO: when python2 support is removed, use packaging.version. — Python 2 support is being removed by this very PR. This TODO should either be acted on or updated. The "Update stale Python 2 comments" commit did not catch it.
3cassandra/io/asyncioreactor.py95LowMissed cleanup: if sys.version_info[0] == 3 and sys.version_info[1] < 10: — the version_info[0] == 3 check is now redundant since minimum Python is 3.9+. Should be simplified to if sys.version_info < (3, 10):. This file was not modified by the PR.

Finding #1 is the most important — it's a latent runtime error that would crash if an unknown prepared statement is executed (error logging path).

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 35 out of 38 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadcassandra/encoder.py
Comment threadsetup.py
@mykaul
mykaulforce-pushed the python_2_no_more branch 2 times, most recently from 4778136 to 42e3d08CompareApril 10, 2026 15:35
CopilotAI review requested due to automatic review settings July 29, 2026 20:31
@coderabbitai

coderabbitaiBot commented Jul 29, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: cb643e66-db08-4831-9aa0-b019a8239ed3

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@mykaul

Copy link
Copy Markdown
Author

Rebased onto current master (11 commits, unchanged count) and did another pass:

Rebase: two real conflicts, both from unrelated master churn — cassandra/cqlengine/query.py (master's own 766898db4 had independently dropped the unused BatchStatement import from the same line this PR touches for UnicodeMixin removal; kept both removals) and tests/integration/standard/test_metadata.py (context-adjacent to the test_export_keyspace_schema_udts removal; master hadn't touched that test, so the removal applied as intended).

Fixed 3 issues introduced by the rebase/rewrite itself (pyflakes before/after diff on every touched file caught these):

  • tests/integration/standard/test_metadata.py and tests/integration/cqlengine/columns/test_validation.py: dropped now-unused import sys (and unused assert_startswith_diff import) left behind after removing the Python-2-only test paths that used them.
  • cassandra/io/asyncorereactor.py / cassandra/pool.py: restored the # noqa marker on the WeakSet import that got dropped when the try/except fallback was collapsed to a single line (pure lint-annotation parity with the original code, not a behavior change).

Addressed the "Issues Found" from the earlier review comment on this PR:

  • query_id.encode('hex') in cluster.py (ResponseFuture's unknown-prepared-statement error-logging path) — genuine Python 2 idiom, bytes has no .encode() in Python 3, would raise AttributeError if that path is ever hit. Fixed to query_id.hex(), folded into the "redundant Python version checks" commit.
  • The cassandra/util.py stale TODO: when python2 support is removed... was already updated by the "docs: update stale Python 2 comments" commit.
  • Left cassandra/io/asyncioreactor.py's sys.version_info[0] == 3 and sys.version_info[1] < 10 alone — it's a harmless (not broken) redundant check in a file this PR doesn't otherwise touch; simplifying it felt like unrelated scope creep for this PR.

Verified, not fixed (checked against current master, no action needed):

  • The 8 review threads on this PR are all already resolved; re-checked each against the current diff, still valid/still fixed.
  • Circular-import risk around cassandra.cython_deps: this PR doesn't touch cython_deps.py/deserializers.pyx/the HAVE_NUMPY import chain at all. Verified HAVE_CYTHON/HAVE_NUMPY report True consistently regardless of which cassandra submodule is imported first.
  • CI: the two "Test wheels building" runs on this PR both ended in startup_failure (a workflow-level failure, not a test failure) — confirmed the same happens on unrelated PRs/branches too, so it's environment/workflow-config noise, not something introduced here. "Integration tests" and "Docs / Build PR" are green.

Full tests/unit/ suite: 720 passed, 88 skipped, 0 failed. import cassandra and the touched submodules import cleanly.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 36 out of 36 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (7)

tests/unit/test_row_factories.py:1

  • The test asserts no warnings unconditionally, but the docstring explicitly says the bug was fixed in Python 3.7+. If your supported matrix still includes Python < 3.7, this will fail on those runtimes. Consider either (a) skipping/branching the assertion based on sys.version_info, or (b) updating the test to enforce warnings.simplefilter("always") inside the context and asserting the expected warning only for affected versions; also re-add an assertion that field access works (e.g., rows[0].col0) to keep the behavioral coverage from the prior test.
    tests/unit/test_orderedmap.py:1
  • These assertions became tautological after removing u'' literals, so the test no longer validates the normalization behavior described by PYTHON-231. To preserve intent under Python 3, use two distinct but equivalent key representations that the type layer normalizes (e.g., bytes vs str keys if supported by the serializer) and assert they resolve to the same stored entry.
    tests/unit/test_orderedmap.py:1
  • These assertions became tautological after removing u'' literals, so the test no longer validates the normalization behavior described by PYTHON-231. To preserve intent under Python 3, use two distinct but equivalent key representations that the type layer normalizes (e.g., bytes vs str keys if supported by the serializer) and assert they resolve to the same stored entry.
    tests/integration/standard/test_metadata.py:1122
  • The Python 2-only UDT export integration test (test_export_keyspace_schema_udts) was removed entirely, which drops coverage for schema export of UDTs. Consider porting it to Python 3 by making the comparison deterministic (e.g., avoid relying on dict iteration order by parsing/normalizing the exported CQL, or comparing against multiple expected fragments in an order-insensitive way) so the behavior remains covered without requiring Python 2.7.
 @greaterthancass21
def test_case_sensitivity(self):
"""

cassandra/cluster.py:1611

  • The bare except: will swallow unexpected failures (e.g., runtime errors inside eventletreactor, TypeError if self.connection_class is not a class), making debugging harder and potentially masking real regressions. Prefer catching the expected exceptions explicitly (typically ImportError, and optionally TypeError for issubclass) and letting other exceptions propagate.
 try:
from cassandra.io.eventletreactor import EventletConnection
is_eventlet = issubclass(self.connection_class, EventletConnection)
except:
# Eventlet is not available or can't be detected
return tpe_class(**kwargs)

cassandra/encoder.py:110

  • The Sphinx deprecated directive normally requires a version argument (.. deprecated:: X.Y). As written, doc builds may emit warnings or fail depending on your Sphinx settings. Provide the deprecation version (or switch to a narrative note if you don’t want to formally deprecate the API).
 Encodes a string value with quote escaping.
.. deprecated::
This method is unused internally since Python 2 support was
removed (``str`` is always unicode on Python 3). It is kept
for backward compatibility with user subclasses of
:class:`Encoder`.

setup.py:111

  • The function name get_subdriname appears to be a typo, which makes it harder to discover/understand. If it’s part of a public/internal API used elsewhere, consider introducing a correctly named wrapper (e.g., get_subdirname) and keeping the old name as a backward-compatible alias with a deprecation path.
def get_subdriname(directory_path):
try:
# List only subdirectories in the given directory
subdirectories = [name for name in os.listdir(directory_path)
if os.path.isdir(os.path.join(directory_path, name))]

CopilotAI review requested due to automatic review settings July 29, 2026 20:48

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 36 out of 36 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (9)

tests/unit/test_orderedmap.py:1

  • These assertions became tautological after removing u'' literals and no longer validate the intended normalization behavior (PYTHON-231). To keep the test meaningful on Python 3, consider asserting equivalence across different input types that serialize identically for the key type (e.g., str keys vs bytes keys for UTF8 serialization), so the test still exercises the normalization path.
    tests/unit/test_orderedmap.py:1
  • These assertions became tautological after removing u'' literals and no longer validate the intended normalization behavior (PYTHON-231). To keep the test meaningful on Python 3, consider asserting equivalence across different input types that serialize identically for the key type (e.g., str keys vs bytes keys for UTF8 serialization), so the test still exercises the normalization path.
    tests/unit/test_row_factories.py:1
  • This test now unconditionally asserts that no warnings are emitted for >255 columns. Previously it handled Python <3.7 where collections.namedtuple had the 255-fields limitation (PYTHON-893). If the supported runtime matrix still includes Python 3.6 (or older), this will fail. Consider restoring a version guard/skip (or asserting the fallback behavior) unless the project minimum Python version is 3.7+.
    tests/unit/test_row_factories.py:1
  • This test now unconditionally asserts that no warnings are emitted for >255 columns. Previously it handled Python <3.7 where collections.namedtuple had the 255-fields limitation (PYTHON-893). If the supported runtime matrix still includes Python 3.6 (or older), this will fail. Consider restoring a version guard/skip (or asserting the fallback behavior) unless the project minimum Python version is 3.7+.
    cassandra/util.py:232
  • The custom WeakSet implementation was removed from cassandra.util. If cassandra.util.WeakSet was part of the library's de-facto public surface (even if undocumented), this is a breaking change for downstream imports. Consider re-exporting weakref.WeakSet under the same name (possibly with a deprecation period) to preserve backward compatibility while still removing the Python 2 fallback implementation.
class SortedSet(object):
'''

cassandra/cqlengine/init.py:27

  • Removing UnicodeMixin can be a breaking change for external users who may import it or subclass cqlengine components expecting its __str__ behavior. If this project maintains compatibility guarantees, consider keeping a minimal deprecated UnicodeMixin alias (or migration shim) for at least one release cycle.
class ValidationError(CQLEngineException):
pass

cassandra/encoder.py:110

  • Sphinx's deprecated directive typically requires a version argument (e.g., .. deprecated:: 3.x). As written, .. deprecated:: without a version may fail doc builds or render incorrectly. Consider adding the appropriate version (or replacing with plain text if you don't want it treated as a formal deprecation directive).
 Encodes a string value with quote escaping.
.. deprecated::
This method is unused internally since Python 2 support was
removed (``str`` is always unicode on Python 3). It is kept
for backward compatibility with user subclasses of
:class:`Encoder`.

cassandra/cqltypes.py:507

  • This changes AsciiType.serialize behavior to explicitly accept bytes (and to always encode('ascii') for str). Please add/adjust unit tests to cover both bytes and str inputs (including non-ASCII str raising as expected), since this is a functional behavior change from prior Python 3 behavior.
 if isinstance(var, bytes):
return var
return var.encode('ascii')

cassandra/cqltypes.py:784

  • Similarly, UTF8Type.serialize now explicitly accepts bytes and encodes str. Adding focused unit coverage here (bytes passthrough, str encoding, and common edge cases) would help prevent regressions in marshalling/encoding behavior.
 if isinstance(ustr, bytes):
return ustr
return ustr.encode('utf-8')

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
setup.py (1)

80-81: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Preserve subprocess failure causes.

Raise both RuntimeError instances with from exc so traceback tooling retains the original CalledProcessError as the direct cause.

Proposed fix
- raise RuntimeError("Documentation step '%s' failed: %s: %s" % ("build_ext", exc, exc.output))+ raise RuntimeError("Documentation step '%s' failed: %s: %s" % ("build_ext", exc, exc.output)) from exc
...
- raise RuntimeError("Documentation step '%s' failed: %s: %s" % (mode, exc, exc.output))+ raise RuntimeError("Documentation step '%s' failed: %s: %s" % (mode, exc, exc.output)) from exc

Also applies to: 89-90

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@setup.py` around lines 80 - 81, Update both RuntimeError raises in the
subprocess failure handlers to explicitly chain the original CalledProcessError
using from exc, preserving each exception as the direct traceback cause while
keeping the existing error messages unchanged.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@cassandra/cluster.py`:
- Around line 1606-1624: Update the exception handler around the
EventletConnection import and issubclass check in the executor-selection flow to
catch only ImportError, and explicitly chain the re-raise when propagating it.
Preserve the fallback for Eventlet being unavailable, while allowing
KeyboardInterrupt, SystemExit, and unrelated import or detection failures to
surface instead of silently returning the default executor.
In `@cassandra/datastax/graph/graphson.py`:
- Around line 61-65: The type mapping table contains two conflicting date
entries; update the duplicate date rows so their graph-mode applicability is
explicitly distinguished, following the existing duration annotation, or
consolidate them into one unambiguous mapping while preserving the correct
Python type for each mode.
---
Nitpick comments:
In `@setup.py`:
- Around line 80-81: Update both RuntimeError raises in the subprocess failure
handlers to explicitly chain the original CalledProcessError using from exc,
preserving each exception as the direct traceback cause while keeping the
existing error messages unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 49211c83-fb63-4e7c-a4d3-2d3a83eaef7e

📥 Commits

Reviewing files that changed from the base of the PR and between b8b714c and f3fa55f.

📒 Files selected for processing (36)
  • cassandra/cluster.py
  • cassandra/connection.py
  • cassandra/cqlengine/__init__.py
  • cassandra/cqlengine/functions.py
  • cassandra/cqlengine/models.py
  • cassandra/cqlengine/named.py
  • cassandra/cqlengine/operators.py
  • cassandra/cqlengine/query.py
  • cassandra/cqlengine/statements.py
  • cassandra/cqltypes.py
  • cassandra/datastax/graph/fluent/_query.py
  • cassandra/datastax/graph/graphson.py
  • cassandra/datastax/graph/query.py
  • cassandra/encoder.py
  • cassandra/io/asyncorereactor.py
  • cassandra/metadata.py
  • cassandra/pool.py
  • cassandra/protocol.py
  • cassandra/query.py
  • cassandra/util.py
  • setup.py
  • tests/integration/cqlengine/columns/test_validation.py
  • tests/integration/cqlengine/model/test_class_construction.py
  • tests/integration/cqlengine/model/test_model_io.py
  • tests/integration/cqlengine/model/test_udts.py
  • tests/integration/cqlengine/query/test_queryset.py
  • tests/integration/standard/test_cluster.py
  • tests/integration/standard/test_metadata.py
  • tests/integration/standard/test_query.py
  • tests/integration/standard/test_types.py
  • tests/unit/advanced/test_insights.py
  • tests/unit/test_marshalling.py
  • tests/unit/test_metadata.py
  • tests/unit/test_orderedmap.py
  • tests/unit/test_row_factories.py
  • tests/unit/test_types.py
💤 Files with no reviewable changes (4)
  • cassandra/cqlengine/init.py
  • cassandra/connection.py
  • tests/integration/cqlengine/query/test_queryset.py
  • cassandra/protocol.py

Comment threadcassandra/cluster.py Outdated
Comment threadcassandra/datastax/graph/graphson.py Outdated
mykaul added 2 commits July 30, 2026 12:48
absolute_import is the default behavior in Python 3 and has been since
Python 3.0. The import was only needed for Python 2 compatibility.
UnicodeMixin was a Python 2 compatibility shim that made __str__
delegate to __unicode__. In Python 3, __str__ is the native method
and UnicodeMixin serves no purpose.
Affected classes: QueryValue, BaseQueryOperator, AbstractQueryableColumn,
ValueQuoter, BaseClause, BaseCQLStatement.
mykaul added 3 commits July 30, 2026 12:48
In Python 3, __str__ is the native string method. The __unicode__
methods were a Python 2 convention used together with UnicodeMixin.
Affected files: statements.py, operators.py, named.py, models.py,
query.py, functions.py.
__nonzero__ was the Python 2 name for the boolean conversion method.
Python 3 uses __bool__ directly.
weakref.WeakSet has been available since Python 2.7 and is always
present in Python 3. Remove the try/except fallback imports in
cluster.py, pool.py, and asyncorereactor.py, and delete the custom
_IterationGuard and WeakSet classes from util.py (~210 lines).
CopilotAI review requested due to automatic review settings July 30, 2026 09:54
@mykaul

Copy link
Copy Markdown
Author

CI note: the only real (non-flaky-NLB) failure here was on Test wheels building / Build wheels for macos-arm on macos-14:

tests/unit/test_host_connection_pool.py::HostConnectionTests::test_successful_wait_for_connection FAILED
E TypeError: __hash__ method should return an integer not 'MagicMock'

(job run)

This is a pre-existing, unrelated flaky-test bug in HashableMock (tests/unit/util.py) — MagicMixin.__init__ replaces __hash__ on the mock's type with a thread-unsafe MagicMock, which can race under concurrent hash() calls (e.g. connection in self._trash in pool.return_connection). It has nothing to do with this PR's Python-2-cleanup diff.

It's already fixed on #766 (fix/hashable-mock-thread-safety), which hasn't merged yet. To unblock this PR's CI without waiting on #766, I've ported that same fix here (amended into the last commit). Verified locally:

  • 2000+ iterations of the target test under an aggressive thread-switch interval (sys.setswitchinterval) with 0 failures with the fix applied, after first reproducing the original failure under the same conditions without it.
  • Full tests/unit/ suite: 720 passed, 88 skipped, 0 failed.

#766 remains the canonical fix for this bug and should still land on its own for traceability/history; this is just a duplicate application to get this PR's CI green in the meantime.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 37 out of 37 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

cassandra/encoder.py:106

  • Sphinx's deprecated directive requires a version argument. Because this method is explicitly included by docs/api/cassandra/encoder.rst, the warning-as-error documentation build will fail while parsing this docstring. Add the release version, consistent with the other deprecation directive in cluster.py.
 .. deprecated::

CopilotAI review requested due to automatic review settings July 30, 2026 12:14

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 37 out of 37 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (4)

cassandra/cluster.py:1608

  • This import can raise AttributeError when an incompatible Eventlet is installed (the module-level Eventlet probe already handles that exact Python 3.12 failure at lines 106–111). Because every Cluster creates its executor through this path, such an installation now prevents even non-Eventlet clusters from being constructed instead of falling back to ThreadPoolExecutor. Handle AttributeError here as well (or reuse the safely probed module-level EventletConnection).
 except ImportError:

cassandra/encoder.py:110

  • Sphinx's deprecated directive requires a version argument. This method is explicitly included by docs/api/cassandra/encoder.rst, and the docs CI builds with warnings as errors, so the argument-less directive will fail the documentation build. Add the release in which this method is deprecated (or use a non-versioned note if it is not formally deprecated).
 .. deprecated::
This method is unused internally since Python 2 support was
removed (``str`` is always unicode on Python 3). It is kept
for backward compatibility with user subclasses of
:class:`Encoder`.

cassandra/datastax/graph/graphson.py:59

  • The standard-library address classes are named IPv4Address and IPv6Address; the all-caps IPV spelling makes the updated type table inaccurate.
inet | gx:InetAddress | gx:InetAddress | str, IPV4Address/IPV6Address

tests/unit/test_row_factories.py:47

  • Use “column lists” rather than “columns lists.”

mykaul added 6 commits July 30, 2026 23:20
… 3.9+
- cluster.py: remove 'if sys.version_info >= 3.7' guard around eventlet
check (always true since we require Python 3.9+). Update error message.
- cluster.py: fix latent bug in ResponseFuture's unknown-prepared-statement
error path: bytes.encode('hex') is a Python 2 idiom that raises
AttributeError on Python 3 bytes objects (no encode() method); use
bytes.hex() instead.
- test_insights.py: remove 'if sys.version_info > (3,)' guard around
namespace suffix (always true).
- test_row_factories.py: remove NAMEDTUPLE_CREATION_BUG flag and
Python 3.0-3.6 warning path (bug was fixed in Python 3.7).
subprocess has been part of the standard library since Python 2.4.
Also fixes:
- Use sys.executable instead of hardcoded 'python' for subprocess calls
- Use 'python -m sphinx' instead of 'sphinx-build' for docs
- Fix get_subdriname() bug: was iterating over characters of a string
path instead of calling os.listdir() on the path
- Remove duplicate is_macos definition
In Python 3, the u'' prefix on string literals is a no-op (str is
always unicode). Remove 150 occurrences across 15 files.
Also update cql_encode_unicode() in encoder.py to pass the string
directly to cql_quote() instead of encoding to UTF-8 bytes first,
since Python 3 strings are always unicode.
- test_validation.py: remove 'if sys.version_info < (3, 1)' blocks that
tested unichr() (only available in Python 2). Also fix 'class
DataType():' to 'class DataType:'.
- test_metadata.py: delete test_export_keyspace_schema_udts which was
skipped on all Python versions except 2.7.
The workaround pre-loaded the UTF-8 encoding module to avoid a deadlock
when importing from multiple threads. This bug was fixed in Python 3.3.
Also ports the HashableMock thread-safety fix from PR scylladb#766
(fix/hashable-mock-thread-safety) to unblock this PR's own CI: the
"Build wheels for macos-arm on macos-14" job was failing on
tests/unit/test_host_connection_pool.py::HostConnectionTests::test_successful_wait_for_connection
with `TypeError: __hash__ method should return an integer not
'MagicMock'`. This is a pre-existing, unrelated flaky-test bug (not
introduced by this Python-2-cleanup PR); PR scylladb#766 is the canonical fix
and should still land separately.
MagicMixin.__init__ replaces __hash__ on the mock's type with a
MagicMock object, which is not thread-safe under concurrent hash()
calls (e.g. `connection in self._trash` in pool.py). Fix by restoring
a plain, id-based __hash__ function on the class after
super().__init__() runs.
CopilotAI review requested due to automatic review settings July 30, 2026 20:20

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 37 out of 37 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (4)

cassandra/encoder.py:106

  • The Sphinx deprecated directive requires a version argument. Because this method is included by docs/api/cassandra/encoder.rst, the new argument-less directive makes the documentation build fail instead of rendering the deprecation notice.
 .. deprecated::

cassandra/cluster.py:1608

  • This local import only catches ImportError, but the module-level import explicitly also catches AttributeError because incompatible Eventlet releases raise it on Python 3.12. Since this method runs during every Cluster initialization, merely having that Eventlet version installed will now break clusters that do not use Eventlet instead of falling back to the standard executor. Handle AttributeError here as well (or reuse the already guarded module-level import).
 except ImportError:

tests/unit/test_orderedmap.py:185

  • These assertions now compare each lookup with the identical expression, so they can pass even if OrderedMapSerializedKey stops normalizing equivalent key representations. Use bytes versus str, which serialize to the same UTF8 key under Python 3, to preserve the normalization regression coverage.
    tests/unit/test_types.py:328
  • After removing the u prefix, this assertion is identical to the following line and no longer exercises a distinct case. Keep only one copy.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@mykaul