valkey-glide 2.0.0__cp39-cp39-macosx_11_0_arm64.whl → 2.2.5rc1__cp39-cp39-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. glide/__init__.py +152 -118
  2. glide/async_commands/cluster_commands.py +29 -14
  3. glide/async_commands/core.py +600 -414
  4. glide/async_commands/{server_modules/ft.py → ft.py} +8 -7
  5. glide/async_commands/{server_modules/glide_json.py → glide_json.py} +15 -92
  6. glide/async_commands/standalone_commands.py +10 -51
  7. glide/glide.cpython-39-darwin.so +0 -0
  8. glide/glide.pyi +1 -1
  9. glide/glide_client.py +54 -48
  10. glide/logger.py +3 -3
  11. glide/opentelemetry.py +8 -4
  12. glide_shared/__init__.py +330 -0
  13. glide_shared/commands/__init__.py +0 -0
  14. {glide/async_commands → glide_shared/commands}/batch.py +426 -32
  15. {glide/async_commands → glide_shared/commands}/batch_options.py +1 -1
  16. glide_shared/commands/core_options.py +407 -0
  17. {glide/async_commands → glide_shared/commands}/server_modules/ft_options/ft_aggregate_options.py +3 -3
  18. {glide/async_commands → glide_shared/commands}/server_modules/ft_options/ft_create_options.py +4 -2
  19. {glide/async_commands → glide_shared/commands}/server_modules/ft_options/ft_profile_options.py +4 -4
  20. {glide/async_commands → glide_shared/commands}/server_modules/ft_options/ft_search_options.py +4 -2
  21. {glide/async_commands → glide_shared/commands}/server_modules/json_batch.py +4 -4
  22. glide_shared/commands/server_modules/json_options.py +93 -0
  23. {glide/async_commands → glide_shared/commands}/sorted_set.py +2 -2
  24. {glide/async_commands → glide_shared/commands}/stream.py +1 -1
  25. {glide → glide_shared}/config.py +315 -60
  26. {glide → glide_shared}/constants.py +3 -3
  27. {glide → glide_shared}/exceptions.py +27 -1
  28. glide_shared/protobuf/command_request_pb2.py +56 -0
  29. glide_shared/protobuf/connection_request_pb2.py +56 -0
  30. {glide → glide_shared}/routes.py +29 -15
  31. {valkey_glide-2.0.0.dist-info → valkey_glide-2.2.5rc1.dist-info}/METADATA +120 -58
  32. valkey_glide-2.2.5rc1.dist-info/RECORD +40 -0
  33. glide/protobuf/command_request_pb2.py +0 -54
  34. glide/protobuf/command_request_pb2.pyi +0 -1193
  35. glide/protobuf/connection_request_pb2.py +0 -52
  36. glide/protobuf/connection_request_pb2.pyi +0 -299
  37. glide/protobuf/response_pb2.pyi +0 -106
  38. valkey_glide-2.0.0.dist-info/RECORD +0 -39
  39. {glide/async_commands → glide_shared/commands}/bitmap.py +0 -0
  40. {glide/async_commands → glide_shared/commands}/command_args.py +0 -0
  41. {glide/async_commands → glide_shared/commands}/server_modules/ft_options/ft_constants.py +0 -0
  42. {glide → glide_shared}/protobuf/response_pb2.py +0 -0
  43. {glide → glide_shared}/protobuf_codec.py +0 -0
  44. {valkey_glide-2.0.0.dist-info → valkey_glide-2.2.5rc1.dist-info}/WHEEL +0 -0
@@ -6,12 +6,18 @@ from dataclasses import dataclass
6
6
  from enum import Enum, IntEnum
7
7
  from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union
8
8
 
9
- from glide.async_commands.core import CoreCommands
10
- from glide.exceptions import ConfigurationError
11
- from glide.protobuf.connection_request_pb2 import ConnectionRequest
12
- from glide.protobuf.connection_request_pb2 import ProtocolVersion as SentProtocolVersion
13
- from glide.protobuf.connection_request_pb2 import ReadFrom as ProtobufReadFrom
14
- from glide.protobuf.connection_request_pb2 import TlsMode
9
+ from glide_shared.commands.core_options import PubSubMsg
10
+ from glide_shared.exceptions import ConfigurationError
11
+ from glide_shared.protobuf.connection_request_pb2 import (
12
+ ConnectionRequest,
13
+ )
14
+ from glide_shared.protobuf.connection_request_pb2 import (
15
+ ProtocolVersion as SentProtocolVersion,
16
+ )
17
+ from glide_shared.protobuf.connection_request_pb2 import ReadFrom as ProtobufReadFrom
18
+ from glide_shared.protobuf.connection_request_pb2 import (
19
+ TlsMode,
20
+ )
15
21
 
