Skip to content

Commit ae981e6

Browse files
committed
Revert send_body() to accept protocol_version as parameter
Instead of setting msg.protocol_version externally and having send_body() access it as an instance variable, revert to the more explicit pattern of passing protocol_version as a parameter to send_body(). This makes the dependency more explicit and easier to understand. Changes: - Updated all send_body(self, f) methods to send_body(self, f, protocol_version) - Updated _write_query_params to accept and use protocol_version parameter - Updated encode_message to pass protocol_version to send_body instead of setting it on message - Updated all test files to pass protocol_version parameter to send_body - Rebuilt C extensions to reflect the changes
1 parent 7de86d8 commit ae981e6

File tree

8 files changed

+2580
-54
lines changed

8 files changed

+2580
-54
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
---
2+
description: 'Python coding conventions and guidelines'
3+
applyTo: '**/*.py'
4+
---
5+
6+
# Python Coding Conventions
7+
8+
## Python Instructions
9+
10+
- Write clear and concise comments for each function.
11+
- Ensure functions have descriptive names and include type hints.
12+
- Provide docstrings following PEP 257 conventions.
13+
- Use the `typing` module for type annotations (e.g., `List[str]`, `Dict[str, int]`).
14+
- Break down complex functions into smaller, more manageable functions.
15+
16+
## General Instructions
17+
18+
- Always prioritize readability and clarity.
19+
- For algorithm-related code, include explanations of the approach used.
20+
- Write code with good maintainability practices, including comments on why certain design decisions were made.
21+
- Handle edge cases and write clear exception handling.
22+
- For libraries or external dependencies, mention their usage and purpose in comments.
23+
- Use consistent naming conventions and follow language-specific best practices.
24+
- Write concise, efficient, and idiomatic code that is also easily understandable.
25+
26+
## Code Style and Formatting
27+
28+
- Follow the **PEP 8** style guide for Python.
29+
- Maintain proper indentation (use 4 spaces for each level of indentation).
30+
- Ensure lines do not exceed 79 characters.
31+
- Place function and class docstrings immediately after the `def` or `class` keyword.
32+
- Use blank lines to separate functions, classes, and code blocks where appropriate.
33+
34+
## Edge Cases and Testing
35+
36+
- Always include test cases for critical paths of the application.
37+
- Account for common edge cases like empty inputs, invalid data types, and large datasets.
38+
- Include comments for edge cases and the expected behavior in those cases.
39+
- Write unit tests for functions and document them with docstrings explaining the test cases.
40+
41+
## Example of Proper Documentation
42+
43+
```python
44+
def calculate_area(radius: float) -> float:
45+
"""
46+
Calculate the area of a circle given the radius.
47+
48+
Parameters:
49+
radius (float): The radius of the circle.
50+
51+
Returns:
52+
float: The area of the circle, calculated as π * radius^2.
53+
"""
54+
import math
55+
return math.pi * radius ** 2
56+
```

cassandra/protocol.py

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ def __init__(self, cqlversion, options):
414414
self.cqlversion = cqlversion
415415
self.options = options
416416

417-
def send_body(self, f):
417+
def send_body(self, f, protocol_version):
418418
optmap = self.options.copy()
419419
optmap['CQL_VERSION'] = self.cqlversion
420420
write_stringmap(f, optmap)
@@ -449,8 +449,8 @@ class CredentialsMessage(_MessageType):
449449
def __init__(self, creds):
450450
self.creds = creds
451451

