Skip to content

Commit 8cac329

Browse files
Merge branch 'main' into aravind-segu/NotebookOAuth
2 parents c801502 + c365716 commit 8cac329

File tree

96 files changed

+1657
-947
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+1657
-947
lines changed

.codegen/_openapi_sha

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
b54bbd860200d735fa2c306ec1559090625370e6
1+
27cebd58ae24e19c95c675db3a93b6046abaca2a

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,6 @@ databricks/sdk/service/tags.py linguist-generated=true
3333
databricks/sdk/service/vectorsearch.py linguist-generated=true
3434
databricks/sdk/service/workspace.py linguist-generated=true
3535
test_http_call.py linguist-generated=true
36+
test_idempotency.py linguist-generated=true
3637
test_json_marshall.py linguist-generated=true
3738
test_lro_call.py linguist-generated=true

.release_metadata.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"timestamp": "2025-10-23 13:43:03+0000"
2+
"timestamp": "2025-11-05 06:52:11+0000"
33
}

CHANGELOG.md

Lines changed: 141 additions & 92 deletions
Large diffs are not rendered by default.

NEXT_CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
# NEXT CHANGELOG
22

3-
## Release v0.71.0
3+
## Release v0.74.0
44

55
### New Features and Improvements
66

7+
### Security
8+
79
### Bug Fixes
810

911
### Documentation

databricks/sdk/__init__.py

Lines changed: 25 additions & 25 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

databricks/sdk/common/lro.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ def __init__(self, *, timeout: Optional[timedelta] = None):
1212
"""
1313
Args:
1414
timeout: The timeout for the Long Running Operations.
15-
If not set, the default timeout is 20 minutes.
15+
if not set, then operation will wait forever.
1616
"""
17-
self.timeout = timeout or timedelta(minutes=20)
17+
self.timeout = timeout