16
22
 
17
23
  class NodeAddress:
@@ -73,7 +79,7 @@ class BackoffStrategy:
73
79
  """
74
80
  Represents the strategy used to determine how and when to reconnect, in case of connection failures.
75
81
  The time between attempts grows exponentially, to the formula rand(0 .. factor * (exponentBase ^ N)), where N
76
- is the number of failed attempts, and `rand(...)` applies a jitter of up to `jitter_percent`% to introduce randomness and reduce retry storms.
82
+ is the number of failed attempts, and rand(...) applies a jitter of up to jitter_percent% to introduce randomness and reduce retry storms.
77
83
  Once the maximum value is reached, that will remain the time between retry attempts until a reconnect attempt is
78
84
  successful.
79
85
  The client will attempt to reconnect indefinitely.
@@ -83,6 +89,7 @@ class BackoffStrategy:
83
89
  where the time between retries increases. Once the retries have reached the maximum value, the time between
84
90
  retries will remain constant until a reconnect attempt is succesful.
85
91
  factor (int): The multiplier that will be applied to the waiting time between each retry.
92
+ This value is specified in milliseconds.
86
93
  exponent_base (int): The exponent base configured for the strategy.
87
94
  jitter_percent (Optional[int]): The Jitter percent on the calculated duration. If not set, a default value will be used.
88
95
  """
@@ -100,23 +107,92 @@ class BackoffStrategy:
100
107
  self.jitter_percent = jitter_percent
101
108
 
102
109
 
110
+ class ServiceType(Enum):
111
+ """
112
+ Represents the types of AWS services that can be used for IAM authentication.
113
+ """
114
+
115
+ ELASTICACHE = 0
116
+ """Amazon ElastiCache service."""
117
+ MEMORYDB = 1
118
+ """Amazon MemoryDB service."""
119
+
120
+
121
+ class IamAuthConfig:
122
+ """
123
+ Configuration settings for IAM authentication.
124
+
125
+ Attributes:
126
+ cluster_name (str): The name of the ElastiCache/MemoryDB cluster.
127
+ service (ServiceType): The type of service being used (ElastiCache or MemoryDB).
128
+ region (str): The AWS region where the ElastiCache/MemoryDB cluster is located.
129
+ refresh_interval_seconds (Optional[int]): Optional refresh interval in seconds for renewing IAM authentication tokens.
130
+ If not provided, the core will use a default value of 300 seconds (5 min).
131
+ """
132
+
133
+ def __init__(
134
+ self,
135
+ cluster_name: str,
136
+ service: ServiceType,
137
+ region: str,
138
+ refresh_interval_seconds: Optional[int] = None,
139
+ ):
140
+ self.cluster_name = cluster_name
141
+ self.service = service
142
+ self.region = region
143
+ self.refresh_interval_seconds = refresh_interval_seconds
144
+
145
+
103
146
  class ServerCredentials:
104
147
  """
105
148
  Represents the credentials for connecting to a server.
106
149
 
150
+ Exactly one of the following authentication modes must be provided:
151
+ - Password-based authentication: Use password (and optionally username)
152
+ - IAM authentication: Use username (required) and iam_config
153
+
154
+ These modes are mutually exclusive - you cannot use both simultaneously.
155
+
107
156
  Attributes:
108
- password (str): The password that will be used for authenticating connections to the servers.
157
+ password (Optional[str]): The password that will be used for authenticating connections to the servers.
158
+ Mutually exclusive with iam_config. Either password or iam_config must be provided.
109
159
  username (Optional[str]): The username that will be used for authenticating connections to the servers.
110
- If not supplied, "default" will be used.
160
+ If not supplied for password-based authentication, "default" will be used.
161
+ Required for IAM authentication.
162
+ iam_config (Optional[IamAuthConfig]): IAM authentication configuration. Mutually exclusive with password.
163
+ Either password or iam_config must be provided.
164
+ The client will automatically generate and refresh the authentication token based on the provided configuration.
111
165
  """
112
166
 
113
167
  def __init__(
114
168
  self,
115
- password: str,
169
+ password: Optional[str] = None,
116
170
  username: Optional[str] = None,
171
+ iam_config: Optional[IamAuthConfig] = None,
117
172
  ):