452-
def send_body(self, f):
453-
if self.protocol_version > 1:
452+
def send_body(self, f, protocol_version):
453+
if protocol_version > 1:
454454
raise UnsupportedOperation(
455455
"Credentials-based authentication is not supported with "
456456
"protocol version 2 or higher. Use the SASL authentication "
@@ -480,7 +480,7 @@ class AuthResponseMessage(_MessageType):
480480
def __init__(self, response):
481481
self.response = response
482482

483-
def send_body(self, f):
483+
def send_body(self, f, protocol_version):
484484
write_longstring(f, self.response)
485485

486486

@@ -500,7 +500,7 @@ class OptionsMessage(_MessageType):
500500
opcode = 0x05
501501
name = 'OPTIONS'
502502

503-
def send_body(self, f):
503+
def send_body(self, f, protocol_version):
504504
pass
505505

506506

@@ -548,7 +548,7 @@ def __init__(self, query_params, consistency_level,
548548
self.skip_meta = skip_meta
549549
self.keyspace = keyspace
550550

551-
def _write_query_params(self, f):
551+
def _write_query_params(self, f, protocol_version):
552552
write_consistency_level(f, self.consistency_level)
553553
flags = 0x00
554554
if self.query_params is not None:
@@ -567,14 +567,14 @@ def _write_query_params(self, f):
567567
flags |= _PROTOCOL_TIMESTAMP_FLAG
568568

569569
if self.keyspace is not None:
570-
if ProtocolVersion.uses_keyspace_flag(self.protocol_version):
570+
if ProtocolVersion.uses_keyspace_flag(protocol_version):
571571
flags |= _WITH_KEYSPACE_FLAG
572572
else:
573573
raise UnsupportedOperation(
574574
"Keyspaces may only be set on queries with protocol version "
575575
"5 or DSE_V2 or higher. Consider setting Cluster.protocol_version.")
576576

577-
if ProtocolVersion.uses_int_query_flags(self.protocol_version):
577+
if ProtocolVersion.uses_int_query_flags(protocol_version):
578578
write_uint(f, flags)
579579
else:
580580
write_byte(f, flags)
@@ -609,9 +609,9 @@ def __init__(self, query, consistency_level, serial_consistency_level=None,
609609
super(QueryMessage, self).__init__(None, consistency_level, serial_consistency_level, fetch_size,
610610
paging_state, timestamp, False, continuous_paging_options, keyspace)
611611

612-
def send_body(self, f):
612+
def send_body(self, f, protocol_version):
613613
write_longstring(f, self.query)
614-
self._write_query_params(f)
614+
self._write_query_params(f, protocol_version)
615615

616616

617617
class ExecuteMessage(_QueryMessage):
@@ -627,14 +627,14 @@ def __init__(self, query_id, query_params, consistency_level,
627627
super(ExecuteMessage, self).__init__(query_params, consistency_level, serial_consistency_level, fetch_size,
628628
paging_state, timestamp, skip_meta, continuous_paging_options)
629629

630-
def _write_query_params(self, f):
631-
super(ExecuteMessage, self)._write_query_params(f)
630+
def _write_query_params(self, f, protocol_version):
631+
super(ExecuteMessage, self)._write_query_params(f, protocol_version)
632632

633-
def send_body(self, f):
633+
def send_body(self, f, protocol_version):
634634
write_string(f, self.query_id)
635-
if ProtocolVersion.uses_prepared_metadata(self.protocol_version):
635+
if ProtocolVersion.uses_prepared_metadata(protocol_version):
636636
write_string(f, self.result_metadata_id)
637-
self._write_query_params(f)
637+
self._write_query_params(f, protocol_version)
638638

639639

640640
CUSTOM_TYPE = object()
@@ -860,20 +860,20 @@ def __init__(self, query, keyspace=None):
860860
self.query = query
861861
self.keyspace = keyspace
862862

863-
def send_body(self, f):
863+
def send_body(self, f, protocol_version):
864864
write_longstring(f, self.query)
865865

866866
flags = 0x00
867867

868868
if self.keyspace is not None:
869-
if ProtocolVersion.uses_keyspace_flag(self.protocol_version):
869+
if ProtocolVersion.uses_keyspace_flag(protocol_version):
870870
flags |= _PREPARED_WITH_KEYSPACE_FLAG
871871
else:
872872
raise UnsupportedOperation(
873873
"Keyspaces may only be set on queries with protocol version "
874874
"5 or DSE_V2 or higher. Consider setting Cluster.protocol_version.")
875875

876-
if ProtocolVersion.uses_prepare_flags(self.protocol_version):
876+
if ProtocolVersion.uses_prepare_flags(protocol_version):
877877
write_uint(f, flags)
878878
else:
879879
# checks above should prevent this, but just to be safe...
@@ -883,9 +883,9 @@ def send_body(self, f):
883883
"protocol version {pv}, which doesn't support flags"
884884
"in prepared statements."
885885
"Consider setting Cluster.protocol_version to 5 or DSE_V2."
886-
"".format(flags=flags, pv=self.protocol_version))
886+
"".format(flags=flags, pv=protocol_version))
887887

888-
if ProtocolVersion.uses_keyspace_flag(self.protocol_version):
888+
if ProtocolVersion.uses_keyspace_flag(protocol_version):
889889
if self.keyspace:
890890
write_string(f, self.keyspace)
891891

@@ -904,7 +904,7 @@ def __init__(self, batch_type, queries, consistency_level,
904904
self.timestamp = timestamp
905905
self.keyspace = keyspace
906906

907-
def send_body(self, f):
907+
def send_body(self, f, protocol_version):
908908
write_byte(f, self.batch_type.value)
909909
write_short(f, len(self.queries))
910910
for prepared, string_or_query_id, params in self.queries:
@@ -926,13 +926,13 @@ def send_body(self, f):
926926
if self.timestamp is not None:
927927
flags |= _PROTOCOL_TIMESTAMP_FLAG
928928
if self.keyspace:
929-
if ProtocolVersion.uses_keyspace_flag(self.protocol_version):
929+
if ProtocolVersion.uses_keyspace_flag(protocol_version):
930930
flags |= _WITH_KEYSPACE_FLAG
931931
else:
932932
raise UnsupportedOperation(
933933
"Keyspaces may only be set on queries with protocol version "
934934
"5 or higher. Consider setting Cluster.protocol_version to 5.")
935-
if ProtocolVersion.uses_int_query_flags(self.protocol_version):
935+
if ProtocolVersion.uses_int_query_flags(protocol_version):
936936
write_int(f, flags)
937937
else:
938938
write_byte(f, flags)
@@ -942,7 +942,7 @@ def send_body(self, f):
942942
if self.timestamp is not None:
943943
write_long(f, self.timestamp)
944944

945-
if ProtocolVersion.uses_keyspace_flag(self.protocol_version):
945+
if ProtocolVersion.uses_keyspace_flag(protocol_version):
946946
if self.keyspace is not None:
947947
write_string(f, self.keyspace)
948948

@@ -962,7 +962,7 @@ class RegisterMessage(_MessageType):
962962
def __init__(self, event_list):
963963
self.event_list = event_list
964964

965-
def send_body(self, f):
965+
def send_body(self, f, protocol_version):
966966
write_stringlist(f, self.event_list)
967967

968968

@@ -1038,7 +1038,7 @@ def __init__(self, op_type, op_id, next_pages=0):
10381038
self.op_id = op_id
10391039
self.next_pages = next_pages
10401040

1041-
def send_body(self, f):
1041+
def send_body(self, f, protocol_version):
10421042
write_int(f, self.op_type)
10431043
write_int(f, self.op_id)
10441044
if self.op_type == ReviseRequestMessage.RevisionType.PAGING_BACKPRESSURE:
@@ -1087,8 +1087,7 @@ def encode_message(cls, msg, stream_id, protocol_version, compressor, allow_beta
10871087
raise UnsupportedOperation("Custom key/value payloads can only be used with protocol version 4 or higher")
10881088
flags |= CUSTOM_PAYLOAD_FLAG
10891089
write_bytesmap(body, msg.custom_payload)
1090-
msg.protocol_version = protocol_version
1091-
msg.send_body(body)
1090+
msg.send_body(body, protocol_version)
10921091
body = body.getvalue()
10931092

10941093
# With checksumming, the compression is done at the segment frame encoding

test_output.log

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/usr/lib/python3.14/site-packages/pytest_asyncio/plugin.py:211: PytestDeprecationWarning: The configuration option "asyncio_default_fixture_loop_scope" is unset.
2+
The event loop scope for asynchronous fixtures will default to the fixture caching scope. Future versions of pytest-asyncio will default the loop scope for asynchronous fixtures to function scope. Set the default fixture loop scope explicitly in order to avoid unexpected behavior in the future. Valid fixture loop scopes are: "function", "class", "module", "package", "session"
3+
4+
warnings.warn(PytestDeprecationWarning(_DEFAULT_FIXTURE_LOOP_SCOPE_UNSET))
5+
============================= test session starts ==============================
6+
platform linux -- Python 3.14.2, pytest-8.3.5, pluggy-1.6.0
7+
rootdir: /home/ykaul/github/python-driver
8+
configfile: pyproject.toml
9+
plugins: asyncio-1.1.0, anyio-4.11.0
10+
asyncio: mode=Mode.STRICT, asyncio_default_fixture_loop_scope=None, asyncio_default_test_loop_scope=function
11+
collected 646 items
12+
13+
tests/unit/advanced/test_execution_profile.py .. [ 0%]
14+
tests/unit/advanced/test_geometry.py .......ssss [ 2%]
15+
tests/unit/advanced/test_graph.py ....................... [ 5%]
16+
tests/unit/advanced/test_insights.py ................ [ 8%]
17+
tests/unit/advanced/test_metadata.py ............ [ 9%]
18+
tests/unit/advanced/test_policies.py ...... [ 10%]
19+
tests/unit/column_encryption/test_policies.py sssssssssssssss [ 13%]
20+
tests/unit/cqlengine/test_columns.py .. [ 13%]
21+
tests/unit/cqlengine/test_connection.py ... [ 13%]
22+
tests/unit/cqlengine/test_udt.py . [ 14%]
23+
tests/unit/cython/test_bytesio.py ss [ 14%]
24+
tests/unit/cython/test_types.py ss [ 14%]
25+
tests/unit/cython/test_utils.py s [ 14%]
26+
tests/unit/io/test_asyncioreactor.py ss [ 15%]
27+
tests/unit/io/test_asyncorereactor.py ssssssssssssssss [ 17%]
28+
tests/unit/io/test_eventletreactor.py ss [ 17%]
29+
tests/unit/io/test_geventreactor.py ss [ 18%]
30+
tests/unit/io/test_libevreactor.py sssssssssssssssss [ 20%]
31+
tests/unit/io/test_twistedreactor.py ssssssssss [ 22%]
32+
tests/unit/test_auth.py . [ 22%]
33+
tests/unit/test_cluster.py ..........ss.sssssssssssssssss [ 27%]
34+
tests/unit/test_concurrent.py ......... [ 28%]
35+
tests/unit/test_connection.py ......................... [ 32%]
36+
tests/unit/test_control_connection.py ...................... [ 35%]
37+
tests/unit/test_endpoints.py ... [ 36%]
38+
tests/unit/test_exception.py .. [ 36%]
39+
tests/unit/test_host_connection_pool.py ..
40+
41+
=============================== warnings summary ===============================
42+
tests/unit/advanced/test_insights.py::TestConfigAsDict::test_graph_options
43+
/home/ykaul/github/python-driver/cassandra/datastax/graph/query.py:80: UserWarning: Unknown keyword argument received for GraphOptions: graph_invalid_option
44+
warn("Unknown keyword argument received for GraphOptions: {0}".format(attr))
45+
46+
tests/unit/advanced/test_policies.py::DSELoadBalancingPolicyTest::test_no_target
47+
tests/unit/advanced/test_policies.py::DSELoadBalancingPolicyTest::test_status_updates
48+
tests/unit/advanced/test_policies.py::DSELoadBalancingPolicyTest::test_target_host_down
49+
tests/unit/advanced/test_policies.py::DSELoadBalancingPolicyTest::test_target_host_nominal
50+
tests/unit/advanced/test_policies.py::DSELoadBalancingPolicyTest::test_target_no_host
51+
/home/ykaul/github/python-driver/cassandra/policies.py:1377: DeprecationWarning: DSELoadBalancingPolicy will be removed in 4.0. Consider using DefaultLoadBalancingPolicy.
52+
warnings.warn("DSELoadBalancingPolicy will be removed in 4.0. Consider using "
53+
54+
tests/unit/test_concurrent.py: 815 warnings
55+
tests/unit/test_connection.py: 1 warning
56+
/home/ykaul/github/python-driver/tests/unit/test_concurrent.py:105: DeprecationWarning: isSet() is deprecated, use is_set() instead
57+
return self._stopper.isSet()
58+
59+
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
60+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! KeyboardInterrupt !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
61+
/home/ykaul/github/python-driver/tests/unit/test_host_connection_pool.py:277: KeyboardInterrupt
62+
(to show a full traceback on KeyboardInterrupt use --full-trace)
63+
================ 147 passed, 92 skipped, 822 warnings in 16.98s ================

test_output_final.log

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/usr/lib/python3.14/site-packages/pytest_asyncio/plugin.py:211: PytestDeprecationWarning: The configuration option "asyncio_default_fixture_loop_scope" is unset.
2+
The event loop scope for asynchronous fixtures will default to the fixture caching scope. Future versions of pytest-asyncio will default the loop scope for asynchronous fixtures to function scope. Set the default fixture loop scope explicitly in order to avoid unexpected behavior in the future. Valid fixture loop scopes are: "function", "class", "module", "package", "session"
3+
4+
warnings.warn(PytestDeprecationWarning(_DEFAULT_FIXTURE_LOOP_SCOPE_UNSET))
5+
============================= test session starts ==============================
6+
platform linux -- Python 3.14.2, pytest-8.3.5, pluggy-1.6.0
7+
rootdir: /home/ykaul/github/python-driver
8+
configfile: pyproject.toml
9+
plugins: asyncio-1.1.0, anyio-4.11.0
10+
asyncio: mode=Mode.STRICT, asyncio_default_fixture_loop_scope=None, asyncio_default_test_loop_scope=function
11+
collected 646 items
12+
13+
tests/unit/advanced/test_execution_profile.py .. [ 0%]
14+
tests/unit/advanced/test_geometry.py .......ssss [ 2%]
15+
tests/unit/advanced/test_graph.py ....................... [ 5%]
16+
tests/unit/advanced/test_insights.py ................ [ 8%]
17+
tests/unit/advanced/test_metadata.py ............ [ 9%]
18+
tests/unit/advanced/test_policies.py ...... [ 10%]
19+
tests/unit/column_encryption/test_policies.py sssssssssssssss [ 13%]
20+
tests/unit/cqlengine/test_columns.py .. [ 13%]
21+
tests/unit/cqlengine/test_connection.py ... [ 13%]
22+
tests/unit/cqlengine/test_udt.py . [ 14%]
23+
tests/unit/cython/test_bytesio.py ss [ 14%]
24+
tests/unit/cython/test_types.py ss [ 14%]
25+
tests/unit/cython/test_utils.py s [ 14%]
26+
tests/unit/io/test_asyncioreactor.py ss [ 15%]
27+
tests/unit/io/test_asyncorereactor.py ssssssssssssssss [ 17%]
28+
tests/unit/io/test_eventletreactor.py ss [ 17%]
29+
tests/unit/io/test_geventreactor.py ss [ 18%]
30+
tests/unit/io/test_libevreactor.py sssssssssssssssss [ 20%]
31+
tests/unit/io/test_twistedreactor.py ssssssssss [ 22%]
32+
tests/unit/test_auth.py . [ 22%]
33+
tests/unit/test_cluster.py ..........ss.sssssssssssssssss [ 27%]
34+
tests/unit/test_concurrent.py ......... [ 28%]
35+
tests/unit/test_connection.py ......................... [ 32%]
36+
tests/unit/test_control_connection.py ...................... [ 35%]
37+
tests/unit/test_endpoints.py ... [ 36%]
38+
tests/unit/test_exception.py .. [ 36%]
39+
tests/unit/test_host_connection_pool.py ..
40+
41+
=============================== warnings summary ===============================
42+
tests/unit/advanced/test_insights.py::TestConfigAsDict::test_graph_options
43+
/home/ykaul/github/python-driver/cassandra/datastax/graph/query.py:80: UserWarning: Unknown keyword argument received for GraphOptions: graph_invalid_option
44+
warn("Unknown keyword argument received for GraphOptions: {0}".format(attr))
45+
46+
tests/unit/advanced/test_policies.py::DSELoadBalancingPolicyTest::test_no_target
47+
tests/unit/advanced/test_policies.py::DSELoadBalancingPolicyTest::test_status_updates
48+
tests/unit/advanced/test_policies.py::DSELoadBalancingPolicyTest::test_target_host_down
49+
tests/unit/advanced/test_policies.py::DSELoadBalancingPolicyTest::test_target_host_nominal
50+
tests/unit/advanced/test_policies.py::DSELoadBalancingPolicyTest::test_target_no_host
51+
/home/ykaul/github/python-driver/cassandra/policies.py:1377: DeprecationWarning: DSELoadBalancingPolicy will be removed in 4.0. Consider using DefaultLoadBalancingPolicy.
52+
warnings.warn("DSELoadBalancingPolicy will be removed in 4.0. Consider using "
53+
54+
tests/unit/test_concurrent.py: 816 warnings
55+
/home/ykaul/github/python-driver/tests/unit/test_concurrent.py:105: DeprecationWarning: isSet() is deprecated, use is_set() instead
56+
return self._stopper.isSet()
57+
58+
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
59+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! KeyboardInterrupt !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
60+
/home/ykaul/github/python-driver/tests/unit/test_host_connection_pool.py:279: KeyboardInterrupt
61+
(to show a full traceback on KeyboardInterrupt use --full-trace)
62+
================ 147 passed, 92 skipped, 822 warnings in 11.34s ================

0 commit comments

Comments
 (0)