databricks/sdk/mixins/files.py

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
from .._base_client import _BaseClient, _RawResponse, _StreamingResponse
3434
from .._property import _cached_property
3535
from ..config import Config
36-
from ..errors import AlreadyExists, NotFound, PermissionDenied
36+
from ..errors import AlreadyExists, InternalError, NotFound, PermissionDenied
3737
from ..errors.mapper import _error_mapper
3838
from ..retries import retried
3939
from ..service import files
@@ -1064,7 +1064,7 @@ def _get_optimized_performance_parameters_for_upload(
10641064
def upload(
10651065
self,
10661066
file_path: str,
1067-
content: BinaryIO,
1067+
contents: BinaryIO,
10681068
*,
10691069
overwrite: Optional[bool] = None,
10701070
part_size: Optional[int] = None,
@@ -1076,7 +1076,7 @@ def upload(
10761076
10771077
:param file_path: str
10781078
The absolute remote path of the target file, e.g. /Volumes/path/to/your/file
1079-
:param content: BinaryIO
1079+
:param contents: BinaryIO
10801080
The contents of the file to upload. This must be a BinaryIO stream.
10811081
:param overwrite: bool (optional)
10821082
If true, an existing file will be overwritten. When not specified, assumed True.
@@ -1096,7 +1096,7 @@ def upload(
10961096

10971097
if self._config.disable_experimental_files_api_client:
10981098
_LOG.info("Disable experimental files API client, will use the original upload method.")
1099-
super().upload(file_path=file_path, contents=content, overwrite=overwrite)
1099+
super().upload(file_path=file_path, contents=contents, overwrite=overwrite)
11001100
return UploadStreamResult()
11011101

11021102
_LOG.debug(f"Uploading file from BinaryIO stream")
@@ -1107,12 +1107,12 @@ def upload(
11071107

11081108
# Determine content length if the stream is seekable
11091109
content_length = None
1110-
if content.seekable():
1110+
if contents.seekable():
11111111
_LOG.debug(f"Uploading using seekable mode")
11121112
# If the stream is seekable, we can read its size.
1113-
content.seek(0, os.SEEK_END)
1114-
content_length = content.tell()
1115-
content.seek(0)
1113+
contents.seek(0, os.SEEK_END)
1114+
content_length = contents.tell()
1115+
contents.seek(0)
11161116

11171117
# Get optimized part size and batch size based on content length and provided part size
11181118
optimized_part_size, optimized_batch_size = self._get_optimized_performance_parameters_for_upload(
@@ -1134,18 +1134,20 @@ def upload(
11341134
f"Upload context: part_size={ctx.part_size}, batch_size={ctx.batch_size}, content_length={ctx.content_length}"
11351135
)
11361136

1137-
if ctx.use_parallel:
1138-
self._parallel_upload_from_stream(ctx, content)
1137+
if ctx.use_parallel and (
1138+
ctx.content_length is None or ctx.content_length >= self._config.files_ext_multipart_upload_min_stream_size
1139+
):
1140+
self._parallel_upload_from_stream(ctx, contents)
11391141
return UploadStreamResult()
11401142
elif ctx.content_length is not None:
1141-
self._upload_single_thread_with_known_size(ctx, content)
1143+
self._upload_single_thread_with_known_size(ctx, contents)
11421144
return UploadStreamResult()
11431145
else:
11441146
_LOG.debug(f"Uploading using non-seekable mode")
11451147
# If the stream is not seekable, we cannot determine its size.
11461148
# We will use a multipart upload.
11471149
_LOG.debug(f"Using multipart upload for non-seekable input stream of unknown size for file {file_path}")
1148-
self._single_thread_multipart_upload(ctx, content)
1150+
self._single_thread_multipart_upload(ctx, contents)
11491151
return UploadStreamResult()
11501152

11511153
def upload_from(
@@ -1206,7 +1208,7 @@ def upload_from(
12061208
use_parallel=use_parallel,
12071209
parallelism=parallelism,
12081210
)
1209-
if ctx.use_parallel:
1211+
if ctx.use_parallel and ctx.content_length >= self._config.files_ext_multipart_upload_min_stream_size:
12101212
self._parallel_upload_from_file(ctx)
12111213
return UploadFileResult()
12121214
else:
@@ -1459,8 +1461,9 @@ def _parallel_multipart_upload_from_stream(
14591461
# Do the first part read ahead
14601462
pre_read_buffer = content.read(ctx.part_size)
14611463
if not pre_read_buffer:
1462-
self._complete_multipart_upload(ctx, {}, session_token)
1463-
return
1464+
raise FallbackToUploadUsingFilesApi(
1465+
b"", "Falling back to single-shot upload with Files API due to empty input stream"
1466+
)
14641467
try:
14651468
etag = self._do_upload_one_part(
14661469
ctx, cloud_provider_session, 1, 0, len(pre_read_buffer), session_token, BytesIO(pre_read_buffer)
@@ -1650,6 +1653,13 @@ def _do_upload_one_part(
16501653
raise FallbackToUploadUsingFilesApi(None, "Presigned URLs are disabled")
16511654
else:
16521655
raise e from None
1656+
except InternalError as e:
1657+
if self._is_presigned_urls_network_zone_error(e):
1658+
raise FallbackToUploadUsingFilesApi(
1659+
None, "Presigned URLs are not supported in the current network zone"
1660+
)
1661+
else:
1662+
raise e from None
16531663

16541664
upload_part_urls = upload_part_urls_response.get("upload_part_urls", [])
16551665
if len(upload_part_urls) == 0:
@@ -1760,6 +1770,13 @@ def _perform_multipart_upload(
17601770
raise FallbackToUploadUsingFilesApi(buffer, "Presigned URLs are disabled")
17611771
else:
17621772
raise e from None
1773+
except InternalError as e:
1774+
if chunk_offset == 0 and self._is_presigned_urls_network_zone_error(e):
1775+
raise FallbackToUploadUsingFilesApi(
1776+
buffer, "Presigned URLs are not supported in the current network zone"
1777+
)
1778+
else:
1779+
raise e from None
17631780

17641781
upload_part_urls = upload_part_urls_response.get("upload_part_urls", [])
17651782
if len(upload_part_urls) == 0:
@@ -1917,6 +1934,13 @@ def _is_presigned_urls_disabled_error(self, e: PermissionDenied) -> bool:
19171934
return True
19181935
return False
19191936

1937+
def _is_presigned_urls_network_zone_error(self, e: InternalError) -> bool:
1938+
error_infos = e.get_error_info()
1939+
for error_info in error_infos:
1940+
if error_info.reason == "FILES_API_REQUESTER_NETWORK_ZONE_UNKNOWN":
1941+
return True
1942+
return False
1943+
19201944
def _perform_resumable_upload(
19211945
self,
19221946
ctx: _UploadContext,
@@ -1966,6 +1990,13 @@ def _perform_resumable_upload(
19661990
raise FallbackToUploadUsingFilesApi(pre_read_buffer, "Presigned URLs are disabled")
19671991
else:
19681992
raise e from None
1993+
except InternalError as e:
1994+
if self._is_presigned_urls_network_zone_error(e):
1995+
raise FallbackToUploadUsingFilesApi(
1996+
pre_read_buffer, "Presigned URLs are not supported in the current network zone"
1997+
)
1998+
else:
1999+
raise e from None
19692000

19702001
resumable_upload_url_node = resumable_upload_url_response.get("resumable_upload_url")
19712002
if not resumable_upload_url_node:
@@ -2350,6 +2381,11 @@ def _create_download_url(self, file_path: str) -> CreateDownloadUrlResponse:
23502381
raise FallbackToDownloadUsingFilesApi(f"Presigned URLs are disabled")
23512382
else:
23522383
raise e from None
2384+
except InternalError as e:
2385+
if self._is_presigned_urls_network_zone_error(e):
2386+
raise FallbackToDownloadUsingFilesApi("Presigned URLs are not supported in the current network zone")
2387+
else:
2388+
raise e from None
23532389

23542390
def _init_download_response_presigned_api(self, file_path: str, added_headers: dict[str, str]) -> DownloadResponse:
23552391
"""

databricks/sdk/retries.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ def _backoff(attempt: int) -> float:
103103

104104
def poll(
105105
fn: Callable[[], Tuple[Optional[T], Optional[RetryError]]],
106-
timeout: timedelta = timedelta(minutes=20),
106+
timeout: Optional[timedelta] = None,
107107
clock: Optional[Clock] = None,
108108
) -> T:
109109
"""Poll a function until it succeeds or times out.
@@ -118,7 +118,7 @@ def poll(
118118
Return (None, RetryError.continues("msg")) to continue polling.
119119
Return (None, RetryError.halt(err)) to stop with error.
120120
Return (result, None) on success.
121-
:param timeout: Maximum time to poll (default: 20 minutes)
121+
:param timeout: Maximum time to poll. If None, polls indefinitely.
122122
:param clock: Clock implementation for testing (default: RealClock)
123123
:returns: The result of the successful function call
124124
:raises TimeoutError: If the timeout is reached
@@ -138,7 +138,7 @@ def check_operation():
138138
if clock is None:
139139
clock = RealClock()
140140

141-
deadline = clock.time() + timeout.total_seconds()
141+
deadline = float("inf") if timeout is None else clock.time() + timeout.total_seconds()
142142
attempt = 0
143143
last_err = None
144144

databricks/sdk/service/agentbricks.py

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)