173
+ # Validate mutual exclusivity
174
+ if password is not None and iam_config is not None:
175
+ raise ConfigurationError(
176
+ "password and iam_config are mutually exclusive. Use either password-based or IAM authentication, not both."
177
+ )
178
+
179
+ # Validate IAM requires username
180
+ if iam_config is not None and not username:
181
+ raise ConfigurationError("username is required for IAM authentication.")
182
+
183
+ # At least one authentication method must be provided
184
+ if password is None and iam_config is None:
185
+ raise ConfigurationError(
186
+ "Either password or iam_config must be provided for authentication."
187
+ )
188
+
118
189
  self.password = password
119
190
  self.username = username
191
+ self.iam_config = iam_config
192
+
193
+ def is_iam_auth(self) -> bool:
194
+ """Returns True if this credential is configured for IAM authentication."""
195
+ return self.iam_config is not None
120
196
 
121
197
 
122
198
  class PeriodicChecksManualInterval:
@@ -165,10 +241,40 @@ class TlsAdvancedConfiguration:
165
241
  Enabling it without TLS will result in a `ConfigurationError`.
166
242
 
167
243
  - Default: False (verification is enforced).
244
+
245
+ root_pem_cacerts (Optional[bytes]): Custom root certificate data for TLS connections in PEM format.
246
+
247
+ - When provided, these certificates will be used instead of the system's default trust store.
248
+ This is useful for connecting to servers with self-signed certificates or corporate
249
+ certificate authorities.
250
+
251
+ - If set to an empty bytes object (non-None but length 0), a `ConfigurationError` will be raised.
252
+
253
+ - If None (default), the system's default certificate trust store will be used (platform verifier).
254
+
255
+ - The certificate data should be in PEM format as a bytes object.
256
+
257
+ - Multiple certificates can be provided by concatenating them in PEM format.
258
+
259
+ Example usage::
260
+
261
+ # Load from file
262
+ with open('/path/to/ca-cert.pem', 'rb') as f:
263
+ cert_data = f.read()
264
+ tls_config = TlsAdvancedConfiguration(root_pem_cacerts=cert_data)
265
+
266
+ # Or provide directly
267
+ cert_data = b"-----BEGIN CERTIFICATE-----\\n...\\n-----END CERTIFICATE-----"
268
+ tls_config = TlsAdvancedConfiguration(root_pem_cacerts=cert_data)
168
269
  """
169
270
 
170
- def __init__(self, use_insecure_tls: Optional[bool] = None):
271
+ def __init__(
272
+ self,
273
+ use_insecure_tls: Optional[bool] = None,
274
+ root_pem_cacerts: Optional[bytes] = None,
275
+ ):
171
276
  self.use_insecure_tls = use_insecure_tls
277
+ self.root_pem_cacerts = root_pem_cacerts
172
278
 
173
279
 
174
280
  class AdvancedBaseClientConfiguration:
@@ -183,15 +289,21 @@ class AdvancedBaseClientConfiguration:
183
289
  tls_config (Optional[TlsAdvancedConfiguration]): The advanced TLS configuration settings.
184
290
  This allows for more granular control of TLS behavior, such as enabling an insecure mode
185
291
  that bypasses certificate validation.
292
+ tcp_nodelay (Optional[bool]): Controls TCP_NODELAY socket option (Nagle's algorithm).
293
+ When True, disables Nagle's algorithm for lower latency by sending packets immediately without buffering.
294
+ When False, enables Nagle's algorithm to reduce network overhead by buffering small packets.
295
+ If not explicitly set, defaults to True.
186
296
  """
187
297
 
188
298
  def __init__(
189
299
  self,
190
300
  connection_timeout: Optional[int] = None,
191
301
  tls_config: Optional[TlsAdvancedConfiguration] = None,
302
+ tcp_nodelay: Optional[bool] = None,
192
303
  ):
193
304
  self.connection_timeout = connection_timeout
194
305
  self.tls_config = tls_config
306
+ self.tcp_nodelay = tcp_nodelay
195
307
 
