ray-hive 1.0__py3-none-any.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.
ray_hive/__init__.py ADDED
@@ -0,0 +1,64 @@
1
+ # Copyright 2026 ray-hive contributors
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
+ """Public API for the ray-hive HiveServer2 datasource."""
16
+
17
+ from ray_hive._api import read_hive
18
+ from ray_hive._credentials import CredentialProvider, SecretRef
19
+ from ray_hive._errors import (
20
+ HiveAuthenticationError,
21
+ HiveCancellationError,
22
+ HiveCleanupError,
23
+ HiveCompatibilityError,
24
+ HiveConfigurationError,
25
+ HiveDependencyError,
26
+ HiveError,
27
+ HiveErrorCode,
28
+ HivePermissionError,
29
+ HivePlanningError,
30
+ HiveProtocolError,
31
+ HiveQueryError,
32
+ HiveSchemaError,
33
+ HiveTransportError,
34
+ )
35
+ from ray_hive._models import (
36
+ HiveConnectionOptions,
37
+ HiveReadOptions,
38
+ HiveTableIdentifier,
39
+ HiveTLSOptions,
40
+ )
41
+
42
+ __all__ = [
43
+ "CredentialProvider",
44
+ "HiveAuthenticationError",
45
+ "HiveCancellationError",
46
+ "HiveCleanupError",
47
+ "HiveCompatibilityError",
48
+ "HiveConfigurationError",
49
+ "HiveConnectionOptions",
50
+ "HiveDependencyError",
51
+ "HiveError",
52
+ "HiveErrorCode",
53
+ "HivePermissionError",
54
+ "HivePlanningError",
55
+ "HiveProtocolError",
56
+ "HiveQueryError",
57
+ "HiveReadOptions",
58
+ "HiveSchemaError",
59
+ "HiveTLSOptions",
60
+ "HiveTableIdentifier",
61
+ "HiveTransportError",
62
+ "SecretRef",
63
+ "read_hive",
64
+ ]
ray_hive/_api.py ADDED
@@ -0,0 +1,40 @@
1
+ # Copyright 2026 ray-hive contributors
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
+ """Stable public read facade."""
16
+
17
+ from __future__ import annotations
18
+
19
+ from collections.abc import Mapping
20
+ from typing import Any
21
+
22
+ from ray_hive._compat import read_datasource_strict
23
+ from ray_hive._models import HiveConnectionOptions, HiveReadOptions
24
+ from ray_hive.datasource import HiveServer2Datasource
25
+
26
+
27
+ def read_hive(
28
+ *,
29
+ connection: HiveConnectionOptions,
30
+ read: HiveReadOptions,
31
+ ray_remote_args: Mapping[str, Any] | None = None,
32
+ ) -> Any:
33
+ """Plan an HS2-only read and return a lazy Ray Dataset with task retries disabled."""
34
+
35
+ datasource = HiveServer2Datasource.from_options(connection, read)
36
+ return read_datasource_strict(
37
+ datasource,
38
+ num_tasks=len(datasource.task_specs),
39
+ ray_remote_args=ray_remote_args,
40
+ )
@@ -0,0 +1,70 @@
1
+ # Copyright 2026 ray-hive contributors
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
+ """Fail-closed HiveServer2 capability negotiation."""
16
+
17
+ from __future__ import annotations
18
+
19
+ from dataclasses import dataclass
20
+ from enum import StrEnum
21
+
22
+ from ray_hive._errors import HiveCompatibilityError
23
+
24
+
25
+ class CapabilityState(StrEnum):
26
+ """Tri-state support result; unknown is never treated as supported."""
27
+
28
+ SUPPORTED = "supported"
29
+ UNSUPPORTED = "unsupported"
30
+ UNKNOWN = "unknown"
31
+
32
+
33
+ @dataclass(frozen=True, slots=True)
34
+ class CapabilityEvidence:
35
+ """The origin of one capability conclusion."""
36
+
37
+ capability: str
38
+ state: CapabilityState
39
+ source: str
40
+
41
+
42
+ @dataclass(frozen=True, slots=True)
43
+ class HiveCapabilities:
44
+ """Immutable capabilities negotiated immediately after OpenSession."""
45
+
46
+ protocol_version: int
47
+ columnar_rowsets: CapabilityState
48
+ binary_columns: CapabilityState
49
+ cancel_operation: CapabilityState
50
+ operation_log: CapabilityState
51
+ evidence: tuple[CapabilityEvidence, ...] = ()
52
+
53
+ def require_core(self) -> None:
54
+ """Reject a connection that cannot satisfy the Core read profile."""
55
+
56
+ if self.protocol_version < 6:
57
+ raise HiveCompatibilityError("HS2 protocol V6 or newer is required")
58
+ if self.columnar_rowsets is not CapabilityState.SUPPORTED:
59
+ raise HiveCompatibilityError("Native V6 columnar result sets are required")
60
+ if self.cancel_operation is not CapabilityState.SUPPORTED:
61
+ raise HiveCompatibilityError("CancelOperation capability is required")
62
+
63
+ def require(self, capability: str, state: CapabilityState) -> None:
64
+ """Require an explicitly supported capability by attribute name."""
65
+
66
+ actual = getattr(self, capability, None)
67
+ if actual is not state:
68
+ raise HiveCompatibilityError(
69
+ f"Required capability {capability!r} is not explicitly {state.value}"
70
+ )
ray_hive/_compat.py ADDED
@@ -0,0 +1,120 @@
1
+ # Copyright 2026 ray-hive contributors
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
+ """The only module allowed to adapt Ray DeveloperAPI version differences."""
16
+
17
+ from __future__ import annotations
18
+
19
+ import inspect
20
+ from collections.abc import Callable, Iterable, Mapping
21
+ from importlib.metadata import version
22
+ from typing import Any
23
+
24
+ import pyarrow as pa
25
+
26
+ from ray_hive._errors import HiveCompatibilityError
27
+
28
+ SUPPORTED_RAY_VERSION = "2.55.1"
29
+ SUPPORTED_PYARROW_VERSION = "19.0.1"
30
+ _READ_TASK_PARAMETERS = ("self", "read_fn", "metadata", "schema", "per_task_row_limit")
31
+ _GET_READ_TASKS_PARAMETERS = (
32
+ "self",
33
+ "parallelism",
34
+ "per_task_row_limit",
35
+ "data_context",
36
+ )
37
+
38
+
39
+ def validate_runtime() -> None:
40
+ """Fail closed unless the exact tested Ray/PyArrow contracts are installed."""
41
+
42
+ if version("ray") != SUPPORTED_RAY_VERSION:
43
+ raise HiveCompatibilityError(
44
+ f"ray-hive requires Ray {SUPPORTED_RAY_VERSION} in the initial release"
45
+ )
46
+ if pa.__version__ != SUPPORTED_PYARROW_VERSION:
47
+ raise HiveCompatibilityError(
48
+ f"ray-hive requires PyArrow {SUPPORTED_PYARROW_VERSION} in the initial release"
49
+ )
50
+
51
+ from ray.data import Datasource, ReadTask
52
+
53
+ read_task_parameters = tuple(inspect.signature(ReadTask.__init__).parameters)
54
+ if read_task_parameters != _READ_TASK_PARAMETERS:
55
+ raise HiveCompatibilityError("Unsupported Ray ReadTask constructor signature")
56
+ get_tasks_parameters = tuple(inspect.signature(Datasource.get_read_tasks).parameters)
57
+ if get_tasks_parameters != _GET_READ_TASKS_PARAMETERS:
58
+ raise HiveCompatibilityError("Unsupported Ray Datasource.get_read_tasks signature")
59
+
60
+
61
+ def initialize_datasource(instance: Any) -> None:
62
+ """Invoke the exact pinned Ray Datasource initializer through the adapter boundary."""
63
+
64
+ from ray.data import Datasource
65
+
66
+ initializer: Any = Datasource.__init__
67
+ initializer(instance)
68
+
69
+
70
+ def create_read_task(
71
+ read_fn: Callable[[], Iterable[pa.Table]],
72
+ schema: pa.Schema,
73
+ *,
74
+ per_task_row_limit: int | None,
75
+ ) -> Any:
76
+ """Construct the exact supported DeveloperAPI ReadTask shape."""
77
+
78
+ validate_runtime()
79
+ from ray.data import ReadTask
80
+ from ray.data.block import BlockMetadata
81
+
82
+ metadata = BlockMetadata(
83
+ num_rows=None,
84
+ size_bytes=None,
85
+ exec_stats=None,
86
+ input_files=None,
87
+ )
88
+ return ReadTask(
89
+ read_fn,
90
+ metadata,
91
+ schema=schema,
92
+ per_task_row_limit=per_task_row_limit,
93
+ )
94
+
95
+
96
+ def read_datasource_strict(
97
+ datasource: Any,
98
+ *,
99
+ num_tasks: int,
100
+ ray_remote_args: Mapping[str, Any] | None,
101
+ ) -> Any:
102
+ """Call the public Ray facade with no retry or lineage replay of a query task."""
103
+
104
+ validate_runtime()
105
+ from ray.data import read_datasource
106
+
107
+ parameters = inspect.signature(read_datasource).parameters
108
+ required = {"datasource", "ray_remote_args", "concurrency", "override_num_blocks"}
109
+ if not required.issubset(parameters):
110
+ raise HiveCompatibilityError("Unsupported Ray read_datasource signature")
111
+ remote_args = dict(ray_remote_args or {})
112
+ if remote_args.get("max_retries", 0) != 0:
113
+ raise HiveCompatibilityError("Strict Hive reads require Ray max_retries=0")
114
+ remote_args["max_retries"] = 0
115
+ return read_datasource(
116
+ datasource,
117
+ ray_remote_args=remote_args,
118
+ concurrency=num_tasks,
119
+ override_num_blocks=num_tasks,
120
+ )
@@ -0,0 +1,140 @@
1
+ # Copyright 2026 ray-hive contributors
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
+ """Serializable secret references resolved only by trusted HS2-connecting nodes."""
16
+
17
+ from __future__ import annotations
18
+
19
+ import os
20
+ from dataclasses import dataclass, field, replace
21
+ from typing import Protocol, runtime_checkable
22
+
23
+ from ray_hive._errors import HiveAuthenticationError, HiveConfigurationError
24
+
25
+
26
+ @dataclass(frozen=True, slots=True)
27
+ class CredentialContext:
28
+ """Security boundary to which resolved credentials must be bound."""
29
+
30
+ endpoint: str
31
+ driver: str
32
+ auth: str
33
+ tls_server_name: str | None
34
+ audience: str | None = None
35
+
36
+
37
+ @dataclass(frozen=True, slots=True)
38
+ class HiveCredentials:
39
+ """Ephemeral authentication material that must never enter a task spec."""
40
+
41
+ username: str | None = None
42
+ password: str | None = field(default=None, repr=False)
43
+ token: str | None = field(default=None, repr=False)
44
+
45
+
46
+ @runtime_checkable
47
+ class CredentialProvider(Protocol):
48
+ """A serializable strategy resolved only by a trusted HS2-connecting node."""
49
+
50
+ def bind(self, context: CredentialContext) -> CredentialProvider:
51
+ """Return a provider restricted to the supplied connection context."""
52
+
53
+ def resolve(self, context: CredentialContext) -> HiveCredentials:
54
+ """Resolve ephemeral authentication material for the supplied context."""
55
+
56
+
57
+ @dataclass(frozen=True, slots=True)
58
+ class SecretRef:
59
+ """An immutable reference to one secret value, never the value itself."""
60
+
61
+ provider: str
62
+ key: str
63
+ allowed_context: CredentialContext | None = field(default=None, repr=False)
64
+
65
+ def __post_init__(self) -> None:
66
+ if self.provider != "env":
67
+ raise HiveConfigurationError(f"Unsupported secret provider: {self.provider!r}")
68
+ if not self.key or "\x00" in self.key:
69
+ raise HiveConfigurationError(
70
+ "Secret reference key must be non-empty and contain no NUL"
71
+ )
72
+
73
+ @classmethod
74
+ def env(cls, variable: str) -> SecretRef:
75
+ """Create a reference resolved from the local HS2 client's environment."""
76
+
77
+ return cls(provider="env", key=variable)
78
+
79
+ def bind(self, context: CredentialContext) -> SecretRef:
80
+ """Bind this reference to exactly one endpoint and authentication context."""
81
+
82
+ if self.allowed_context is not None and self.allowed_context != context:
83
+ raise HiveConfigurationError("Secret reference is already bound to another endpoint")
84
+ return replace(self, allowed_context=context)
85
+
86
+ def resolve_value(self, context: CredentialContext) -> str:
87
+ """Resolve the referenced value after validating its endpoint binding."""
88
+
89
+ if self.allowed_context is None or self.allowed_context != context:
90
+ raise HiveAuthenticationError("Secret reference is not bound to this endpoint")
91
+ try:
92
+ return os.environ[self.key]
93
+ except KeyError as exc:
94
+ raise HiveAuthenticationError(
95
+ f"Required environment credential {self.key!r} is unavailable"
96
+ ) from exc
97
+
98
+ def resolve(self, context: CredentialContext) -> HiveCredentials:
99
+ """Resolve a direct secret reference as a password credential."""
100
+
101
+ return HiveCredentials(password=self.resolve_value(context))
102
+
103
+ def __repr__(self) -> str:
104
+ return (
105
+ f"SecretRef(provider={self.provider!r}, key={self.key!r}, "
106
+ f"bound={self.allowed_context is not None})"
107
+ )
108
+
109
+
110
+ @dataclass(frozen=True, slots=True)
111
+ class EnvironmentCredentialProvider:
112
+ """Resolve optional username, password, and token environment references."""
113
+
114
+ username: str | None = None
115
+ password: SecretRef | None = None
116
+ token: SecretRef | None = None
117
+ allowed_context: CredentialContext | None = field(default=None, repr=False)
118
+
119
+ def bind(self, context: CredentialContext) -> EnvironmentCredentialProvider:
120
+ """Bind every contained reference to one connection context."""
121
+
122
+ if self.allowed_context is not None and self.allowed_context != context:
123
+ raise HiveConfigurationError("Credential provider is already bound to another endpoint")
124
+ return replace(
125
+ self,
126
+ password=self.password.bind(context) if self.password else None,
127
+ token=self.token.bind(context) if self.token else None,
128
+ allowed_context=context,
129
+ )
130
+
131
+ def resolve(self, context: CredentialContext) -> HiveCredentials:
132
+ """Resolve authentication material only when the binding is exact."""
133
+
134
+ if self.allowed_context != context:
135
+ raise HiveAuthenticationError("Credential provider is not bound to this endpoint")
136
+ return HiveCredentials(
137
+ username=self.username,
138
+ password=self.password.resolve_value(context) if self.password else None,
139
+ token=self.token.resolve_value(context) if self.token else None,
140
+ )
ray_hive/_errors.py ADDED
@@ -0,0 +1,157 @@
1
+ # Copyright 2026 ray-hive contributors
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
+ """Public, stable error contracts for ray-hive."""
16
+
17
+ from __future__ import annotations
18
+
19
+ from enum import StrEnum
20
+
21
+
22
+ class HiveErrorCode(StrEnum):
23
+ """Stable conformance error codes."""
24
+
25
+ CONFIG = "CONFIG"
26
+ AUTH = "AUTH"
27
+ PERMISSION = "PERMISSION"
28
+ QUERY_COMPILE = "QUERY_COMPILE"
29
+ QUERY_EXECUTION = "QUERY_EXECUTION"
30
+ SCHEMA = "SCHEMA"
31
+ PROTOCOL = "PROTOCOL"
32
+ TRANSPORT_BEFORE_OUTPUT = "TRANSPORT_BEFORE_OUTPUT"
33
+ TRANSPORT_AFTER_OUTPUT = "TRANSPORT_AFTER_OUTPUT"
34
+ CANCELLED = "CANCELLED"
35
+ CLEANUP = "CLEANUP"
36
+
37
+
38
+ class HiveError(Exception):
39
+ """Base exception with stable, sanitized machine-readable attributes."""
40
+
41
+ default_error_code = HiveErrorCode.CONFIG
42
+ default_retryable = False
43
+ default_partial_output = False
44
+
45
+ def __init__(
46
+ self,
47
+ safe_message: str,
48
+ *,
49
+ error_code: HiveErrorCode | None = None,
50
+ retryable: bool | None = None,
51
+ partial_output: bool | None = None,
52
+ operation_state: str | None = None,
53
+ ) -> None:
54
+ super().__init__(safe_message)
55
+ self.error_code = error_code or self.default_error_code
56
+ self.retryable = self.default_retryable if retryable is None else retryable
57
+ self.partial_output = (
58
+ self.default_partial_output if partial_output is None else partial_output
59
+ )
60
+ self.operation_state = operation_state
61
+
62
+ def __repr__(self) -> str:
63
+ return (
64
+ f"{type(self).__name__}(error_code={self.error_code.value!r}, "
65
+ f"retryable={self.retryable!r}, partial_output={self.partial_output!r}, "
66
+ f"operation_state={self.operation_state!r})"
67
+ )
68
+
69
+ def mark_partial_output(self) -> HiveError:
70
+ """Preserve the concrete error while recording that output was published."""
71
+
72
+ self.partial_output = True
73
+ self.retryable = False
74
+ if self.error_code is HiveErrorCode.TRANSPORT_BEFORE_OUTPUT:
75
+ self.error_code = HiveErrorCode.TRANSPORT_AFTER_OUTPUT
76
+ return self
77
+
78
+
79
+ class HiveConfigurationError(HiveError):
80
+ """Raised for invalid or insecure configuration."""
81
+
82
+
83
+ class HiveDependencyError(HiveConfigurationError):
84
+ """Raised when an explicitly selected optional dependency is unavailable."""
85
+
86
+
87
+ class HiveAuthenticationError(HiveError):
88
+ """Raised when HiveServer2 authentication fails."""
89
+
90
+ default_error_code = HiveErrorCode.AUTH
91
+
92
+
93
+ class HivePermissionError(HiveError):
94
+ """Raised when HiveServer2 rejects an authorized operation."""
95
+
96
+ default_error_code = HiveErrorCode.PERMISSION
97
+
98
+
99
+ class HivePlanningError(HiveError):
100
+ """Raised when a logical read cannot be compiled safely."""
101
+
102
+ default_error_code = HiveErrorCode.QUERY_COMPILE
103
+
104
+
105
+ class HiveSchemaError(HiveError):
106
+ """Raised for unsupported types or schema drift."""
107
+
108
+ default_error_code = HiveErrorCode.SCHEMA
109
+
110
+
111
+ class HiveQueryError(HiveError):
112
+ """Raised when an accepted Hive query fails during execution."""
113
+
114
+ default_error_code = HiveErrorCode.QUERY_EXECUTION
115
+
116
+
117
+ class HiveProtocolError(HiveError):
118
+ """Raised when the server violates the negotiated HS2 protocol."""
119
+
120
+ default_error_code = HiveErrorCode.PROTOCOL
121
+
122
+
123
+ class HiveCompatibilityError(HiveProtocolError):
124
+ """Raised when a runtime version or capability is unsupported."""
125
+
126
+
127
+ class HiveTransportError(HiveError):
128
+ """Raised when the HS2 transport fails."""
129
+
130
+ default_error_code = HiveErrorCode.TRANSPORT_BEFORE_OUTPUT
131
+ default_retryable = True
132
+
133
+ @classmethod
134
+ def after_output(
135
+ cls, safe_message: str, *, operation_state: str | None = None
136
+ ) -> HiveTransportError:
137
+ """Construct a non-retryable transport error after a block was published."""
138
+
139
+ return cls(
140
+ safe_message,
141
+ error_code=HiveErrorCode.TRANSPORT_AFTER_OUTPUT,
142
+ retryable=False,
143
+ partial_output=True,
144
+ operation_state=operation_state,
145
+ )
146
+
147
+
148
+ class HiveCancellationError(HiveError):
149
+ """Raised when a read is cancelled."""
150
+
151
+ default_error_code = HiveErrorCode.CANCELLED
152
+
153
+
154
+ class HiveCleanupError(HiveError):
155
+ """Raised when required resource cleanup fails."""
156
+
157
+ default_error_code = HiveErrorCode.CLEANUP
@@ -0,0 +1,15 @@
1
+ # Copyright 2026 ray-hive contributors
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
+ """Internal HiveServer2 protocol implementation."""