196
308
  def _create_a_protobuf_conn_request(
197
309
  self, request: ConnectionRequest
@@ -199,13 +311,27 @@ class AdvancedBaseClientConfiguration:
199
311
  if self.connection_timeout:
200
312
  request.connection_timeout = self.connection_timeout
201
313
 
202
- if self.tls_config and self.tls_config.use_insecure_tls:
203
- if request.tls_mode == TlsMode.SecureTls:
314
+ if self.tcp_nodelay is not None:
315
+ request.tcp_nodelay = self.tcp_nodelay
316
+
317
+ if self.tls_config:
318
+ if self.tls_config.use_insecure_tls:
319
+ # Validate that TLS is enabled before allowing insecure mode
320
+ if request.tls_mode == TlsMode.NoTls:
321
+ raise ConfigurationError(
322
+ "use_insecure_tls cannot be enabled when use_tls is disabled."
323
+ )
324
+
325
+ # Override the default SecureTls mode to InsecureTls when user explicitly requests it
204
326
  request.tls_mode = TlsMode.InsecureTls
205
- elif request.tls_mode == TlsMode.NoTls:
206
- raise ConfigurationError(
207
- "use_insecure_tls cannot be enabled when use_tls is disabled."
208
- )
327
+
328
+ # Handle root certificates
329
+ if self.tls_config.root_pem_cacerts is not None:
330
+ if len(self.tls_config.root_pem_cacerts) == 0:
331
+ raise ConfigurationError(
332
+ "root_pem_cacerts cannot be an empty bytes object; use None to use platform verifier"
333
+ )
334
+ request.root_certs.append(self.tls_config.root_pem_cacerts)
209
335
 
210
336
  return request
211
337
 
@@ -223,9 +349,9 @@ class BaseClientConfiguration:
223
349
  For example::
224
350
 
225
351
  [
226
- {address:sample-address-0001.use1.cache.amazonaws.com, port:6379},
227
- {address: sample-address-0002.use2.cache.amazonaws.com, port:6379}
228
- ].
352
+ NodeAddress("sample-address-0001.use1.cache.amazonaws.com", 6379),
353
+ NodeAddress("sample-address-0002.use1.cache.amazonaws.com", 6379)
354
+ ]
229
355
 
230
356
  use_tls (bool): True if communication with the cluster should use Transport Level Security.
231
357
  Should match the TLS configuration of the server/cluster, otherwise the connection attempt will fail.
@@ -242,6 +368,8 @@ class BaseClientConfiguration:
242
368
  reconnect_strategy (Optional[BackoffStrategy]): Strategy used to determine how and when to reconnect, in case of
243
369
  connection failures.
244
370
  If not set, a default backoff strategy will be used.
371
+ database_id (Optional[int]): Index of the logical database to connect to.
372
+ Must be a non-negative integer.If not set, the client will connect to database 0.
245
373
  client_name (Optional[str]): Client name to be used for the client. Will be used with CLIENT SETNAME command
246
374
  during connection establishment.
247
375
  protocol (ProtocolVersion): Serialization protocol to be used. If not set, `RESP3` will be used.
@@ -285,6 +413,7 @@ class BaseClientConfiguration:
285
413
  read_from: ReadFrom = ReadFrom.PRIMARY,
286
414
  request_timeout: Optional[int] = None,
287
415
  reconnect_strategy: Optional[BackoffStrategy] = None,
416
+ database_id: Optional[int] = None,
288
417
  client_name: Optional[str] = None,
289
418
  protocol: ProtocolVersion = ProtocolVersion.RESP3,
290
419
  inflight_requests_limit: Optional[int] = None,
@@ -298,6 +427,7 @@ class BaseClientConfiguration:
298
427
  self.read_from = read_from
299
428
  self.request_timeout = request_timeout
300
429
  self.reconnect_strategy = reconnect_strategy
430
+ self.database_id = database_id
301
431
  self.client_name = client_name
302
432
  self.protocol = protocol
303
433
  self.inflight_requests_limit = inflight_requests_limit
@@ -315,6 +445,73 @@ class BaseClientConfiguration:
315
445
  "client_az must be set when read_from is set to AZ_AFFINITY_REPLICAS_AND_PRIMARY"
316
446
  )
317
447
 
448
+ def _set_addresses_in_request(self, request: ConnectionRequest) -> None:
449
+ """Set addresses in the protobuf request."""
450
+ for address in self.addresses:
451
+ address_info = request.addresses.add()
452
+ address_info.host = address.host
453
+ address_info.port = address.port
454
+
455
+ def _set_reconnect_strategy_in_request(self, request: ConnectionRequest) -> None:
456
+ """Set reconnect strategy in the protobuf request."""
457
+ if not self.reconnect_strategy:
458
+ return
459
+
460
+ request.connection_retry_strategy.number_of_retries = (
461
+ self.reconnect_strategy.num_of_retries
462
+ )
463
+ request.connection_retry_strategy.factor = self.reconnect_strategy.factor
464
+ request.connection_retry_strategy.exponent_base = (
465
+ self.reconnect_strategy.exponent_base
466
+ )
467
+ if self.reconnect_strategy.jitter_percent is not None:
468
+ request.connection_retry_strategy.jitter_percent = (
469
+ self.reconnect_strategy.jitter_percent
470
+ )
471
+
472
+ def _set_credentials_in_request(self, request: ConnectionRequest) -> None:
473
+ """Set credentials in the protobuf request."""
474
+ if not self.credentials:
475
+ return
476
+
477
+ if self.credentials.username:
478
+ request.authentication_info.username = self.credentials.username
479
+
480
+ if self.credentials.password:
481
+ request.authentication_info.password = self.credentials.password
482
+
483
+ # Set IAM credentials if present
484
+ if self.credentials.iam_config:
485
+ iam_config = self.credentials.iam_config
486
+ request.authentication_info.iam_credentials.cluster_name = (
487
+ iam_config.cluster_name
488
+ )
489
+ request.authentication_info.iam_credentials.region = iam_config.region
490
+
491
+ # Map ServiceType enum to protobuf ServiceType
492
+ if iam_config.service == ServiceType.ELASTICACHE:
493
+ from glide_shared.protobuf.connection_request_pb2 import (
494
+ ServiceType as ProtobufServiceType,
495
+ )
496
+
497
+ request.authentication_info.iam_credentials.service_type = (
498
+ ProtobufServiceType.ELASTICACHE
499
+ )
500
+ elif iam_config.service == ServiceType.MEMORYDB:
501
+ from glide_shared.protobuf.connection_request_pb2 import (
502
+ ServiceType as ProtobufServiceType,
503
+ )
504
+
505
+ request.authentication_info.iam_credentials.service_type = (
506
+ ProtobufServiceType.MEMORYDB
507
+ )
508
+
509
+ # Set optional refresh interval
510
+ if iam_config.refresh_interval_seconds is not None:
511
+ request.authentication_info.iam_credentials.refresh_interval_seconds = (
512
+ iam_config.refresh_interval_seconds
513
+ )
514
+
318
515
  def _create_a_protobuf_conn_request(
319
516
  self, cluster_mode: bool = False
320
517
  ) -> ConnectionRequest:
@@ -328,44 +525,34 @@ class BaseClientConfiguration:
328
525
  ConnectionRequest: Protobuf ConnectionRequest.
329
526
  """
330
527
  request = ConnectionRequest()
331
- for address in self.addresses:
332
- address_info = request.addresses.add()
333
- address_info.host = address.host
334
- address_info.port = address.port
528
+
529
+ # Set basic configuration
530
+ self._set_addresses_in_request(request)
335
531
  request.tls_mode = TlsMode.SecureTls if self.use_tls else TlsMode.NoTls
336
532
  request.read_from = self.read_from.value
533
+ request.cluster_mode_enabled = cluster_mode
534
+ request.protocol = self.protocol.value
535
+
536
+ # Set optional configuration
337
537
  if self.request_timeout:
338
538
  request.request_timeout = self.request_timeout
339
- if self.reconnect_strategy:
340
- request.connection_retry_strategy.number_of_retries = (
341
- self.reconnect_strategy.num_of_retries
342
- )
343
- request.connection_retry_strategy.factor = self.reconnect_strategy.factor
344
- request.connection_retry_strategy.exponent_base = (
345
- self.reconnect_strategy.exponent_base
346
- )
347
- if self.reconnect_strategy.jitter_percent is not None:
348
- request.connection_retry_strategy.jitter_percent = (
349
- self.reconnect_strategy.jitter_percent
350
- )
351
539
 
352
- request.cluster_mode_enabled = True if cluster_mode else False
353
- if self.credentials:
354
- if self.credentials.username:
355
- request.authentication_info.username = self.credentials.username
356
- request.authentication_info.password = self.credentials.password
540
+ self._set_reconnect_strategy_in_request(request)
541
+ self._set_credentials_in_request(request)
542
+
357
543
  if self.client_name:
358
544
  request.client_name = self.client_name
359
- request.protocol = self.protocol.value
360
545
  if self.inflight_requests_limit:
361
546
  request.inflight_requests_limit = self.inflight_requests_limit
362
547
  if self.client_az:
363
548
  request.client_az = self.client_az
549
+ if self.database_id is not None:
550
+ request.database_id = self.database_id
364
551
  if self.advanced_config:
365
552
  self.advanced_config._create_a_protobuf_conn_request(request)
366
-
367
553
  if self.lazy_connect is not None:
368
554
  request.lazy_connect = self.lazy_connect
555
+
369
556
  return request
370
557
 
371
558
  def _is_pubsub_configured(self) -> bool:
@@ -373,7 +560,7 @@ class BaseClientConfiguration:
373
560
 
374
561
  def _get_pubsub_callback_and_context(
375
562
  self,
376
- ) -> Tuple[Optional[Callable[[CoreCommands.PubSubMsg, Any], None]], Any]:
563
+ ) -> Tuple[Optional[Callable[[PubSubMsg, Any], None]], Any]:
377
564
  return None, None
378
565
 
379
566
 
@@ -386,9 +573,10 @@ class AdvancedGlideClientConfiguration(AdvancedBaseClientConfiguration):
386
573
  self,
387
574
  connection_timeout: Optional[int] = None,
388
575
  tls_config: Optional[TlsAdvancedConfiguration] = None,
576
+ tcp_nodelay: Optional[bool] = None,
389
577
  ):
390
578
 
391
- super().__init__(connection_timeout, tls_config)
579
+ super().__init__(connection_timeout, tls_config, tcp_nodelay)
392
580
 
393
581
 
394
582
  class GlideClientConfiguration(BaseClientConfiguration):
@@ -397,12 +585,12 @@ class GlideClientConfiguration(BaseClientConfiguration):
397
585
 
398
586
  Attributes:
399
587
  addresses (List[NodeAddress]): DNS Addresses and ports of known nodes in the cluster.
400
- Only nodes whose addresses were provided will be used by the client.
588
+ Only nodes whose addresses were provided will be used by the client.
401
589
  For example::
402
590
 
403
591
  [
404
- {address:sample-address-0001.use1.cache.amazonaws.com, port:6379},
405
- {address: sample-address-0002.use2.cache.amazonaws.com, port:6379}
592
+ NodeAddress("sample-address-0001.use1.cache.amazonaws.com", 6379),
593
+ NodeAddress("sample-address-0002.use1.cache.amazonaws.com", 6379)
406
594
  ]
407
595
 
408
596
  use_tls (bool): True if communication with the cluster should use Transport Level Security.
@@ -418,7 +606,7 @@ class GlideClientConfiguration(BaseClientConfiguration):
418
606
  reconnect_strategy (Optional[BackoffStrategy]): Strategy used to determine how and when to reconnect, in case of
419
607
  connection failures.
420
608
  If not set, a default backoff strategy will be used.
421
- database_id (Optional[int]): index of the logical database to connect to.
609
+ database_id (Optional[int]): Index of the logical database to connect to.
422
610
  client_name (Optional[str]): Client name to be used for the client. Will be used with CLIENT SETNAME command during
423
611
  connection establishment.
424
612
  protocol (ProtocolVersion): The version of the RESP protocol to communicate with the server.
@@ -457,7 +645,7 @@ class GlideClientConfiguration(BaseClientConfiguration):
457
645
  Attributes:
458
646
  channels_and_patterns (Dict[GlideClientConfiguration.PubSubChannelModes, Set[str]]):
459
647
  Channels and patterns by modes.
460
- callback (Optional[Callable[[CoreCommands.PubSubMsg, Any], None]]):
648
+ callback (Optional[Callable[[PubSubMsg, Any], None]]):
461
649
  Optional callback to accept the incomming messages.
462
650
  context (Any):
463
651
  Arbitrary context to pass to the callback.
@@ -466,7 +654,7 @@ class GlideClientConfiguration(BaseClientConfiguration):
466
654
  channels_and_patterns: Dict[
467
655
  GlideClientConfiguration.PubSubChannelModes, Set[str]
468
656
  ]
469
- callback: Optional[Callable[[CoreCommands.PubSubMsg, Any], None]]
657
+ callback: Optional[Callable[[PubSubMsg, Any], None]]
470
658
  context: Any
471
659
 
472
660
  def __init__(
@@ -493,6 +681,7 @@ class GlideClientConfiguration(BaseClientConfiguration):
493
681
  read_from=read_from,
494
682
  request_timeout=request_timeout,
495
683
  reconnect_strategy=reconnect_strategy,
684
+ database_id=database_id,
496
685
  client_name=client_name,
497
686
  protocol=protocol,
498
687
  inflight_requests_limit=inflight_requests_limit,
@@ -500,7 +689,6 @@ class GlideClientConfiguration(BaseClientConfiguration):
500
689
  advanced_config=advanced_config,
501
690
  lazy_connect=lazy_connect,
502
691
  )
503
- self.database_id = database_id
504
692
  self.pubsub_subscriptions = pubsub_subscriptions
505
693
 
506
694
  def _create_a_protobuf_conn_request(
@@ -508,8 +696,6 @@ class GlideClientConfiguration(BaseClientConfiguration):
508
696
  ) -> ConnectionRequest:
509
697
  assert cluster_mode is False
510
698
  request = super()._create_a_protobuf_conn_request(cluster_mode)
511
- if self.database_id:
512
- request.database_id = self.database_id
513
699
 
514
700
  if self.pubsub_subscriptions:
515
701
  if self.protocol == ProtocolVersion.RESP2:
@@ -540,7 +726,7 @@ class GlideClientConfiguration(BaseClientConfiguration):
540
726
 
541
727
  def _get_pubsub_callback_and_context(
542
728
  self,
543
- ) -> Tuple[Optional[Callable[[CoreCommands.PubSubMsg, Any], None]], Any]:
729
+ ) -> Tuple[Optional[Callable[[PubSubMsg, Any], None]], Any]:
544
730
  if self.pubsub_subscriptions:
545
731
  return self.pubsub_subscriptions.callback, self.pubsub_subscriptions.context
546
732
  return None, None
@@ -549,14 +735,39 @@ class GlideClientConfiguration(BaseClientConfiguration):
549
735
  class AdvancedGlideClusterClientConfiguration(AdvancedBaseClientConfiguration):
550
736
  """
551
737
  Represents the advanced configuration settings for a Glide Cluster client.
738
+
739
+ Attributes:
740
+ connection_timeout (Optional[int]): The duration in milliseconds to wait for a TCP/TLS connection to complete.
741
+ This applies both during initial client creation and any reconnection that may occur during request processing.
742
+ **Note**: A high connection timeout may lead to prolonged blocking of the entire command pipeline.
743
+ If not explicitly set, a default value of 2000 milliseconds will be used.
744
+ tls_config (Optional[TlsAdvancedConfiguration]): The advanced TLS configuration settings.
745
+ This allows for more granular control of TLS behavior, such as enabling an insecure mode
746
+ that bypasses certificate validation.
747
+ refresh_topology_from_initial_nodes (bool): Enables refreshing the cluster topology using only the initial nodes.
748
+ When this option is enabled, all topology updates (both the periodic checks and on-demand refreshes
749
+ triggered by topology changes) will query only the initial nodes provided when creating the client, rather than using the internal cluster view.
552
750
  """
553
751
 
554
752
  def __init__(
555
753
  self,
556
754
  connection_timeout: Optional[int] = None,
557
755
  tls_config: Optional[TlsAdvancedConfiguration] = None,
756
+ refresh_topology_from_initial_nodes: bool = False,
757
+ tcp_nodelay: Optional[bool] = None,
558
758
  ):
559
- super().__init__(connection_timeout, tls_config)
759
+ super().__init__(connection_timeout, tls_config, tcp_nodelay)
760
+ self.refresh_topology_from_initial_nodes = refresh_topology_from_initial_nodes
761
+
762
+ def _create_a_protobuf_conn_request(
763
+ self, request: ConnectionRequest
764
+ ) -> ConnectionRequest:
765
+ super()._create_a_protobuf_conn_request(request)
766
+
767
+ request.refresh_topology_from_initial_nodes = (
768
+ self.refresh_topology_from_initial_nodes
769
+ )
770
+ return request
560
771
 
561
772
 
562
773
  class GlideClusterClientConfiguration(BaseClientConfiguration):
@@ -569,7 +780,7 @@ class GlideClusterClientConfiguration(BaseClientConfiguration):
569
780
  For example::
570
781
 
571
782
  [
572
- {address:configuration-endpoint.use1.cache.amazonaws.com, port:6379}
783
+ NodeAddress("sample-address-0001.use1.cache.amazonaws.com", 6379),
573
784
  ]
574
785
 
575
786
  use_tls (bool): True if communication with the cluster should use Transport Level Security.
@@ -585,6 +796,7 @@ class GlideClusterClientConfiguration(BaseClientConfiguration):
585
796
  reconnect_strategy (Optional[BackoffStrategy]): Strategy used to determine how and when to reconnect, in case of
586
797
  connection failures.
587
798
  If not set, a default backoff strategy will be used.
799
+ database_id (Optional[int]): Index of the logical database to connect to.
588
800
  client_name (Optional[str]): Client name to be used for the client. Will be used with CLIENT SETNAME command during
589
801
  connection establishment.
590
802
  protocol (ProtocolVersion): The version of the RESP protocol to communicate with the server.
@@ -634,7 +846,7 @@ class GlideClusterClientConfiguration(BaseClientConfiguration):
634
846
  Attributes:
635
847
  channels_and_patterns (Dict[GlideClusterClientConfiguration.PubSubChannelModes, Set[str]]):
636
848
  Channels and patterns by modes.
637
- callback (Optional[Callable[[CoreCommands.PubSubMsg, Any], None]]):
849
+ callback (Optional[Callable[[PubSubMsg, Any], None]]):
638
850
  Optional callback to accept the incoming messages.
639
851
  context (Any):
640
852
  Arbitrary context to pass to the callback.
@@ -643,7 +855,7 @@ class GlideClusterClientConfiguration(BaseClientConfiguration):
643
855
  channels_and_patterns: Dict[
644
856
  GlideClusterClientConfiguration.PubSubChannelModes, Set[str]
645
857
  ]
646
- callback: Optional[Callable[[CoreCommands.PubSubMsg, Any], None]]
858
+ callback: Optional[Callable[[PubSubMsg, Any], None]]
647
859
  context: Any
648
860
 
649
861
  def __init__(
@@ -654,6 +866,7 @@ class GlideClusterClientConfiguration(BaseClientConfiguration):
654
866
  read_from: ReadFrom = ReadFrom.PRIMARY,
655
867
  request_timeout: Optional[int] = None,
656
868
  reconnect_strategy: Optional[BackoffStrategy] = None,
869
+ database_id: Optional[int] = None,
657
870
  client_name: Optional[str] = None,
658
871
  protocol: ProtocolVersion = ProtocolVersion.RESP3,
659
872
  periodic_checks: Union[
@@ -672,6 +885,7 @@ class GlideClusterClientConfiguration(BaseClientConfiguration):
672
885
  read_from=read_from,
673
886
  request_timeout=request_timeout,
674
887
  reconnect_strategy=reconnect_strategy,
888
+ database_id=database_id,
675
889
  client_name=client_name,
676
890
  protocol=protocol,
677
891
  inflight_requests_limit=inflight_requests_limit,
@@ -725,7 +939,48 @@ class GlideClusterClientConfiguration(BaseClientConfiguration):
725
939
 
726
940
  def _get_pubsub_callback_and_context(
727
941
  self,
728
- ) -> Tuple[Optional[Callable[[CoreCommands.PubSubMsg, Any], None]], Any]:
942
+ ) -> Tuple[Optional[Callable[[PubSubMsg, Any], None]], Any]:
729
943
  if self.pubsub_subscriptions:
730
944
  return self.pubsub_subscriptions.callback, self.pubsub_subscriptions.context
731
945
  return None, None
946
+
947
+
948
+ def load_root_certificates_from_file(path: str) -> bytes:
949
+ """
950
+ Load PEM-encoded root certificates from a file.
951
+
952
+ This is a convenience function for loading custom root certificates from disk
953
+ to be used with TlsAdvancedConfiguration.
954
+
955
+ Args:
956
+ path (str): The file path to the PEM-encoded certificate file.
957
+
958
+ Returns:
959
+ bytes: The certificate data in PEM format.
960
+
961
+ Raises:
962
+ FileNotFoundError: If the certificate file does not exist.
963
+ ConfigurationError: If the certificate file is empty.
964
+
965
+ Example usage::
966
+
967
+ from glide_shared.config import load_root_certificates_from_file, TlsAdvancedConfiguration
968
+
969
+ # Load certificates from file
970
+ certs = load_root_certificates_from_file('/path/to/ca-cert.pem')
971
+
972
+ # Use in TLS configuration
973
+ tls_config = TlsAdvancedConfiguration(root_pem_cacerts=certs)
974
+ """
975
+ try:
976
+ with open(path, "rb") as f:
977
+ data = f.read()
978
+ except FileNotFoundError:
979
+ raise FileNotFoundError(f"Certificate file not found: {path}")
980
+ except Exception as e:
981
+ raise ConfigurationError(f"Failed to read certificate file: {e}")
982
+
983
+ if len(data) == 0:
984
+ raise ConfigurationError(f"Certificate file is empty: {path}")
985
+
986
+ return data
@@ -2,9 +2,9 @@
2
2
 
3
3
  from typing import Any, Dict, List, Literal, Mapping, Optional, Set, TypeVar, Union
4
4
 
5
- from glide.protobuf.command_request_pb2 import CommandRequest
6
- from glide.protobuf.connection_request_pb2 import ConnectionRequest
7
- from glide.routes import ByAddressRoute, RandomNode, SlotIdRoute, SlotKeyRoute
5
+ from glide_shared.protobuf.command_request_pb2 import CommandRequest
6
+ from glide_shared.protobuf.connection_request_pb2 import ConnectionRequest
7
+ from glide_shared.routes import ByAddressRoute, RandomNode, SlotIdRoute, SlotKeyRoute
8
8
 
9
9
  OK: str = "OK"
10
10
  DEFAULT_READ_BYTES_SIZE: int = pow(2, 16)