From 8de8d384341ea9dd72f9d6861895da05f8a487bc Mon Sep 17 00:00:00 2001 From: Divyansh Vijayvergia <171924202+Divyansh-db@users.noreply.github.com> Date: Thu, 11 Jun 2026 19:50:23 +0200 Subject: [PATCH] Release databricks-sdk-py v0.117.0 (#1472) Update SDK from the Code generator ## How is this tested? CI --- .claude/commands/write-pr-description.md | 77 -- .codegen.json | 17 +- .codegen/_openapi_sha | 1 - .github/workflows/next-changelog.yml | 108 -- .vscode/settings.json | 11 - CHANGELOG.md | 36 + NEXT_CHANGELOG.md | 20 - databricks/sdk/service/bundledeployments.py | 8 +- databricks/sdk/service/catalog.py | 6 +- databricks/sdk/service/dashboards.py | 3 +- databricks/sdk/service/database.py | 61 + databricks/sdk/service/marketplace.py | 3 +- databricks/sdk/service/ml.py | 42 +- databricks/sdk/service/postgres.py | 61 + databricks/sdk/service/provisioning.py | 3 +- databricks/sdk/service/settings.py | 6 +- databricks/sdk/service/sql.py | 14 +- databricks/sdk/version.py | 2 +- docs/account/provisioning/storage.rst | 6 +- docs/dbdataclasses/catalog.rst | 4 +- docs/dbdataclasses/database.rst | 11 + docs/dbdataclasses/postgres.rst | 11 + docs/dbdataclasses/sql.rst | 1 - docs/workspace/catalog/external_locations.rst | 27 +- .../workspace/catalog/storage_credentials.rst | 19 +- docs/workspace/compute/clusters.rst | 3 +- docs/workspace/dashboards/lakeview.rst | 3 +- docs/workspace/iam/permissions.rst | 2 +- docs/workspace/ml/model_registry.rst | 9 +- docs/workspace/sql/warehouses.rst | 4 +- docs/workspace/workspace/workspace.rst | 19 +- pyproject.toml | 3 + tagging.py | 1057 ----------------- uv.lock | 240 ++-- 34 files changed, 400 insertions(+), 1498 deletions(-) delete mode 100644 .claude/commands/write-pr-description.md delete mode 100755 .codegen/_openapi_sha delete mode 100755 .github/workflows/next-changelog.yml delete mode 100644 .vscode/settings.json delete mode 100644 NEXT_CHANGELOG.md delete mode 100755 tagging.py diff --git a/.claude/commands/write-pr-description.md b/.claude/commands/write-pr-description.md deleted file mode 100644 index 6889c809c..000000000 --- a/.claude/commands/write-pr-description.md +++ /dev/null @@ -1,77 +0,0 @@ ---- -description: > - Write or improve a GitHub pull request description. Use when the user asks - to "write a PR description", "improve the PR description", "update the PR - body", or provides a PR URL and asks for a better description. - Keywords: PR description, pull request summary, PR body, PR writeup. -allowed-tools: [Read, Glob, Grep, Bash, ToolSearch] ---- - -# Write PR Description - -Generate a structured PR description that explains **why** the change exists, -not just what files were touched. - -## When to use - -- The user provides a PR URL and asks to write or improve its description. -- The user asks to draft a PR description for the current branch. -- An agent needs to open a PR and wants a high-quality description. - -## Workflow - -### Phase 1: Gather context - -Collect all the information needed to understand the change: - -1. **Read the PR metadata** — title, current description, author, branch name. -2. **Read the full diff** — understand every file changed, every function added - or removed, every signature change. Do not skip files. -3. **Read surrounding code when needed** — if the diff modifies an interface or - a struct, read the full file to understand how the change fits into the - existing architecture. -4. **Check for linked issues or docs** — the PR or commit messages may - reference issues, design docs, or RFCs that explain motivation. - -### Phase 2: Analyze the change - -Before writing, answer these questions internally: - -- **What was the status quo before this PR?** What limitation, bug, or missing - capability existed? -- **Why is this change needed now?** What concrete problem does it solve? Who - benefits? -- **What are the key design decisions?** Why was this approach chosen over - alternatives? -- **What is the new API surface?** Any new public types, functions, or - configuration options? -- **What are the architectural changes?** How does the internal structure - change? What moves where? What gets refactored? -- **Are there behavioral changes?** If not, say so explicitly — this reassures - reviewers. - -### Phase 3: Write the description - -Use the structure defined in `.github/PULL_REQUEST_TEMPLATE.md` as the -template. The tone should be direct and technical. Write for a reviewer who is -familiar with the codebase but has not seen this change before. - -**Key principles:** - -- **Lead with why, not what.** The diff already shows the what. The description - should explain the reasoning that is not visible in the code. -- **Be specific.** Instead of "improves extensibility", say "allows internal - tools to compose their own auth chain from individual credential strategies". -- **Name things.** Reference actual types, functions, files, and config fields. - Use backticks for code references. -- **State non-changes explicitly.** If the PR is a refactor with no behavioral - change, say "No behavioral changes. Existing users are unaffected." This is - valuable information for reviewers. -- **Keep the summary to one or two sentences.** It should be scannable. -- **Use the motivation section to tell a story.** What was the problem? Why - couldn't it be solved before? What does this PR unlock? - -### Phase 4: Update the PR - -Use the GitHub MCP tools or `gh` CLI to update the PR body with the new -description. Confirm with the user before pushing if unsure. diff --git a/.codegen.json b/.codegen.json index 2ef68a1a1..1626d2f43 100644 --- a/.codegen.json +++ b/.codegen.json @@ -1,9 +1,24 @@ { "mode": "py_v0", - "api_changelog": true, + "api_changelog": false, "version": { "databricks/sdk/version.py": "__version__ = \"$VERSION\"" }, + "sync": { + "skip_from_source": [ + "BUILD.bazel", + "CLAUDE.md", + ".claude", + ".codegen", + ".cursor", + ".cursorrules", + ".nextchanges", + "changelog_validate", + "next-changelog.yml", + "README.universe.md", + "tagging.py" + ] + }, "toolchain": { "required": ["python3.12", "uv"], "pre_setup": [ diff --git a/.codegen/_openapi_sha b/.codegen/_openapi_sha deleted file mode 100755 index a08cdd46e..000000000 --- a/.codegen/_openapi_sha +++ /dev/null @@ -1 +0,0 @@ -55d22d7e7879e6be815cfe8e3e4cf7130f822c94 \ No newline at end of file diff --git a/.github/workflows/next-changelog.yml b/.github/workflows/next-changelog.yml deleted file mode 100755 index 475157d1f..000000000 --- a/.github/workflows/next-changelog.yml +++ /dev/null @@ -1,108 +0,0 @@ -# Generated file. DO NOT EDIT. -name: Check for NEXT_CHANGELOG.md Changes - -on: - # Use pull_request_target to have access to GitHub API - pull_request_target: - types: [opened, synchronize, reopened, edited] - -jobs: - check-next-changelog: - # Allow Dependabot PRs to pass without a changelog entry - if: github.actor != 'dependabot[bot]' - runs-on: - group: databricks-protected-runner-group - labels: linux-ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Fetch list of changed files - id: changed-files - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - # Use the GitHub API to fetch changed files - files=$(gh pr view ${{ github.event.pull_request.number }} --json files -q '.files[].path') - - # Sanitize to avoid code injection - sanitized_files=$(echo "$files" | sed 's/[^a-zA-Z0-9._/-]/_/g') - - # Store the sanitized list of files in a temporary file to avoid env variable issues - echo "$sanitized_files" > modified_files.txt - - - name: Fetch PR message - id: pr-message - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - # Use the GitHub API to fetch the PR message - pr_message=$(gh pr view ${{ github.event.pull_request.number }} --json body -q '.body') - - # Sanitize the PR message to avoid code injection, keeping the equal sign - sanitized_pr_message=$(echo "$pr_message" | sed 's/[^a-zA-Z0-9._/-=]/_/g') - - # Store the sanitized PR message - echo "$sanitized_pr_message" > pr_message.txt - - - name: Verify NEXT_CHANGELOG.md was modified or PR message contains NO_CHANGELOG=true - run: | - # Read the sanitized files and PR message from the temporary files - modified_files=$(cat modified_files.txt) - pr_message=$(cat pr_message.txt) - - # Check if NEXT_CHANGELOG.md exists in the list of changed files - echo "Changed files: $modified_files" - if ! echo "$modified_files" | grep -q "NEXT_CHANGELOG.md"; then - echo "NEXT_CHANGELOG.md not modified." - - # Check if PR message contains NO_CHANGELOG=true - if echo "$pr_message" | grep -q "NO_CHANGELOG=true"; then - echo "NO_CHANGELOG=true found in PR message. Skipping changelog check." - exit 0 - else - echo "WARNING: file NEXT_CHANGELOG.md not changed. If this is expected, add NO_CHANGELOG=true to the PR message." - exit 1 - fi - fi - - - name: Comment on PR with instructions if needed - if: failure() # This step will only run if the previous step fails (i.e., if NEXT_CHANGELOG.md was not modified and NO_CHANGELOG=true was not in the PR message) - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - # Check if a comment exists with the instructions - previous_comment_ids=$(gh api "repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" \ - --jq '.[] | select(.body | startswith("")) | .id') - echo "Previous comment IDs: $previous_comment_ids" - - # If no previous comment exists, add one with instructions - if [ -z "$previous_comment_ids" ]; then - echo "Adding instructions comment." - gh pr comment ${{ github.event.pull_request.number }} --body \ - " - Please ensure that the NEXT_CHANGELOG.md file is updated with any relevant changes. - If this is not necessary for your PR, please include the following in your PR description: - NO_CHANGELOG=true - and rerun the job." - fi - - - name: Delete instructions comment on success - if: success() # This step will only run if the previous check passed (i.e., if NEXT_CHANGELOG.md was modified or NO_CHANGELOG=true is in the PR message) - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - # Check if there is a previous instructions comment - previous_comment_ids=$(gh api "repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" \ - --jq '.[] | select(.body | startswith("")) | .id') - - # If a comment exists, delete it - if [ -n "$previous_comment_ids" ]; then - echo "Deleting previous instructions comment." - for comment_id in $previous_comment_ids; do - gh api "repos/${{ github.repository }}/issues/comments/$comment_id" --method DELETE - done - else - echo "No instructions comment found to delete." - fi diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index c2c2020ec..000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "python.testing.pytestArgs": [ - "tests" - ], - "python.testing.unittestEnabled": false, - "python.testing.pytestEnabled": true, - "python.envFile": "${workspaceRoot}/.env", - "databricks.python.envFile": "${workspaceFolder}/.env", - "jupyter.interactiveWindow.cellMarker.codeRegex": "^# COMMAND ----------|^# Databricks notebook source|^(#\\s*%%|#\\s*\\|#\\s*In\\[\\d*?\\]|#\\s*In\\[ \\])", - "jupyter.interactiveWindow.cellMarker.default": "# COMMAND ----------" -} diff --git a/CHANGELOG.md b/CHANGELOG.md index a83bec38b..8509eeed1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,41 @@ # Version changelog +## Release v0.117.0 (2026-06-11) + +### API Changes +* Add `type_overrides` field for `databricks.sdk.service.database.SyncedTableSpec`. +* Add `type_overrides` field for `databricks.sdk.service.postgres.SyncedTableSyncedTableSpec`. +* Change `resource_id` field for `databricks.sdk.service.bundledeployments.Operation` to no longer be required. +* [Breaking] Change `resource_id` field for `databricks.sdk.service.bundledeployments.Operation` to no longer be required. + +### Bug Fixes +* Cache tokens minted by `DatabricksOidcTokenSource` (Workload Identity +Federation / account-wide token federation). Previously a fresh +`/oidc/v1/token` exchange was performed on every authenticated API +call, adding latency, amplifying transient federation-policy errors, +and hitting OIDC token-endpoint rate limits. The token source now +reuses the cached token until it is stale or expired, fetching a fresh +ID token on each refresh to support rotation. +* Make `WorkspaceClient.dbutils` lazy (`functools.cached_property`). +The constructor no longer imports `databricks.sdk.runtime` eagerly, so +on Spark Connect (shared-access-mode) clusters, consumers that never +read `.dbutils` — such as `dbt-databricks` Python models — no longer +crash with `CONTEXT_UNAVAILABLE_FOR_REMOTE_CLIENT` during client +construction. +* Fall back to the remote runtime implementation when the legacy user +namespace cannot be materialized. On Spark Connect runtimes (e.g. +shared-access-mode clusters), importing `databricks.sdk.runtime` — +which happens when constructing a `WorkspaceClient` on such a cluster +— tried to build a legacy `SparkContext` and raised +`CONTEXT_UNAVAILABLE_FOR_REMOTE_CLIENT` at import time. It now logs a +warning and falls back to the Spark Connect-compatible remote +implementation instead of crashing. + +### Internal Changes +* Declare `urllib3` as an explicit dependency. The SDK imports it +directly; previously it was only available transitively through +`requests`. No resolution change for users. + ## Release v0.116.0 (2026-06-10) ### API Changes diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md deleted file mode 100644 index c91bb5225..000000000 --- a/NEXT_CHANGELOG.md +++ /dev/null @@ -1,20 +0,0 @@ -# NEXT CHANGELOG - -## Release v0.117.0 - -### New Features and Improvements - -### Security - -### Bug Fixes - -* Fall back to the remote runtime implementation when the legacy user namespace cannot be materialized. On Spark Connect runtimes (e.g. shared-access-mode clusters), importing `databricks.sdk.runtime` — which happens when constructing a `WorkspaceClient` on such a cluster — tried to build a legacy `SparkContext` and raised `CONTEXT_UNAVAILABLE_FOR_REMOTE_CLIENT` at import time. It now logs a warning and falls back to the Spark Connect-compatible remote implementation instead of crashing. -* Cache tokens minted by `DatabricksOidcTokenSource` (Workload Identity Federation / account-wide token federation). Previously a fresh `/oidc/v1/token` exchange was performed on every authenticated API call, adding latency, amplifying transient federation-policy errors, and hitting OIDC token-endpoint rate limits. The token source now reuses the cached token until it is stale or expired, fetching a fresh ID token on each refresh to support rotation. - -### Documentation - -### Breaking Changes - -### Internal Changes - -### API Changes diff --git a/databricks/sdk/service/bundledeployments.py b/databricks/sdk/service/bundledeployments.py index b1c3922a3..88b34b02b 100755 --- a/databricks/sdk/service/bundledeployments.py +++ b/databricks/sdk/service/bundledeployments.py @@ -422,9 +422,6 @@ class Operation: action_type: OperationActionType """The type of operation performed on this resource.""" - resource_id: str - """ID reference for the actual resource in the workspace (e.g. the job ID, pipeline ID).""" - status: OperationStatus """Whether the operation succeeded or failed.""" @@ -439,6 +436,11 @@ class Operation: """Resource name of the operation. Format: deployments/{deployment_id}/versions/{version_id}/operations/{resource_key}""" + resource_id: Optional[str] = None + """ID of the actual resource in the workspace (e.g. the job ID, pipeline ID). Required for every + operation except CREATE and RECREATE, which produce a new resource whose ID is not yet known + when the operation is recorded.""" + resource_key: Optional[str] = None """Resource identifier within the bundle (e.g. "jobs.foo", "pipelines.bar", "jobs.foo.permissions", "files."). Can be an arbitrary UTF-8 encoded string key. This key links the operation diff --git a/databricks/sdk/service/catalog.py b/databricks/sdk/service/catalog.py index 45b8a75ee..b7810b3c4 100755 --- a/databricks/sdk/service/catalog.py +++ b/databricks/sdk/service/catalog.py @@ -1853,7 +1853,7 @@ def from_dict(cls, d: Dict[str, Any]) -> ConnectionInfo: class ConnectionType(Enum): - """Next Id: 126""" + """Next Id: 127""" BIGQUERY = "BIGQUERY" CONFLUENCE = "CONFLUENCE" @@ -9290,8 +9290,8 @@ def from_dict(cls, d: Dict[str, Any]) -> Securable: class SecurableKind(Enum): - """Latest kind: CONNECTION_ADOBE_CAMPAIGNS_OAUTH_M2M = 345; Next id: 346. Reserved numbers: 316, - 317, 327, 330, 341 (former ENDPOINT_LLM_*, MODEL_SERVICE_STANDARD, + """Latest kind: CONNECTION_CONFLUENT_SCHEMA_REGISTRY_BASIC = 346; Next id: 347. Reserved numbers: + 316, 317, 327, 330, 341 (former ENDPOINT_LLM_*, MODEL_SERVICE_STANDARD, MODEL_SERVICE_SYSTEM_DELTASHARING, MCP_SERVICE_STANDARD).""" TABLE_DB_STORAGE = "TABLE_DB_STORAGE" diff --git a/databricks/sdk/service/dashboards.py b/databricks/sdk/service/dashboards.py index ac66fc011..b3cbaf296 100755 --- a/databricks/sdk/service/dashboards.py +++ b/databricks/sdk/service/dashboards.py @@ -4062,8 +4062,7 @@ def revert(self, dashboard_id: str, *, etag: Optional[str] = None) -> RevertDash UUID identifying the dashboard. :param etag: str (optional) The etag for the dashboard. Optionally, it can be provided to verify that the dashboard has not been - modified from its last retrieval. TODO(TSE-3937): update to new non-CMK-encrypted label when - available + modified from its last retrieval. :returns: :class:`RevertDashboardResponse` """ diff --git a/databricks/sdk/service/database.py b/databricks/sdk/service/database.py index bb00fec41..15e8a216e 100755 --- a/databricks/sdk/service/database.py +++ b/databricks/sdk/service/database.py @@ -1361,6 +1361,10 @@ class SyncedTableSpec: timeseries_key: Optional[str] = None """Time series key to deduplicate (tie-break) rows with the same primary key.""" + type_overrides: Optional[List[SyncedTableSpecTypeOverride]] = None + """Override the default Delta->PG type mapping for specific columns. A TypeOverride with + PG_SPECIFIC_TYPE_UNSPECIFIED is rejected; a valid pg_type must be set.""" + def as_dict(self) -> dict: """Serializes the SyncedTableSpec into a dictionary suitable for use as a JSON request body.""" body = {} @@ -1380,6 +1384,8 @@ def as_dict(self) -> dict: body["source_table_full_name"] = self.source_table_full_name if self.timeseries_key is not None: body["timeseries_key"] = self.timeseries_key + if self.type_overrides: + body["type_overrides"] = [v.as_dict() for v in self.type_overrides] return body def as_shallow_dict(self) -> dict: @@ -1401,6 +1407,8 @@ def as_shallow_dict(self) -> dict: body["source_table_full_name"] = self.source_table_full_name if self.timeseries_key is not None: body["timeseries_key"] = self.timeseries_key + if self.type_overrides: + body["type_overrides"] = self.type_overrides return body @classmethod @@ -1415,6 +1423,59 @@ def from_dict(cls, d: Dict[str, Any]) -> SyncedTableSpec: scheduling_policy=_enum(d, "scheduling_policy", SyncedTableSchedulingPolicy), source_table_full_name=d.get("source_table_full_name", None), timeseries_key=d.get("timeseries_key", None), + type_overrides=_repeated_dict(d, "type_overrides", SyncedTableSpecTypeOverride), + ) + + +class SyncedTableSpecPgSpecificType(Enum): + """PostgreSQL-specific target types that can override the default Delta-to-PG mapping.""" + + PG_SPECIFIC_TYPE_VECTOR = "PG_SPECIFIC_TYPE_VECTOR" + + +@dataclass +class SyncedTableSpecTypeOverride: + """Overrides the default Delta-to-PostgreSQL type mapping for a single column.""" + + column_name: str + """Name of the source column whose target PostgreSQL type should be overridden.""" + + pg_type: SyncedTableSpecPgSpecificType + """PostgreSQL-specific target type to use for the column.""" + + size: Optional[int] = None + """Size parameter for the target type. Required when pg_type is PG_SPECIFIC_TYPE_VECTOR (specifies + the vector dimension, e.g., 1024).""" + + def as_dict(self) -> dict: + """Serializes the SyncedTableSpecTypeOverride into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.column_name is not None: + body["column_name"] = self.column_name + if self.pg_type is not None: + body["pg_type"] = self.pg_type.value + if self.size is not None: + body["size"] = self.size + return body + + def as_shallow_dict(self) -> dict: + """Serializes the SyncedTableSpecTypeOverride into a shallow dictionary of its immediate attributes.""" + body = {} + if self.column_name is not None: + body["column_name"] = self.column_name + if self.pg_type is not None: + body["pg_type"] = self.pg_type + if self.size is not None: + body["size"] = self.size + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> SyncedTableSpecTypeOverride: + """Deserializes the SyncedTableSpecTypeOverride from a dictionary.""" + return cls( + column_name=d.get("column_name", None), + pg_type=_enum(d, "pg_type", SyncedTableSpecPgSpecificType), + size=d.get("size", None), ) diff --git a/databricks/sdk/service/marketplace.py b/databricks/sdk/service/marketplace.py index 1e4454391..701055032 100755 --- a/databricks/sdk/service/marketplace.py +++ b/databricks/sdk/service/marketplace.py @@ -847,7 +847,6 @@ class FileParent: file_parent_type: Optional[FileParentType] = None parent_id: Optional[str] = None - """TODO make the following fields required""" def as_dict(self) -> dict: """Serializes the FileParent into a dictionary suitable for use as a JSON request body.""" @@ -1702,7 +1701,7 @@ class ListingDetail: pricing_model: Optional[str] = None """What the pricing model is (e.g. paid, subscription, paid upfront); should only be present if - cost is paid TODO: Not used yet, should deprecate if we will never use it""" + cost is paid""" privacy_policy_link: Optional[str] = None diff --git a/databricks/sdk/service/ml.py b/databricks/sdk/service/ml.py index f68806141..1264fb15e 100755 --- a/databricks/sdk/service/ml.py +++ b/databricks/sdk/service/ml.py @@ -452,9 +452,9 @@ class AvgFunction: input: str """The input column from which the average is computed. For Kafka sources, use dot-prefixed path - notation (e.g., "value.amount"). For nested fields, the leaf node name is used. TODO(FS-939): - Colon-prefixed notation (e.g., "value:amount") is supported for backwards compatibility but is - deprecated; migrate to dot notation.""" + notation (e.g., "value.amount"). For nested fields, the leaf node name is used. Colon-prefixed + notation (e.g., "value:amount") is supported for backwards compatibility but is deprecated; + migrate to dot notation.""" def as_dict(self) -> dict: """Serializes the AvgFunction into a dictionary suitable for use as a JSON request body.""" @@ -725,9 +725,9 @@ class CountFunction: input: str """The input column from which the count is computed. For Kafka sources, use dot-prefixed path - notation (e.g., "value.amount"). For nested fields, the leaf node name is used. TODO(FS-939): - Colon-prefixed notation (e.g., "value:amount") is supported for backwards compatibility but is - deprecated; migrate to dot notation.""" + notation (e.g., "value.amount"). For nested fields, the leaf node name is used. Colon-prefixed + notation (e.g., "value:amount") is supported for backwards compatibility but is deprecated; + migrate to dot notation.""" def as_dict(self) -> dict: """Serializes the CountFunction into a dictionary suitable for use as a JSON request body.""" @@ -1510,8 +1510,8 @@ def from_dict(cls, d: Dict[str, Any]) -> DeltaTableSource: @dataclass class DirectMtlsConfig: - """Direct connection configs for mTLS, as Kafka Connections do not support mTLS yet (XTA-18030). - Temporarily used until UC Kafka Connections gain mTLS support.""" + """Direct connection configs for mTLS, as Kafka Connections do not support mTLS yet . Temporarily + used until UC Kafka Connections gain mTLS support.""" bootstrap_servers: str """A comma-separated list of host:port pairs for the Kafka bootstrap servers.""" @@ -1591,9 +1591,9 @@ class EntityColumn: """The name of the entity column. For Kafka sources, use dot-prefixed path notation to reference fields within the key or value schema (e.g., "value.user_id", "key.partition_key"). For nested fields, the leaf node name (e.g., "user_id" from "value.trip_details.user_id") is what will be - present in materialized tables and expected to match at query time. TODO(FS-939): Colon-prefixed - notation (e.g., "value:user_id") is supported for backwards compatibility but is deprecated; - migrate to dot notation.""" + present in materialized tables and expected to match at query time. Colon-prefixed notation + (e.g., "value:user_id") is supported for backwards compatibility but is deprecated; migrate to + dot notation.""" def as_dict(self) -> dict: """Serializes the EntityColumn into a dictionary suitable for use as a JSON request body.""" @@ -3545,7 +3545,7 @@ def from_dict(cls, d: Dict[str, Any]) -> LineageContext: @dataclass class LinkedFeature: - """Feature for model version. ([ML-57150] Renamed from Feature to LinkedFeature)""" + """Feature for model version.""" feature_name: Optional[str] = None """Feature name""" @@ -6941,8 +6941,8 @@ class StddevPopFunction: input: str """The input column from which the population standard deviation is computed. For Kafka sources, use dot-prefixed path notation (e.g., "value.amount"). For nested fields, the leaf node name is - used. TODO(FS-939): Colon-prefixed notation (e.g., "value:amount") is supported for backwards - compatibility but is deprecated; migrate to dot notation.""" + used. Colon-prefixed notation (e.g., "value:amount") is supported for backwards compatibility + but is deprecated; migrate to dot notation.""" def as_dict(self) -> dict: """Serializes the StddevPopFunction into a dictionary suitable for use as a JSON request body.""" @@ -7110,8 +7110,8 @@ class StreamConnectionConfig: direct_mtls_config: Optional[DirectMtlsConfig] = None """Direct mTLS configuration for stream platform access. This is only used in the short term until - UC Kafka Connections support mTLS (XTA-18030). Once UC Kafka Connections support mTLS, this will - be deprecated.""" + UC Kafka Connections support mTLS . Once UC Kafka Connections support mTLS, this will be + deprecated.""" uc_connection_name: Optional[str] = None """Name of an existing UC Connection for stream platform access. Must be the correct type for the @@ -7318,9 +7318,9 @@ class SumFunction: input: str """The input column from which the sum is computed. For Kafka sources, use dot-prefixed path - notation (e.g., "value.amount"). For nested fields, the leaf node name is used. TODO(FS-939): - Colon-prefixed notation (e.g., "value:amount") is supported for backwards compatibility but is - deprecated; migrate to dot notation.""" + notation (e.g., "value.amount"). For nested fields, the leaf node name is used. Colon-prefixed + notation (e.g., "value:amount") is supported for backwards compatibility but is deprecated; + migrate to dot notation.""" def as_dict(self) -> dict: """Serializes the SumFunction into a dictionary suitable for use as a JSON request body.""" @@ -7448,8 +7448,8 @@ class TimeseriesColumn: reference fields within the key or value schema (e.g., "value.event_timestamp"). For nested fields, the leaf node name (e.g., "event_timestamp" from "value.event_details.event_timestamp") is what will be present in materialized tables and expected to match at query time. - TODO(FS-939): Colon-prefixed notation (e.g., "value:event_timestamp") is supported for backwards - compatibility but is deprecated; migrate to dot notation.""" + Colon-prefixed notation (e.g., "value:event_timestamp") is supported for backwards compatibility + but is deprecated; migrate to dot notation.""" def as_dict(self) -> dict: """Serializes the TimeseriesColumn into a dictionary suitable for use as a JSON request body.""" diff --git a/databricks/sdk/service/postgres.py b/databricks/sdk/service/postgres.py index f6deee272..38261e8ce 100755 --- a/databricks/sdk/service/postgres.py +++ b/databricks/sdk/service/postgres.py @@ -2821,6 +2821,10 @@ class SyncedTableSyncedTableSpec: timeseries_key: Optional[str] = None """Time series key to deduplicate (tie-break) rows with the same primary key.""" + type_overrides: Optional[List[SyncedTableSyncedTableSpecTypeOverride]] = None + """Override the default Delta->PG type mapping for specific columns. A TypeOverride with + PG_SPECIFIC_TYPE_UNSPECIFIED is rejected; a valid pg_type must be set.""" + def as_dict(self) -> dict: """Serializes the SyncedTableSyncedTableSpec into a dictionary suitable for use as a JSON request body.""" body = {} @@ -2844,6 +2848,8 @@ def as_dict(self) -> dict: body["source_table_full_name"] = self.source_table_full_name if self.timeseries_key is not None: body["timeseries_key"] = self.timeseries_key + if self.type_overrides: + body["type_overrides"] = [v.as_dict() for v in self.type_overrides] return body def as_shallow_dict(self) -> dict: @@ -2869,6 +2875,8 @@ def as_shallow_dict(self) -> dict: body["source_table_full_name"] = self.source_table_full_name if self.timeseries_key is not None: body["timeseries_key"] = self.timeseries_key + if self.type_overrides: + body["type_overrides"] = self.type_overrides return body @classmethod @@ -2885,9 +2893,16 @@ def from_dict(cls, d: Dict[str, Any]) -> SyncedTableSyncedTableSpec: scheduling_policy=_enum(d, "scheduling_policy", SyncedTableSyncedTableSpecSyncedTableSchedulingPolicy), source_table_full_name=d.get("source_table_full_name", None), timeseries_key=d.get("timeseries_key", None), + type_overrides=_repeated_dict(d, "type_overrides", SyncedTableSyncedTableSpecTypeOverride), ) +class SyncedTableSyncedTableSpecPgSpecificType(Enum): + """PostgreSQL-specific target types that can override the default Delta-to-PG mapping.""" + + PG_SPECIFIC_TYPE_VECTOR = "PG_SPECIFIC_TYPE_VECTOR" + + class SyncedTableSyncedTableSpecSyncedTableSchedulingPolicy(Enum): """Scheduling policy of the synced table's underlying pipeline.""" @@ -2896,6 +2911,52 @@ class SyncedTableSyncedTableSpecSyncedTableSchedulingPolicy(Enum): TRIGGERED = "TRIGGERED" +@dataclass +class SyncedTableSyncedTableSpecTypeOverride: + """Overrides the default Delta-to-PostgreSQL type mapping for a single column.""" + + column_name: str + """Name of the source column whose target PostgreSQL type should be overridden.""" + + pg_type: SyncedTableSyncedTableSpecPgSpecificType + """PostgreSQL-specific target type to use for the column.""" + + size: Optional[int] = None + """Size parameter for the target type. Required when pg_type is PG_SPECIFIC_TYPE_VECTOR (specifies + the vector dimension, e.g., 1024).""" + + def as_dict(self) -> dict: + """Serializes the SyncedTableSyncedTableSpecTypeOverride into a dictionary suitable for use as a JSON request body.""" + body = {} + if self.column_name is not None: + body["column_name"] = self.column_name + if self.pg_type is not None: + body["pg_type"] = self.pg_type.value + if self.size is not None: + body["size"] = self.size + return body + + def as_shallow_dict(self) -> dict: + """Serializes the SyncedTableSyncedTableSpecTypeOverride into a shallow dictionary of its immediate attributes.""" + body = {} + if self.column_name is not None: + body["column_name"] = self.column_name + if self.pg_type is not None: + body["pg_type"] = self.pg_type + if self.size is not None: + body["size"] = self.size + return body + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> SyncedTableSyncedTableSpecTypeOverride: + """Deserializes the SyncedTableSyncedTableSpecTypeOverride from a dictionary.""" + return cls( + column_name=d.get("column_name", None), + pg_type=_enum(d, "pg_type", SyncedTableSyncedTableSpecPgSpecificType), + size=d.get("size", None), + ) + + @dataclass class SyncedTableSyncedTableStatus: detailed_state: Optional[SyncedTableState] = None diff --git a/databricks/sdk/service/provisioning.py b/databricks/sdk/service/provisioning.py index 24b986ba1..bf82e9b24 100755 --- a/databricks/sdk/service/provisioning.py +++ b/databricks/sdk/service/provisioning.py @@ -1438,8 +1438,7 @@ class VpcEndpoint: """*""" account_id: Optional[str] = None - """The Databricks account ID that hosts the VPC endpoint configuration. TODO - This may signal an - OpenAPI diff; it does not show up in the generated spec""" + """The Databricks account ID that hosts the VPC endpoint configuration.""" aws_account_id: Optional[str] = None """The AWS Account in which the VPC endpoint object exists.""" diff --git a/databricks/sdk/service/settings.py b/databricks/sdk/service/settings.py index 036e9f7cc..4e86b0ed2 100755 --- a/databricks/sdk/service/settings.py +++ b/databricks/sdk/service/settings.py @@ -5305,11 +5305,7 @@ def from_dict(cls, d: Dict[str, Any]) -> NetworkConnectivityConfiguration: @dataclass class NetworkPolicyEgress: - """The network policies applying for egress traffic. This message is used by the UI/REST API. We - translate this message to the format expected by the dataplane in Lakehouse Network Manager (for - the format expected by the dataplane, see networkconfig.textproto). This policy should be - consistent with [[com.databricks.api.proto.settingspolicy.EgressNetworkPolicy]]. Details see - API-design: https://docs.google.com/document/d/1DKWO_FpZMCY4cF2O62LpwII1lx8gsnDGG-qgE3t3TOA/""" + """The network policies applying for egress traffic.""" network_access: Optional[EgressNetworkPolicyNetworkAccessPolicy] = None """The access policy enforced for egress traffic to the internet.""" diff --git a/databricks/sdk/service/sql.py b/databricks/sdk/service/sql.py index 886259b65..5f193aa45 100755 --- a/databricks/sdk/service/sql.py +++ b/databricks/sdk/service/sql.py @@ -2455,7 +2455,7 @@ class EndpointInfo: please tune max_num_clusters. Supported values: - 2X-Small - X-Small - Small - Medium - Large - X-Large - 2X-Large - 3X-Large - - 4X-Large - 5X-Large""" + - 4X-Large - 5X-Large - Auto""" creator_name: Optional[str] = None """warehouse creator name""" @@ -3070,7 +3070,7 @@ class GetWarehouseResponse: please tune max_num_clusters. Supported values: - 2X-Small - X-Small - Small - Medium - Large - X-Large - 2X-Large - 3X-Large - - 4X-Large - 5X-Large""" + - 4X-Large - 5X-Large - Auto""" creator_name: Optional[str] = None """warehouse creator name""" @@ -6043,11 +6043,7 @@ class SpotInstancePolicy(Enum): +-------+--------------------------------------+--------------------------------+ | AWS | On Demand Driver with Spot Executors | On Demand Driver and Executors | | AZURE | On Demand Driver and Executors | On Demand Driver and Executors | - +-------+--------------------------------------+--------------------------------+ - - While including "spot" in the enum name may limit the the future extensibility of this field - because it limits this enum to denoting "spot or not", this is the field that PM recommends - after discussion with customers per SC-48783.""" + +-------+--------------------------------------+--------------------------------+""" COST_OPTIMIZED = "COST_OPTIMIZED" POLICY_UNSPECIFIED = "POLICY_UNSPECIFIED" @@ -9806,7 +9802,7 @@ def create( max_num_clusters. Supported values: - 2X-Small - X-Small - Small - Medium - Large - X-Large - 2X-Large - 3X-Large - - 4X-Large - 5X-Large + 4X-Large - 5X-Large - Auto :param creator_name: str (optional) warehouse creator name :param enable_photon: bool (optional) @@ -10039,7 +10035,7 @@ def edit( max_num_clusters. Supported values: - 2X-Small - X-Small - Small - Medium - Large - X-Large - 2X-Large - 3X-Large - - 4X-Large - 5X-Large + 4X-Large - 5X-Large - Auto :param creator_name: str (optional) warehouse creator name :param enable_photon: bool (optional) diff --git a/databricks/sdk/version.py b/databricks/sdk/version.py index fcd925265..edbb23462 100644 --- a/databricks/sdk/version.py +++ b/databricks/sdk/version.py @@ -1 +1 @@ -__version__ = "0.116.0" +__version__ = "0.117.0" diff --git a/docs/account/provisioning/storage.rst b/docs/account/provisioning/storage.rst index b9f080e36..25ee5abaa 100644 --- a/docs/account/provisioning/storage.rst +++ b/docs/account/provisioning/storage.rst @@ -16,7 +16,6 @@ .. code-block:: - import os import time from databricks.sdk import AccountClient @@ -26,11 +25,8 @@ storage = a.storage.create( storage_configuration_name=f"sdk-{time.time_ns()}", - root_bucket_info=provisioning.RootBucketInfo(bucket_name=os.environ["TEST_ROOT_BUCKET"]), + root_bucket_info=provisioning.RootBucketInfo(bucket_name=f"sdk-{time.time_ns()}"), ) - - # cleanup - a.storage.delete(storage_configuration_id=storage.storage_configuration_id) Creates a Databricks storage configuration for an account. diff --git a/docs/dbdataclasses/catalog.rst b/docs/dbdataclasses/catalog.rst index b5f52d058..f4237f40a 100755 --- a/docs/dbdataclasses/catalog.rst +++ b/docs/dbdataclasses/catalog.rst @@ -283,7 +283,7 @@ These dataclasses are used in the SDK to represent API requests and responses fo .. py:class:: ConnectionType - Next Id: 126 + Next Id: 127 .. py:attribute:: BIGQUERY :value: "BIGQUERY" @@ -1552,7 +1552,7 @@ These dataclasses are used in the SDK to represent API requests and responses fo .. py:class:: SecurableKind - Latest kind: CONNECTION_ADOBE_CAMPAIGNS_OAUTH_M2M = 345; Next id: 346. Reserved numbers: 316, 317, 327, 330, 341 (former ENDPOINT_LLM_*, MODEL_SERVICE_STANDARD, MODEL_SERVICE_SYSTEM_DELTASHARING, MCP_SERVICE_STANDARD). + Latest kind: CONNECTION_CONFLUENT_SCHEMA_REGISTRY_BASIC = 346; Next id: 347. Reserved numbers: 316, 317, 327, 330, 341 (former ENDPOINT_LLM_*, MODEL_SERVICE_STANDARD, MODEL_SERVICE_SYSTEM_DELTASHARING, MCP_SERVICE_STANDARD). .. py:attribute:: TABLE_DB_STORAGE :value: "TABLE_DB_STORAGE" diff --git a/docs/dbdataclasses/database.rst b/docs/dbdataclasses/database.rst index dbb238f00..d58538b28 100755 --- a/docs/dbdataclasses/database.rst +++ b/docs/dbdataclasses/database.rst @@ -186,6 +186,17 @@ These dataclasses are used in the SDK to represent API requests and responses fo :members: :undoc-members: +.. py:class:: SyncedTableSpecPgSpecificType + + PostgreSQL-specific target types that can override the default Delta-to-PG mapping. + + .. py:attribute:: PG_SPECIFIC_TYPE_VECTOR + :value: "PG_SPECIFIC_TYPE_VECTOR" + +.. autoclass:: SyncedTableSpecTypeOverride + :members: + :undoc-members: + .. py:class:: SyncedTableState The state of a synced table. diff --git a/docs/dbdataclasses/postgres.rst b/docs/dbdataclasses/postgres.rst index 70a21cfa6..29403c35f 100755 --- a/docs/dbdataclasses/postgres.rst +++ b/docs/dbdataclasses/postgres.rst @@ -603,6 +603,13 @@ These dataclasses are used in the SDK to represent API requests and responses fo :members: :undoc-members: +.. py:class:: SyncedTableSyncedTableSpecPgSpecificType + + PostgreSQL-specific target types that can override the default Delta-to-PG mapping. + + .. py:attribute:: PG_SPECIFIC_TYPE_VECTOR + :value: "PG_SPECIFIC_TYPE_VECTOR" + .. py:class:: SyncedTableSyncedTableSpecSyncedTableSchedulingPolicy Scheduling policy of the synced table's underlying pipeline. @@ -616,6 +623,10 @@ These dataclasses are used in the SDK to represent API requests and responses fo .. py:attribute:: TRIGGERED :value: "TRIGGERED" +.. autoclass:: SyncedTableSyncedTableSpecTypeOverride + :members: + :undoc-members: + .. autoclass:: SyncedTableSyncedTableStatus :members: :undoc-members: diff --git a/docs/dbdataclasses/sql.rst b/docs/dbdataclasses/sql.rst index 199fde251..19340a98b 100755 --- a/docs/dbdataclasses/sql.rst +++ b/docs/dbdataclasses/sql.rst @@ -1017,7 +1017,6 @@ These dataclasses are used in the SDK to represent API requests and responses fo EndpointSpotInstancePolicy configures whether the endpoint should use spot instances. The breakdown of how the EndpointSpotInstancePolicy converts to per cloud configurations is: +-------+--------------------------------------+--------------------------------+ | Cloud | COST_OPTIMIZED | RELIABILITY_OPTIMIZED | +-------+--------------------------------------+--------------------------------+ | AWS | On Demand Driver with Spot Executors | On Demand Driver and Executors | | AZURE | On Demand Driver and Executors | On Demand Driver and Executors | +-------+--------------------------------------+--------------------------------+ - While including "spot" in the enum name may limit the the future extensibility of this field because it limits this enum to denoting "spot or not", this is the field that PM recommends after discussion with customers per SC-48783. .. py:attribute:: COST_OPTIMIZED :value: "COST_OPTIMIZED" diff --git a/docs/workspace/catalog/external_locations.rst b/docs/workspace/catalog/external_locations.rst index 0578df8b4..f8dbf9fe2 100755 --- a/docs/workspace/catalog/external_locations.rst +++ b/docs/workspace/catalog/external_locations.rst @@ -30,22 +30,20 @@ w = WorkspaceClient() - storage_credential = w.storage_credentials.create( + credential = w.storage_credentials.create( name=f"sdk-{time.time_ns()}", aws_iam_role=catalog.AwsIamRoleRequest(role_arn=os.environ["TEST_METASTORE_DATA_ACCESS_ARN"]), - comment="created via SDK", ) - external_location = w.external_locations.create( + created = w.external_locations.create( name=f"sdk-{time.time_ns()}", - credential_name=storage_credential.name, - comment="created via SDK", - url="s3://" + os.environ["TEST_BUCKET"] + "/" + f"sdk-{time.time_ns()}", + credential_name=credential.name, + url="s3://%s/%s" % (os.environ["TEST_BUCKET"], f"sdk-{time.time_ns()}"), ) # cleanup - w.storage_credentials.delete(name=storage_credential.name) - w.external_locations.delete(name=external_location.name) + w.storage_credentials.delete(name=credential.name) + w.external_locations.delete(name=created.name) Creates a new external location entry in the metastore. The caller must be a metastore admin or have the **CREATE_EXTERNAL_LOCATION** privilege on both the metastore and the associated storage @@ -150,10 +148,11 @@ .. code-block:: from databricks.sdk import WorkspaceClient + from databricks.sdk.service import catalog w = WorkspaceClient() - all = w.external_locations.list() + all = w.external_locations.list(catalog.ListExternalLocationsRequest()) Gets an array of external locations (__ExternalLocationInfo__ objects) from the metastore. The caller must be a metastore admin, the owner of the external location, or a user that has some privilege on @@ -200,24 +199,24 @@ credential = w.storage_credentials.create( name=f"sdk-{time.time_ns()}", - aws_iam_role=catalog.AwsIamRoleRequest(role_arn=os.environ["TEST_METASTORE_DATA_ACCESS_ARN"]), + aws_iam_role=catalog.AwsIamRole(role_arn=os.environ["TEST_METASTORE_DATA_ACCESS_ARN"]), ) created = w.external_locations.create( name=f"sdk-{time.time_ns()}", credential_name=credential.name, - url="s3://%s/%s" % (os.environ["TEST_BUCKET"], f"sdk-{time.time_ns()}"), + url=f's3://{os.environ["TEST_BUCKET"]}/sdk-{time.time_ns()}', ) _ = w.external_locations.update( name=created.name, credential_name=credential.name, - url="s3://%s/%s" % (os.environ["TEST_BUCKET"], f"sdk-{time.time_ns()}"), + url=f's3://{os.environ["TEST_BUCKET"]}/sdk-{time.time_ns()}', ) # cleanup - w.storage_credentials.delete(name=credential.name) - w.external_locations.delete(name=created.name) + w.storage_credentials.delete(delete=credential.name) + w.external_locations.delete(delete=created.name) Updates an external location in the metastore. The caller must be the owner of the external location, or be a metastore admin. In the second case, the admin can only update the name of the external diff --git a/docs/workspace/catalog/storage_credentials.rst b/docs/workspace/catalog/storage_credentials.rst index c174e87a3..2eacfda5e 100755 --- a/docs/workspace/catalog/storage_credentials.rst +++ b/docs/workspace/catalog/storage_credentials.rst @@ -30,14 +30,13 @@ w = WorkspaceClient() - storage_credential = w.storage_credentials.create( + created = w.storage_credentials.create( name=f"sdk-{time.time_ns()}", - aws_iam_role=catalog.AwsIamRoleRequest(role_arn=os.environ["TEST_METASTORE_DATA_ACCESS_ARN"]), - comment="created via SDK", + aws_iam_role=catalog.AwsIamRole(role_arn=os.environ["TEST_METASTORE_DATA_ACCESS_ARN"]), ) # cleanup - w.storage_credentials.delete(name=storage_credential.name) + w.storage_credentials.delete(delete=created.name) Creates a new storage credential. @@ -99,13 +98,13 @@ created = w.storage_credentials.create( name=f"sdk-{time.time_ns()}", - aws_iam_role=catalog.AwsIamRoleRequest(role_arn=os.environ["TEST_METASTORE_DATA_ACCESS_ARN"]), + aws_iam_role=catalog.AwsIamRole(role_arn=os.environ["TEST_METASTORE_DATA_ACCESS_ARN"]), ) - by_name = w.storage_credentials.get(name=created.name) + by_name = w.storage_credentials.get(get=created.name) # cleanup - w.storage_credentials.delete(name=created.name) + w.storage_credentials.delete(delete=created.name) Gets a storage credential from the metastore. The caller must be a metastore admin, the owner of the storage credential, or have some permission on the storage credential. @@ -174,17 +173,17 @@ created = w.storage_credentials.create( name=f"sdk-{time.time_ns()}", - aws_iam_role=catalog.AwsIamRoleRequest(role_arn=os.environ["TEST_METASTORE_DATA_ACCESS_ARN"]), + aws_iam_role=catalog.AwsIamRole(role_arn=os.environ["TEST_METASTORE_DATA_ACCESS_ARN"]), ) _ = w.storage_credentials.update( name=created.name, comment=f"sdk-{time.time_ns()}", - aws_iam_role=catalog.AwsIamRoleRequest(role_arn=os.environ["TEST_METASTORE_DATA_ACCESS_ARN"]), + aws_iam_role=catalog.AwsIamRole(role_arn=os.environ["TEST_METASTORE_DATA_ACCESS_ARN"]), ) # cleanup - w.storage_credentials.delete(name=created.name) + w.storage_credentials.delete(delete=created.name) Updates a storage credential on the metastore. diff --git a/docs/workspace/compute/clusters.rst b/docs/workspace/compute/clusters.rst index d92941b48..f162b4edf 100644 --- a/docs/workspace/compute/clusters.rst +++ b/docs/workspace/compute/clusters.rst @@ -653,11 +653,10 @@ .. code-block:: from databricks.sdk import WorkspaceClient - from databricks.sdk.service import compute w = WorkspaceClient() - all = w.clusters.list(compute.ListClustersRequest()) + nodes = w.clusters.list_node_types() Return information about all pinned and active clusters, and all clusters terminated within the last 30 days. Clusters terminated prior to this period are not included. diff --git a/docs/workspace/dashboards/lakeview.rst b/docs/workspace/dashboards/lakeview.rst index d02189aaa..21167041b 100755 --- a/docs/workspace/dashboards/lakeview.rst +++ b/docs/workspace/dashboards/lakeview.rst @@ -218,8 +218,7 @@ UUID identifying the dashboard. :param etag: str (optional) The etag for the dashboard. Optionally, it can be provided to verify that the dashboard has not been - modified from its last retrieval. TODO(TSE-3937): update to new non-CMK-encrypted label when - available + modified from its last retrieval. :returns: :class:`RevertDashboardResponse` diff --git a/docs/workspace/iam/permissions.rst b/docs/workspace/iam/permissions.rst index 531208e2f..1f836a4b1 100755 --- a/docs/workspace/iam/permissions.rst +++ b/docs/workspace/iam/permissions.rst @@ -44,7 +44,7 @@ obj = w.workspace.get_status(path=notebook_path) - levels = w.permissions.get_permission_levels(request_object_type="notebooks", request_object_id="%d" % (obj.object_id)) + _ = w.permissions.get(request_object_type="notebooks", request_object_id="%d" % (obj.object_id)) Gets the permissions of an object. Objects can inherit permissions from their parent objects or root object. diff --git a/docs/workspace/ml/model_registry.rst b/docs/workspace/ml/model_registry.rst index 46c3a4565..601ffd87d 100755 --- a/docs/workspace/ml/model_registry.rst +++ b/docs/workspace/ml/model_registry.rst @@ -736,13 +736,14 @@ w = WorkspaceClient() - created = w.model_registry.create_model(name=f"sdk-{time.time_ns()}") + model = w.model_registry.create_model(name=f"sdk-{time.time_ns()}") - model = w.model_registry.get_model(name=created.registered_model.name) + created = w.model_registry.create_model_version(name=model.registered_model.name, source="dbfs:/tmp") - w.model_registry.update_model( - name=model.registered_model_databricks.name, + w.model_registry.update_model_version( description=f"sdk-{time.time_ns()}", + name=created.model_version.name, + version=created.model_version.version, ) Updates a registered model. diff --git a/docs/workspace/sql/warehouses.rst b/docs/workspace/sql/warehouses.rst index f642f0204..51ea6835c 100755 --- a/docs/workspace/sql/warehouses.rst +++ b/docs/workspace/sql/warehouses.rst @@ -51,7 +51,7 @@ max_num_clusters. Supported values: - 2X-Small - X-Small - Small - Medium - Large - X-Large - 2X-Large - 3X-Large - - 4X-Large - 5X-Large + 4X-Large - 5X-Large - Auto :param creator_name: str (optional) warehouse creator name :param enable_photon: bool (optional) @@ -190,7 +190,7 @@ max_num_clusters. Supported values: - 2X-Small - X-Small - Small - Medium - Large - X-Large - 2X-Large - 3X-Large - - 4X-Large - 5X-Large + 4X-Large - 5X-Large - Auto :param creator_name: str (optional) warehouse creator name :param enable_photon: bool (optional) diff --git a/docs/workspace/workspace/workspace.rst b/docs/workspace/workspace/workspace.rst index 23362f2fd..97dbf6cef 100755 --- a/docs/workspace/workspace/workspace.rst +++ b/docs/workspace/workspace/workspace.rst @@ -150,9 +150,9 @@ w = WorkspaceClient() - notebook = f"/Users/{w.current_user.me().user_name}/sdk-{time.time_ns()}" + notebook_path = f"/Users/{w.current_user.me().user_name}/sdk-{time.time_ns()}" - get_status_response = w.workspace.get_status(path=notebook) + obj = w.workspace.get_status(path=notebook_path) Gets the status of an object or a directory. If `path` does not exist, this call returns an error `RESOURCE_DOES_NOT_EXIST`. @@ -181,18 +181,11 @@ notebook_path = f"/Users/{w.current_user.me().user_name}/sdk-{time.time_ns()}" w.workspace.import_( - path=notebook_path, - overwrite=true_, + content=base64.b64encode(("CREATE LIVE TABLE dlt_sample AS SELECT 1").encode()).decode(), format=workspace.ImportFormat.SOURCE, - language=workspace.Language.PYTHON, - content=base64.b64encode( - ( - """import time - time.sleep(10) - dbutils.notebook.exit('hello') - """ - ).encode() - ).decode(), + language=workspace.Language.SQL, + overwrite=true_, + path=notebook_path, ) Imports a workspace object (for example, a notebook or file) or the contents of an entire directory. diff --git a/pyproject.toml b/pyproject.toml index 6f1552e29..d59beedb5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,9 @@ dependencies = [ # Exclude vulnerable protobuf versions: [,4.25.8), [5.26.0rc1, 5.29.5), [6.30.0rc1, 6.31.1) # https://security.snyk.io/vuln/SNYK-PYTHON-PROTOBUF-10364902 "protobuf>=4.25.8,!=5.26.*,!=5.27.*,!=5.28.*,!=5.29.0,!=5.29.1,!=5.29.2,!=5.29.3,!=5.29.4,!=6.30.0,!=6.30.1,!=6.31.0,<7.0", + # Imported directly (databricks/sdk/_base_client.py); declared + # explicitly rather than relied on transitively through requests. + "urllib3>=1.26,<3", ] [project.urls] diff --git a/tagging.py b/tagging.py deleted file mode 100755 index a3897bbee..000000000 --- a/tagging.py +++ /dev/null @@ -1,1057 +0,0 @@ -#!/usr/bin/env python3 -# /// script -# dependencies = ["PyGithub>=2,<3", "pyjwt<2.12.0", "charset-normalizer<3.4.6"] -# /// - -import os -import re -import argparse -from typing import Optional, List, Callable, Dict -from dataclasses import dataclass, replace -import subprocess -import time -import json -from github import Github, Repository, InputGitTreeElement, InputGitAuthor -from datetime import datetime, timezone - -NEXT_CHANGELOG_FILE_NAME = "NEXT_CHANGELOG.md" -CHANGELOG_FILE_NAME = "CHANGELOG.md" -PACKAGE_FILE_NAME = ".package.json" -CODEGEN_FILE_NAME = ".codegen.json" -CREATED_TAGS_FILE_NAME = "created_tags.json" -""" -This script tags the release of the SDKs using a combination of the GitHub API and Git commands. -It reads the local repository to determine necessary changes, updates changelogs, and creates tags. - -### How it Works: -- It does **not** modify the local repository directly. -- Instead of committing and pushing changes locally, it uses the **GitHub API** to create commits and tags. -""" - - -@dataclass(frozen=True) -class Version: - """ - A semver 2.0.0-compliant version (https://semver.org). - - Mirrors the API of the `semver` PyPI package so this implementation can be - swapped for that library if it is ever added to the wheelhouse. Supports - parsing, stringification, and the two bumps we need: minor (for stable - releases) and prerelease (for release trains). - """ - - # Permissive pattern for locating a semver version string inside larger - # text (e.g. a changelog header). Callers use it in f-strings; strict - # validation happens via Version.parse. - PATTERN = r"\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?" - - # Strict anchored regex per https://semver.org. Rejects leading zeros in - # numeric identifiers and invalid pre-release/build identifier charsets. - _PARSE_REGEX = re.compile( - r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)" - r"(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)" - r"(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?" - r"(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$" - ) - - major: int - minor: int - patch: int - prerelease: str = "" - build: str = "" - - @classmethod - def parse(cls, text: str) -> "Version": - """Parse a semver string, raising ValueError on malformed input.""" - match = cls._PARSE_REGEX.match(text) - if not match: - raise ValueError(f"Invalid semver version: {text!r}") - major, minor, patch, prerelease, build = match.groups() - return cls( - major=int(major), - minor=int(minor), - patch=int(patch), - prerelease=prerelease or "", - build=build or "", - ) - - def __str__(self) -> str: - result = f"{self.major}.{self.minor}.{self.patch}" - if self.prerelease: - result += f"-{self.prerelease}" - if self.build: - result += f"+{self.build}" - return result - - def bump_minor(self) -> "Version": - """ - Bump the minor version and reset patch. - - Per semver item 9, a pre-release version has lower precedence than - the same MAJOR.MINOR.PATCH, so bumping to a new minor drops any - pre-release and build metadata. - """ - return Version(major=self.major, minor=self.minor + 1, patch=0) - - def bump_prerelease(self) -> "Version": - """ - Increment the rightmost numeric identifier in the pre-release. - - Matches the npm `prerelease` bump semantics: - 0.0.0-alpha.1 -> 0.0.0-alpha.2 - 0.0.0-alpha -> 0.0.0-alpha.1 - 0.0.0-rc.1.2 -> 0.0.0-rc.1.3 - - Raises ValueError if the version has no pre-release to bump. - Build metadata is dropped since it does not affect precedence. - """ - if not self.prerelease: - raise ValueError(f"Cannot bump prerelease of {self}: no pre-release component") - parts = self.prerelease.split(".") - for i in range(len(parts) - 1, -1, -1): - if parts[i].isdigit(): - parts[i] = str(int(parts[i]) + 1) - return replace(self, prerelease=".".join(parts), build="") - # No numeric identifier exists; append ".1" to start a counter. - return replace(self, prerelease=f"{self.prerelease}.1", build="") - - def next_release_version(self) -> "Version": - """ - Default next version for the changelog after this one is released. - - If on a pre-release track, stay on it by bumping the pre-release - identifier (npm convention). Otherwise, bump the minor version, - the script's historical default for stable releases. Teams can - override the default in the release PR. - """ - if self.prerelease: - return self.bump_prerelease() - return self.bump_minor() - - -def _read_local_head_sha() -> str: - """ - Returns the SHA of the local working tree's HEAD via ``git rev-parse``. - """ - return subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip() - - -class MainAdvancedError(Exception): - """ - Raised when ``origin/main`` has advanced since the workflow's - checkout — i.e., another commit landed during this run. The local - working tree is now stale, so any commit produced from it would - silently revert whatever the concurrent push added. - """ - - -# GitHub does not support signing commits for GitHub Apps directly. -# This class replaces usages for git commands such as "git add", "git commit", and "git push". -@dataclass -class GitHubRepo: - def __init__(self, repo: Repository): - self.repo = repo - self.changed_files: list[InputGitTreeElement] = [] - self.ref = "heads/main" - # Anchor ``self.sha`` to the **local checkout** rather than a - # live API call. ``actions/checkout`` populates the working tree - # at this SHA, and every subsequent file read in this run is - # against that tree; the API HEAD is only relevant when we go - # to push. - self.sha = _read_local_head_sha() - - # Replaces "git add file" - def add_file(self, loc: str, content: str): - local_path = os.path.relpath(loc, os.getcwd()) - print(f"Adding file {local_path}") - blob = self.repo.create_git_blob(content=content, encoding="utf-8") - element = InputGitTreeElement(path=local_path, mode="100644", type="blob", sha=blob.sha) - self.changed_files.append(element) - - # Replaces "git commit && git push" - def commit_and_push(self, message: str): - head_ref = self.repo.get_git_ref(self.ref) - if head_ref.object.sha != self.sha: - raise MainAdvancedError( - f"origin/main advanced from {self.sha} to {head_ref.object.sha} " - f"during this run. Local working tree is stale; aborting before " - f"the commit would silently revert the new content. Re-run the " - f"workflow." - ) - base_tree = self.repo.get_git_tree(sha=head_ref.object.sha) - new_tree = self.repo.create_git_tree(self.changed_files, base_tree) - parent_commit = self.repo.get_git_commit(head_ref.object.sha) - - new_commit = self.repo.create_git_commit(message=message, tree=new_tree, parents=[parent_commit]) - # Update branch reference. - head_ref.edit(new_commit.sha) - self.sha = new_commit.sha - - def reset(self, sha: Optional[str] = None): - self.changed_files = [] - if sha: - self.sha = sha - else: - self.sha = _read_local_head_sha() - - def tag(self, tag_name: str, tag_message: str): - # Create a tag pointing to the new commit - # The email MUST be the GitHub Apps email. - # Otherwise, the tag will not be verified. - tagger = InputGitAuthor( - name="Databricks SDK Release Bot", email="DECO-SDK-Tagging[bot]@users.noreply.github.com" - ) - - tag = self.repo.create_git_tag(tag=tag_name, message=tag_message, object=self.sha, type="commit", tagger=tagger) - # Create a Git ref (the actual reference for the tag in the repo) - self.repo.create_git_ref(ref=f"refs/tags/{tag_name}", sha=tag.sha) - - -gh: Optional[GitHubRepo] = None - - -@dataclass -class Package: - """ - Represents a package in the repository. - :name: The package name. - :path: The path to the package relative to the repository root. - """ - - name: str - path: str - - -@dataclass -class TagInfo: - """ - Represents all changes on a release. - :package: package info. - :version: release version for the package. Format: v.. - :content: changes for the release, as they appear in the changelog. - When written to CHANGELOG.md, the current date (YYYY-MM-DD) is automatically added. - - Example (from NEXT_CHANGELOG.md): - - ## Release v0.56.0 - - ### New Features and Improvements - * Feature - * Some improvement - - ### Bug Fixes - * Bug fix - - ### Documentation - * Doc Changes - - ### Internal Changes - * More Changes - - ### API Changes - * Add new Service - - Note: When written to CHANGELOG.md, the header becomes: ## Release v0.56.0 (YYYY-MM-DD) - - """ - - package: Package - version: str - content: str - - def tag_name(self) -> str: - return f"{self.package.name}/v{self.version}" if self.package.name else f"v{self.version}" - - -def get_package_name(package_path: str) -> str: - """ - Returns the package name from the package path. - The name is found inside the .package.json file: - { - "package": "package_name" - } - """ - filepath = os.path.join(os.getcwd(), package_path, PACKAGE_FILE_NAME) - with open(filepath, "r") as file: - content = json.load(file) - if "package" in content: - return content["package"] - # Legacy SDKs have no packages. - return "" - - -def stage_version_updates(tag_infos: List[TagInfo], packages: List[Package]) -> None: - """ - Stages all version-related edits for the release in a single pass over - every package the workspace already opts in via ``.package.json``. - """ - - # Load patterns from '.codegen.json' at the top level of the repository. - package_file_path = os.path.join(os.getcwd(), CODEGEN_FILE_NAME) - with open(package_file_path, "r") as file: - codegen = json.load(file) - - version_patterns = codegen.get("version", {}) - dep_patterns = codegen.get("dependency_pattern", {}) - name_template = codegen.get("dependency_name_template", "") - - if not version_patterns and not dep_patterns: - print("Neither `version` nor `dependency_pattern` found in .codegen.json. Nothing to update.") - return - - bumped_by_dir: Dict[str, TagInfo] = {info.package.path: info for info in tag_infos} - new_dep_versions = compute_dependency_rewrites(tag_infos, name_template) - - files = sorted(set(version_patterns.keys()) | set(dep_patterns.keys())) - - for pkg in packages: - for filename in files: - loc = os.path.join(os.getcwd(), pkg.path, filename) - - with open(loc, "r") as file: - content = file.read() - original = content - - # Own version (only when this package is being released and the - # file has a version pattern declared). - info = bumped_by_dir.get(pkg.path) - if info is not None and filename in version_patterns: - pattern = version_patterns[filename] - previous_version = pattern.replace("$VERSION", Version.PATTERN) - new_version = pattern.replace("$VERSION", info.version) - content = re.sub(previous_version, new_version, content) - - # Sibling dependency rewrites (only when the file has a - # dependency pattern and there is at least one bumped sibling). - if filename in dep_patterns and new_dep_versions: - content = rewrite_dependencies(content, dep_patterns[filename], new_dep_versions) - - if content != original: - gh.add_file(loc, content) - - -def compute_dependency_rewrites( - tag_infos: List[TagInfo], - name_template: str, -) -> Dict[str, str]: - """ - Returns a map of dependency-name to the new semver string for each - bumped package. - """ - if not name_template: - return {} - rewrites: Dict[str, str] = {} - for info in tag_infos: - # Skip legacy releases that don't have a per-package name; their - # tag_info has an empty package.name and they can't be referenced - # as a sibling dep anyway. - if not info.package.name: - continue - dep_name = name_template.replace("$PACKAGE", info.package.name) - rewrites[dep_name] = info.version - return rewrites - - -def rewrite_dependencies(content: str, pattern: str, new_versions: Dict[str, str]) -> str: - """ - Apply ``pattern`` (with ``$DEPENDENCY`` and ``$VERSION`` placeholders) to - rewrite every entry in ``content`` whose dependency name appears in - ``new_versions``. - """ - # Sentinel strings used to protect the placeholders through re.escape: - # we substitute them in, escape the whole template, then swap them out - # for the dep-name literal and Version.PATTERN. Control characters so - # they can't collide with anything in real .codegen.json patterns. - dep_sentinel = "\x01DEPENDENCY\x01" - ver_sentinel = "\x01VERSION\x01" - - for dep_name, new_value in new_versions.items(): - regex = pattern.replace("$DEPENDENCY", dep_sentinel).replace("$VERSION", ver_sentinel) - regex = re.escape(regex) - regex = regex.replace(re.escape(dep_sentinel), re.escape(dep_name)) - regex = regex.replace(re.escape(ver_sentinel), Version.PATTERN) - - # Build the literal replacement text by substituting the same - # placeholders directly. A lambda is used instead of a string to - # avoid re.sub interpreting \1, \g<...>, etc. inside the value. - replacement_text = pattern.replace("$DEPENDENCY", dep_name).replace("$VERSION", new_value) - content = re.sub(regex, lambda _m, text=replacement_text: text, content) - return content - - -def clean_next_changelog(package_path: str) -> None: - """ - Cleans the "NEXT_CHANGELOG.md" file. It performs 2 operations: - * Increase the version to the next minor version. - * Remove release notes. Sections names are kept to - keep consistency in the section names between releases. - """ - - file_path = os.path.join(os.getcwd(), package_path, NEXT_CHANGELOG_FILE_NAME) - with open(file_path, "r") as file: - content = file.read() - - # Remove content between ### sections. - cleaned_content = re.sub(r"(### [^\n]+\n)(?:.*?\n?)*?(?=###|$)", r"\1", content) - # Ensure there is exactly one empty line before each section. - cleaned_content = re.sub(r"(\n*)(###[^\n]+)", r"\n\n\2", cleaned_content) - # Find the version number and compute the default next release version. - # Teams can adjust the version in the PR if the default is not desired. - # For stable versions, bump minor (historical default since minor releases - # are more common than patch or major). For pre-release versions, stay on - # the same track by bumping the pre-release identifier (npm convention). - version_match = re.search(rf"Release v({Version.PATTERN})", cleaned_content) - if not version_match: - raise Exception("Version not found in the changelog") - current = Version.parse(version_match.group(1)) - new_header = f"Release v{current.next_release_version()}" - cleaned_content = cleaned_content.replace(version_match.group(0), new_header) - - # Update file with cleaned content - gh.add_file(file_path, cleaned_content) - - -def get_previous_tag_info(package: Package) -> Optional[TagInfo]: - """ - Extracts the previous tag info from the "CHANGELOG.md" file. - Used for failure recovery purposes. - """ - changelog_path = os.path.join(os.getcwd(), package.path, CHANGELOG_FILE_NAME) - - with open(changelog_path, "r") as f: - changelog = f.read() - - # Extract the latest release section using regex. - match = re.search( - rf"## (\[Release\] )?Release v{Version.PATTERN}.*?(?=\n## (\[Release\] )?Release v|\Z)", - changelog, - re.S, - ) - - # E.g., for new packages. - if not match: - return None - - latest_release = match.group(0) - version_match = re.search(rf"## (\[Release\] )?Release v({Version.PATTERN})", latest_release) - - if not version_match: - raise Exception("Version not found in the changelog") - - # Validate the extracted string is spec-compliant; fail loudly otherwise. - version = str(Version.parse(version_match.group(2))) - return TagInfo(package=package, version=version, content=latest_release) - - -def _load_codegen_config() -> Dict: - """ - Loads ``.codegen.json`` from the repo root. Returns an empty dict when - the file is missing. - """ - package_file_path = os.path.join(os.getcwd(), CODEGEN_FILE_NAME) - if not os.path.exists(package_file_path): - return {} - with open(package_file_path, "r") as file: - return json.load(file) - - -def get_next_tag_info(package: Package) -> Optional[TagInfo]: - """ - Extracts the changes from the "NEXT_CHANGELOG.md" file. - The result is already processed. - """ - next_changelog_path = os.path.join(os.getcwd(), package.path, NEXT_CHANGELOG_FILE_NAME) - # Read NEXT_CHANGELOG.md - with open(next_changelog_path, "r") as f: - next_changelog = f.read() - - # Remove "# NEXT CHANGELOG" line - next_changelog = re.sub(r"^# NEXT CHANGELOG(\n+)", "", next_changelog, flags=re.MULTILINE) - - # Remove empty sections - next_changelog = re.sub(r"###[^\n]+\n+(?=##|\Z)", "", next_changelog) - # Ensure there is exactly one empty line before each section - next_changelog = re.sub(r"(\n*)(###[^\n]+)", r"\n\n\2", next_changelog) - - # By default, packages whose NEXT_CHANGELOG.md has no populated - # sections are skipped — there's nothing meaningful to release. - # Repos like sdk-js which are still in development can opt in - # by setting ``allow_empty_changelog: true`` in .codegen.json. - if not re.search(r"###", next_changelog) and not _load_codegen_config().get("allow_empty_changelog", False): - print("All sections are empty. No changes will be made to the changelog.") - return None - - version_match = re.search(rf"## Release v({Version.PATTERN})", next_changelog) - - if not version_match: - raise Exception("Version not found in the changelog") - - # Validate the extracted string is spec-compliant; fail loudly otherwise. - version = str(Version.parse(version_match.group(1))) - return TagInfo(package=package, version=version, content=next_changelog) - - -def write_changelog(tag_info: TagInfo) -> None: - """ - Updates the changelog with a new tag info. - """ - changelog_path = os.path.join(os.getcwd(), tag_info.package.path, CHANGELOG_FILE_NAME) - with open(changelog_path, "r") as f: - changelog = f.read() - - # Add current date to the release header. - current_date = datetime.now(tz=timezone.utc).strftime("%Y-%m-%d") - content_with_date = re.sub( - rf"## Release v({Version.PATTERN})", - rf"## Release v\1 ({current_date})", - tag_info.content.strip(), - ) - - updated_changelog = re.sub(r"(# Version changelog\n\n)", f"\\1{content_with_date}\n\n\n", changelog) - gh.add_file(changelog_path, updated_changelog) - - -def process_package(package: Package) -> TagInfo: - """ - Processes a package's changelog scaffolding for the release. - """ - print(f"Processing package {package.name}") - tag_info = get_next_tag_info(package) - - # If there are no updates, skip. - if tag_info is None: - return - - write_changelog(tag_info) - clean_next_changelog(package.path) - return tag_info - - -def find_packages() -> List[Package]: - """ - Returns all directories which contains a ".package.json" file. - """ - paths = _find_directories_with_file(PACKAGE_FILE_NAME) - return [Package(name=get_package_name(path), path=path) for path in paths] - - -def _find_directories_with_file(target_file: str) -> List[str]: - root_path = os.getcwd() - matching_directories = [] - - for dirpath, _, filenames in os.walk(root_path): - if target_file in filenames: - path = os.path.relpath(dirpath, root_path) - # If the path is the root directory (e.g., SDK V0), set it to an empty string. - if path == ".": - path = "" - matching_directories.append(path) - - return matching_directories - - -def is_tag_applied(tag: TagInfo) -> bool: - """ - Returns whether a tag is already applied in the repository. - - :param tag: The tag to check. - :return: True if the tag is applied, False otherwise. - :raises Exception: If the git command fails. - """ - try: - # Check if the specific tag exists - result = subprocess.check_output(["git", "tag", "--list", tag.tag_name()], stderr=subprocess.PIPE, text=True) - return result.strip() == tag.tag_name() - except subprocess.CalledProcessError as e: - # Raise a exception for git command errors - raise Exception(f"Git command failed: {e.stderr.strip() or e}") from e - - -def find_last_release_tag(package: Package) -> Optional[str]: - """ - Returns the most recent ``/v*`` tag in the repository, or - ``None`` if no such tag exists. Tags are sorted by semver ordering - (``--sort=-v:refname``) so pre-releases sort below their stable - counterparts. - - :raises Exception: If the git command fails. - """ - pattern = f"{package.name}/v*" if package.name else "v*" - try: - output = subprocess.check_output( - ["git", "tag", "--list", pattern, "--sort=-v:refname"], - stderr=subprocess.PIPE, - text=True, - ).strip() - except subprocess.CalledProcessError as e: - raise Exception(f"Git command failed: {e.stderr.strip() or e}") from e - if not output: - return None - return output.split("\n")[0].strip() - - -def has_commits_since_tag(tag: str, path: str) -> bool: - """ - Returns True iff at least one commit reachable from HEAD but not from - ``tag`` touches ``path``. Used to detect that a sibling dependency has - unreleased changes that would ship stale if we tagged a dependent - without re-tagging the dependency. - - :raises Exception: If the git command fails. - """ - args = ["git", "log", "--oneline", f"{tag}..HEAD", "--", path or "."] - try: - output = subprocess.check_output(args, stderr=subprocess.PIPE, text=True).strip() - except subprocess.CalledProcessError as e: - raise Exception(f"Git command failed: {e.stderr.strip() or e}") from e - return bool(output) - - -def check_dependency_freshness(tag_infos: List[TagInfo], all_packages: List[Package]) -> None: - """ - Hard-fails when a package being released depends on a sibling package - that has unreleased commits since its last tag. - - Why: dependency rewrites (``stage_version_updates``) only fire for - siblings that are *also* being released. Without this check, releasing - package_a alone — when package_b has commits since its last tag — - publishes ``package_a@new`` pinning the *old* package_b artifact, which - won't have the changes package_a's source depends on. The check is - commit-based (not changelog-based) so a missing ``NEXT_CHANGELOG.md`` - entry on package_b is still caught. - - No-op when ``.codegen.json`` declares no dependency pattern (legacy - SDKs without per-package wiring). - """ - if not tag_infos: - return - - package_file_path = os.path.join(os.getcwd(), CODEGEN_FILE_NAME) - with open(package_file_path, "r") as file: - codegen = json.load(file) - - name_template = codegen.get("dependency_name_template", "") - dep_patterns = codegen.get("dependency_pattern", {}) - if not name_template or not dep_patterns: - return - - releasing_paths = {info.package.path for info in tag_infos} - by_dep_name: Dict[str, Package] = {} - for pkg in all_packages: - if not pkg.name: - continue - by_dep_name[name_template.replace("$PACKAGE", pkg.name)] = pkg - - issues: List[str] = [] - for info in tag_infos: - for filename, pattern in dep_patterns.items(): - loc = os.path.join(os.getcwd(), info.package.path, filename) - if not os.path.exists(loc): - continue - with open(loc, "r") as f: - content = f.read() - - for dep_name, dep_pkg in by_dep_name.items(): - if dep_pkg.path == info.package.path: - continue - if dep_pkg.path in releasing_paths: - continue - - # Same regex construction used by ``rewrite_dependencies``, - # so "is this dep referenced?" matches "would the rewrite - # touch it?". Keeps the two in lockstep. - regex = ( - re.escape(pattern) - .replace(re.escape("$DEPENDENCY"), re.escape(dep_name)) - .replace(re.escape("$VERSION"), Version.PATTERN) - ) - if not re.search(regex, content): - continue - - last_tag = find_last_release_tag(dep_pkg) - if last_tag is None: - # No prior tag means the dep was never released; we - # can't reason about staleness. Surface it anyway so - # the human resolves it explicitly. - issues.append( - f"{info.package.name} depends on {dep_pkg.name}, " - f"which has never been released. Release " - f"{dep_pkg.name} first or include it in this run." - ) - continue - if has_commits_since_tag(last_tag, dep_pkg.path): - issues.append( - f"{info.package.name} depends on {dep_pkg.name}, " - f"which has commits since {last_tag} but is not " - f"being released. Either release {dep_pkg.name} " - f"as well, or hold this release until its changes " - f"are reverted." - ) - - if issues: - raise Exception("Dependency freshness check failed:\n - " + "\n - ".join(issues)) - - -def find_last_tags() -> List[TagInfo]: - """ - Finds the last tags for each package. - - Returns a list of TagInfo objects for each package with a non-None changelog. - """ - packages = find_packages() - - return [info for info in (get_previous_tag_info(package) for package in packages) if info is not None] - - -def find_pending_tags() -> List[TagInfo]: - """ - Finds all tags that are pending to be applied. - """ - tag_infos = find_last_tags() - return [tag for tag in tag_infos if not is_tag_applied(tag)] - - -def generate_commit_message(tag_infos: List[TagInfo]) -> str: - """ - Generates a commit message for the release. - """ - if not tag_infos: - raise Exception("No tag infos provided to generate commit message") - - info = tag_infos[0] - # Legacy mode for SDKs without per service packaging - if not info.package.name: - if len(tag_infos) > 1: - raise Exception("Multiple packages found in legacy mode") - return f"[Release] Release v{info.version}\n\n{info.content}" - - # Sort tag_infos by package name for consistency. - tag_infos.sort(key=lambda info: info.package.name) - titles = ", ".join(f"{info.package.name}/v{info.version}" for info in tag_infos) - body = "\n\n".join(f"## {info.package.name}/v{info.version}\n\n{info.content}" for info in tag_infos) - return f"[Release] {titles}\n\n{body}" - - -def push_changes(tag_infos: List[TagInfo]) -> None: - """Pushes changes to the remote repository after handling possible merge conflicts.""" - - commit_message = generate_commit_message(tag_infos) - - # Create the release metadata file - file_name = os.path.join(os.getcwd(), ".release_metadata.json") - metadata = {"timestamp": datetime.now(tz=timezone.utc).strftime("%Y-%m-%d %H:%M:%S%z")} - content = json.dumps(metadata, indent=4) - gh.add_file(file_name, content) - - gh.commit_and_push(commit_message) - - -def reset_repository(hash: Optional[str] = None) -> None: - """ - Reset git to the specified commit. Defaults to HEAD. - - :param hash: The commit hash to reset to. If None, it resets to HEAD. - """ - # Fetch the latest changes from the remote repository. - subprocess.run(["git", "fetch"]) - - # Determine the commit hash (default to origin/main if none is provided). - commit_hash = hash or "origin/main" - - # ``git reset --hard`` must land before ``gh.reset(None)``, since - # ``gh.reset(None)`` reads ``git rev-parse HEAD`` to anchor - # ``self.sha`` to the local working tree. - subprocess.run(["git", "reset", "--hard", commit_hash], check=True) - gh.reset(hash) - - -def retry_function( - func: Callable[[], List[TagInfo]], cleanup: Callable[[], None], max_attempts: int = 5, delay: int = 5 -) -> List[TagInfo]: - """ - Calls a function call up to `max_attempts` times if an exception occurs. - - :param func: The function to call. - :param cleanup: Cleanup function in between retries - :param max_attempts: The maximum number of retries. - :param delay: The delay between retries in seconds. - :return: The return value of the function, or None if all retries fail. - """ - attempts = 0 - while attempts <= max_attempts: - try: - return func() # Call the function - except MainAdvancedError: - # Permanent failure: another commit landed on main during - # this run, so the local tree is stale. Retrying with the - # same stale tree would just hit the same mismatch — only - # a fresh workflow run against the new main can recover. - raise - except Exception as e: - attempts += 1 - print(f"Attempt {attempts} failed: {e}") - if attempts < max_attempts: - time.sleep(delay) # Wait before retrying - cleanup() - else: - print("All retry attempts failed.") - raise e # Re-raise the exception after max retries - - -def update_changelogs(selected_packages: List[Package], all_packages: List[Package]) -> List[TagInfo]: - """ - Updates changelogs and pushes the commits. - - ``selected_packages`` are the packages whose ``NEXT_CHANGELOG.md`` is - consulted to decide what gets released this run. ``all_packages`` is - the full repo inventory used for cross-package dep rewrites. - - The freshness check is deliberately *not* called here. ``process`` - runs it before entering the retry loop so a freshness violation - fails fast — the check is deterministic against the same git state, - so wrapping it in retry would just delay the same failure five - times. - """ - tag_infos = [info for info in (process_package(package) for package in selected_packages) if info is not None] - # If any package was changed, stage version updates and push. - if tag_infos: - stage_version_updates(tag_infos, all_packages) - push_changes(tag_infos) - return tag_infos - - -def preview_tag_infos(packages: List[Package]) -> List[TagInfo]: - """ - Read-only sibling of ``process_package``: returns the TagInfos that - would be released for ``packages`` without writing any changelog - edits. ``process`` calls this before the retry loop so the freshness - check has a snapshot to validate against. ``process_package`` will - re-derive the same TagInfos when ``update_changelogs`` runs; the - duplication is just a couple of NEXT_CHANGELOG.md reads. - """ - return [info for info in (get_next_tag_info(package) for package in packages) if info is not None] - - -def order_tag_infos_by_dependency(tag_infos: List[TagInfo]) -> List[TagInfo]: - """ - Returns ``tag_infos`` in topological order: every package appears - after every sibling it depends on. - """ - if not tag_infos: - return list(tag_infos) - - if any(not info.package.name for info in tag_infos) and len(tag_infos) > 1: - raise Exception("Multiple packages found in legacy mode") - - package_file_path = os.path.join(os.getcwd(), CODEGEN_FILE_NAME) - with open(package_file_path, "r") as file: - codegen = json.load(file) - - name_template = codegen.get("dependency_name_template", "") - dep_patterns = codegen.get("dependency_pattern", {}) - if not name_template or not dep_patterns: - return list(tag_infos) - - by_dep_name: Dict[str, TagInfo] = { - name_template.replace("$PACKAGE", info.package.name): info for info in tag_infos if info.package.name - } - - # Adjacency: path -> set of paths it depends on (within tag_infos). - deps: Dict[str, set] = {info.package.path: set() for info in tag_infos} - for info in tag_infos: - for filename, pattern in dep_patterns.items(): - loc = os.path.join(os.getcwd(), info.package.path, filename) - if not os.path.exists(loc): - continue - with open(loc, "r") as f: - content = f.read() - for dep_name, dep_info in by_dep_name.items(): - if dep_info.package.path == info.package.path: - continue - regex = ( - re.escape(pattern) - .replace(re.escape("$DEPENDENCY"), re.escape(dep_name)) - .replace(re.escape("$VERSION"), Version.PATTERN) - ) - if re.search(regex, content): - deps[info.package.path].add(dep_info.package.path) - - # Stable topological sort: at each step, emit every node whose deps - # are already emitted, alphabetically by package name. Ties broken - # alphabetically so the manifest is reproducible across runs. - emitted: set = set() - ordered: List[TagInfo] = [] - while len(ordered) < len(tag_infos): - ready = sorted( - ( - info - for info in tag_infos - if info.package.path not in emitted and deps[info.package.path].issubset(emitted) - ), - key=lambda info: info.package.name, - ) - if not ready: - remaining = [info.package.name for info in tag_infos if info.package.path not in emitted] - raise Exception(f"Cyclic dependency detected among packages: {remaining}") - for info in ready: - ordered.append(info) - emitted.add(info.package.path) - return ordered - - -def push_tags(tag_infos: List[TagInfo]) -> None: - """ - Creates and pushes tags to the repository. - - Tags are emitted in topological order — dependencies before - dependents — so downstream publishing pipelines reading - ``created_tags.json`` can walk it sequentially without re-deriving - the dependency graph. See ``order_tag_infos_by_dependency``. - - As a side effect, writes the names of successfully created tags to - ``./created_tags.json`` so that workflows triggering this script can - discover what was produced (the GitHub Actions workflow uploads this - file as the ``created-tags`` artifact). - - Schema: - {"tags": ["service-a/v1.2.3", "service-b/v0.4.0"]} - - The manifest is written even if tag creation fails partway through: - tags that succeeded before the failure are flushed before the - exception is re-raised, so recovery-mode runs still surface their - output. - """ - tag_infos = order_tag_infos_by_dependency(tag_infos) - created: List[str] = [] - try: - for tag_info in tag_infos: - gh.tag(tag_info.tag_name(), tag_info.content) - created.append(tag_info.tag_name()) - finally: - manifest_path = os.path.join(os.getcwd(), CREATED_TAGS_FILE_NAME) - with open(manifest_path, "w") as f: - json.dump({"tags": created}, f) - - -def run_command(command: List[str]) -> str: - """ - Runs a command and returns the output - """ - output = subprocess.check_output(command) - print(f'Running command: {" ".join(command)}') - return output.decode() - - -def pull_last_release_commit() -> None: - """ - Reset the repository to the last release. - Uses commit for last change to .release_metadata.json, since it's only updated on releases. - """ - commit_hash = subprocess.check_output( - ["git", "log", "-n", "1", "--format=%H", "--", ".release_metadata.json"], text=True - ).strip() - - # If no commit is found, raise an exception - if not commit_hash: - raise ValueError("No commit found for .release_metadata.json") - - # Reset the repository to the commit - reset_repository(commit_hash) - - -def get_packages_from_args() -> List[str]: - """ - Retrieves the list of packages to tag. - - python3 ./tagging.py --package # single package - python3 ./tagging.py --package , # multiple packages - - Returns an empty list when --package is omitted, which means all packages - with pending releases will be tagged. - """ - parser = argparse.ArgumentParser(description="Update changelogs and tag the release.") - parser.add_argument( - "--package", - "-p", - type=str, - default="", - help="Comma-separated list of packages to tag. Leave empty to tag all packages with pending releases.", - ) - args = parser.parse_args() - return [name.strip() for name in args.package.split(",") if name.strip()] - - -def init_github(): - token = os.environ["GITHUB_TOKEN"] - repo_name = os.environ["GITHUB_REPOSITORY"] - g = Github(token) - repo = g.get_repo(repo_name) - global gh - gh = GitHubRepo(repo) - - -def process(): - """ - Main entry point for tagging process. - - Tagging process consist of multiple steps: - * For each package, update the corresponding CHANGELOG.md file based on the contents of NEXT_CHANGELOG.md file - * If any package has been updated, commit and push the changes. - * Apply and push the new tags matching the version. - - If a specific pagkage is provided as a parameter, only that package will be tagged. - - If any tag are pending from an early process, it will skip updating the CHANGELOG.md files and only apply the tags. - """ - - package_names = get_packages_from_args() - pending_tags = find_pending_tags() - - # pending_tags is non-empty only when the tagging process previously failed or interrupted. - # We must complete the interrupted tagging process before starting a new one to avoid inconsistent states and missing changelog entries. - # Therefore, we don't support specifying packages until the previously started process has been successfully completed. - if pending_tags and package_names: - pending_packages = [tag.package.name for tag in pending_tags] - raise Exception(f"Cannot release packages {package_names}. Pending release for {pending_packages}") - - if pending_tags: - print("Found pending tags from previous executions, entering recovery mode.") - pull_last_release_commit() - push_tags(pending_tags) - return - - all_packages = find_packages() - # If packages are specified as an argument, only release those — but - # dep rewrites and the freshness check still operate over the full - # set. - selected_packages = all_packages - if package_names: - selected_packages = [package for package in all_packages if package.name in package_names] - - # Run the freshness check against a read-only preview before the - # retry loop, since the check is deterministic. A freshness - # violation fails the run immediately, with no commits, no tags, no - # retry storm. - check_dependency_freshness(preview_tag_infos(selected_packages), all_packages) - - pending_tags = retry_function( - func=lambda: update_changelogs(selected_packages, all_packages), - cleanup=reset_repository, - ) - push_tags(pending_tags) - - -def validate_git_root(): - """ - Validate that the script is run from the root of the repository. - """ - repo_root = subprocess.check_output(["git", "rev-parse", "--show-toplevel"]).strip().decode("utf-8") - current_dir = subprocess.check_output(["pwd"]).strip().decode("utf-8") - if repo_root != current_dir: - raise Exception("Please run this script from the root of the repository.") - - -if __name__ == "__main__": - validate_git_root() - init_github() - process() diff --git a/uv.lock b/uv.lock index da06c746a..642917e79 100755 --- a/uv.lock +++ b/uv.lock @@ -11,7 +11,7 @@ resolution-markers = [ [[package]] name = "annotated-types" version = "0.7.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, @@ -20,7 +20,7 @@ wheels = [ [[package]] name = "anyio" version = "4.12.1" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "idna" }, @@ -34,7 +34,7 @@ wheels = [ [[package]] name = "asttokens" version = "3.0.1" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308, upload-time = "2025-11-15T16:43:48.578Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" }, @@ -43,7 +43,7 @@ wheels = [ [[package]] name = "build" version = "1.4.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "colorama", marker = "os_name == 'nt'" }, { name = "importlib-metadata", marker = "python_full_version < '3.10.2'" }, @@ -59,7 +59,7 @@ wheels = [ [[package]] name = "certifi" version = "2026.2.25" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, @@ -68,7 +68,7 @@ wheels = [ [[package]] name = "cffi" version = "2.0.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "pycparser", marker = "implementation_name != 'PyPy'" }, ] @@ -150,7 +150,7 @@ wheels = [ [[package]] name = "charset-normalizer" version = "3.4.5" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/1d/35/02daf95b9cd686320bb622eb148792655c9412dbb9b67abb5694e5910a24/charset_normalizer-3.4.5.tar.gz", hash = "sha256:95adae7b6c42a6c5b5b559b1a99149f090a57128155daeea91732c8d970d8644", size = 134804, upload-time = "2026-03-06T06:03:19.46Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/a7/21/a2b1505639008ba2e6ef03733a81fc6cfd6a07ea6139a2b76421230b8dad/charset_normalizer-3.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4167a621a9a1a986c73777dbc15d4b5eac8ac5c10393374109a343d4013ec765", size = 283319, upload-time = "2026-03-06T06:00:26.433Z" }, @@ -239,7 +239,7 @@ wheels = [ [[package]] name = "check-manifest" version = "0.51" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "build" }, { name = "setuptools" }, @@ -253,7 +253,7 @@ wheels = [ [[package]] name = "colorama" version = "0.4.6" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, @@ -262,7 +262,7 @@ wheels = [ [[package]] name = "comm" version = "0.2.3" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319, upload-time = "2025-07-25T14:02:04.452Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294, upload-time = "2025-07-25T14:02:02.896Z" }, @@ -271,7 +271,7 @@ wheels = [ [[package]] name = "coverage" version = "7.13.4" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/24/56/95b7e30fa389756cb56630faa728da46a27b8c6eb46f9d557c68fff12b65/coverage-7.13.4.tar.gz", hash = "sha256:e5c8f6ed1e61a8b2dcdf31eb0b9bbf0130750ca79c1c49eb898e2ad86f5ccc91", size = 827239, upload-time = "2026-02-09T12:59:03.86Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/44/d4/7827d9ffa34d5d4d752eec907022aa417120936282fc488306f5da08c292/coverage-7.13.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fc31c787a84f8cd6027eba44010517020e0d18487064cd3d8968941856d1415", size = 219152, upload-time = "2026-02-09T12:56:11.974Z" }, @@ -389,7 +389,7 @@ toml = [ [[package]] name = "cryptography" version = "46.0.5" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, @@ -449,7 +449,7 @@ wheels = [ [[package]] name = "databricks-connect" version = "16.1.7" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } resolution-markers = [ "python_full_version == '3.11.*'", "python_full_version < '3.11'", @@ -459,10 +459,10 @@ dependencies = [ { name = "googleapis-common-protos", marker = "python_full_version < '3.12'" }, { name = "grpcio", marker = "python_full_version < '3.12'" }, { name = "grpcio-status", marker = "python_full_version < '3.12'" }, - { name = "numpy", version = "1.26.4", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "1.26.4", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" }, marker = "python_full_version < '3.12'" }, { name = "packaging", marker = "python_full_version < '3.12'" }, { name = "pandas", marker = "python_full_version < '3.12'" }, - { name = "py4j", version = "0.10.9.7", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" }, marker = "python_full_version < '3.12'" }, + { name = "py4j", version = "0.10.9.7", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" }, marker = "python_full_version < '3.12'" }, { name = "pyarrow", marker = "python_full_version < '3.12'" }, { name = "setuptools", marker = "python_full_version < '3.12'" }, { name = "six", marker = "python_full_version < '3.12'" }, @@ -474,7 +474,7 @@ wheels = [ [[package]] name = "databricks-connect" version = "18.1.1" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } resolution-markers = [ "python_full_version >= '3.12'", ] @@ -483,10 +483,10 @@ dependencies = [ { name = "googleapis-common-protos", marker = "python_full_version >= '3.12'" }, { name = "grpcio", marker = "python_full_version >= '3.12'" }, { name = "grpcio-status", marker = "python_full_version >= '3.12'" }, - { name = "numpy", version = "2.4.3", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" }, marker = "python_full_version >= '3.12'" }, + { name = "numpy", version = "2.4.3", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" }, marker = "python_full_version >= '3.12'" }, { name = "packaging", marker = "python_full_version >= '3.12'" }, { name = "pandas", marker = "python_full_version >= '3.12'" }, - { name = "py4j", version = "0.10.9.9", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" }, marker = "python_full_version >= '3.12'" }, + { name = "py4j", version = "0.10.9.9", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" }, marker = "python_full_version >= '3.12'" }, { name = "pyarrow", marker = "python_full_version >= '3.12'" }, { name = "setuptools", marker = "python_full_version >= '3.12'" }, { name = "six", marker = "python_full_version >= '3.12'" }, @@ -503,18 +503,19 @@ dependencies = [ { name = "google-auth" }, { name = "protobuf" }, { name = "requests" }, + { name = "urllib3" }, ] [package.optional-dependencies] dev = [ { name = "build" }, { name = "check-manifest" }, - { name = "databricks-connect", version = "16.1.7", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" }, marker = "python_full_version < '3.12'" }, - { name = "databricks-connect", version = "18.1.1", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" }, marker = "python_full_version >= '3.12'" }, + { name = "databricks-connect", version = "16.1.7", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" }, marker = "python_full_version < '3.12'" }, + { name = "databricks-connect", version = "18.1.1", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" }, marker = "python_full_version >= '3.12'" }, { name = "httpx" }, - { name = "ipython", version = "8.38.0", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.10.0", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" }, marker = "python_full_version == '3.11.*'" }, - { name = "ipython", version = "9.11.0", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" }, marker = "python_full_version >= '3.12'" }, + { name = "ipython", version = "8.38.0", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" }, marker = "python_full_version < '3.11'" }, + { name = "ipython", version = "9.10.0", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "ipython", version = "9.11.0", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" }, marker = "python_full_version >= '3.12'" }, { name = "ipywidgets" }, { name = "langchain-openai" }, { name = "openai" }, @@ -530,9 +531,9 @@ dev = [ { name = "wheel" }, ] notebook = [ - { name = "ipython", version = "8.38.0", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.10.0", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" }, marker = "python_full_version == '3.11.*'" }, - { name = "ipython", version = "9.11.0", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" }, marker = "python_full_version >= '3.12'" }, + { name = "ipython", version = "8.38.0", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" }, marker = "python_full_version < '3.11'" }, + { name = "ipython", version = "9.10.0", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "ipython", version = "9.11.0", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" }, marker = "python_full_version >= '3.12'" }, { name = "ipywidgets" }, ] openai = [ @@ -568,6 +569,7 @@ requires-dist = [ { name = "requests", specifier = ">=2.28.1,<3" }, { name = "requests-mock", marker = "extra == 'dev'" }, { name = "ruff", marker = "extra == 'dev'", specifier = "==0.5.6" }, + { name = "urllib3", specifier = ">=1.26,<3" }, { name = "wheel", marker = "extra == 'dev'" }, ] provides-extras = ["dev", "notebook", "openai"] @@ -575,7 +577,7 @@ provides-extras = ["dev", "notebook", "openai"] [[package]] name = "decorator" version = "5.2.1" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, @@ -584,7 +586,7 @@ wheels = [ [[package]] name = "distro" version = "1.9.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, @@ -593,7 +595,7 @@ wheels = [ [[package]] name = "exceptiongroup" version = "1.3.1" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] @@ -605,7 +607,7 @@ wheels = [ [[package]] name = "execnet" version = "2.1.2" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/bf/89/780e11f9588d9e7128a3f87788354c7946a9cbb1401ad38a48c4db9a4f07/execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd", size = 166622, upload-time = "2025-11-12T09:56:37.75Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec", size = 40708, upload-time = "2025-11-12T09:56:36.333Z" }, @@ -614,7 +616,7 @@ wheels = [ [[package]] name = "executing" version = "2.2.1" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, @@ -623,7 +625,7 @@ wheels = [ [[package]] name = "google-auth" version = "2.49.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "cryptography" }, { name = "pyasn1-modules" }, @@ -637,7 +639,7 @@ wheels = [ [[package]] name = "googleapis-common-protos" version = "1.73.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "protobuf" }, ] @@ -649,7 +651,7 @@ wheels = [ [[package]] name = "grpcio" version = "1.78.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "typing-extensions" }, ] @@ -710,7 +712,7 @@ wheels = [ [[package]] name = "grpcio-status" version = "1.78.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "googleapis-common-protos" }, { name = "grpcio" }, @@ -724,7 +726,7 @@ wheels = [ [[package]] name = "h11" version = "0.16.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, @@ -733,7 +735,7 @@ wheels = [ [[package]] name = "httpcore" version = "1.0.9" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "certifi" }, { name = "h11" }, @@ -746,7 +748,7 @@ wheels = [ [[package]] name = "httpx" version = "0.28.1" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "anyio" }, { name = "certifi" }, @@ -761,7 +763,7 @@ wheels = [ [[package]] name = "idna" version = "3.11" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, @@ -770,7 +772,7 @@ wheels = [ [[package]] name = "importlib-metadata" version = "8.7.1" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "zipp", marker = "python_full_version < '3.11'" }, ] @@ -782,7 +784,7 @@ wheels = [ [[package]] name = "iniconfig" version = "2.3.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, @@ -791,7 +793,7 @@ wheels = [ [[package]] name = "ipython" version = "8.38.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } resolution-markers = [ "python_full_version < '3.11'", ] @@ -816,7 +818,7 @@ wheels = [ [[package]] name = "ipython" version = "9.10.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } resolution-markers = [ "python_full_version == '3.11.*'", ] @@ -841,7 +843,7 @@ wheels = [ [[package]] name = "ipython" version = "9.11.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } resolution-markers = [ "python_full_version >= '3.12'", ] @@ -865,7 +867,7 @@ wheels = [ [[package]] name = "ipython-pygments-lexers" version = "1.1.1" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "pygments", marker = "python_full_version >= '3.11'" }, ] @@ -877,12 +879,12 @@ wheels = [ [[package]] name = "ipywidgets" version = "8.1.8" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "comm" }, - { name = "ipython", version = "8.38.0", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.10.0", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" }, marker = "python_full_version == '3.11.*'" }, - { name = "ipython", version = "9.11.0", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" }, marker = "python_full_version >= '3.12'" }, + { name = "ipython", version = "8.38.0", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" }, marker = "python_full_version < '3.11'" }, + { name = "ipython", version = "9.10.0", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "ipython", version = "9.11.0", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" }, marker = "python_full_version >= '3.12'" }, { name = "jupyterlab-widgets" }, { name = "traitlets" }, { name = "widgetsnbextension" }, @@ -895,7 +897,7 @@ wheels = [ [[package]] name = "jedi" version = "0.19.2" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "parso" }, ] @@ -907,7 +909,7 @@ wheels = [ [[package]] name = "jiter" version = "0.13.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/0d/5e/4ec91646aee381d01cdb9974e30882c9cd3b8c5d1079d6b5ff4af522439a/jiter-0.13.0.tar.gz", hash = "sha256:f2839f9c2c7e2dffc1bc5929a510e14ce0a946be9365fd1219e7ef342dae14f4", size = 164847, upload-time = "2026-02-02T12:37:56.441Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d0/5a/41da76c5ea07bec1b0472b6b2fdb1b651074d504b19374d7e130e0cdfb25/jiter-0.13.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2ffc63785fd6c7977defe49b9824ae6ce2b2e2b77ce539bdaf006c26da06342e", size = 311164, upload-time = "2026-02-02T12:35:17.688Z" }, @@ -1004,7 +1006,7 @@ wheels = [ [[package]] name = "jsonpatch" version = "1.33" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "jsonpointer" }, ] @@ -1016,7 +1018,7 @@ wheels = [ [[package]] name = "jsonpointer" version = "3.0.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/6a/0a/eebeb1fa92507ea94016a2a790b93c2ae41a7e18778f85471dc54475ed25/jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef", size = 9114, upload-time = "2024-06-10T19:24:42.462Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595, upload-time = "2024-06-10T19:24:40.698Z" }, @@ -1025,7 +1027,7 @@ wheels = [ [[package]] name = "jupyterlab-widgets" version = "3.0.16" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/26/2d/ef58fed122b268c69c0aa099da20bc67657cdfb2e222688d5731bd5b971d/jupyterlab_widgets-3.0.16.tar.gz", hash = "sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0", size = 897423, upload-time = "2025-11-01T21:11:29.724Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl", hash = "sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8", size = 914926, upload-time = "2025-11-01T21:11:28.008Z" }, @@ -1034,7 +1036,7 @@ wheels = [ [[package]] name = "langchain-core" version = "1.2.20" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "jsonpatch" }, { name = "langsmith" }, @@ -1053,7 +1055,7 @@ wheels = [ [[package]] name = "langchain-openai" version = "1.1.11" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "langchain-core" }, { name = "openai" }, @@ -1067,7 +1069,7 @@ wheels = [ [[package]] name = "langsmith" version = "0.7.22" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "httpx" }, { name = "orjson", marker = "platform_python_implementation != 'PyPy'" }, @@ -1087,7 +1089,7 @@ wheels = [ [[package]] name = "matplotlib-inline" version = "0.2.1" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "traitlets" }, ] @@ -1099,7 +1101,7 @@ wheels = [ [[package]] name = "numpy" version = "1.26.4" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } resolution-markers = [ "python_full_version == '3.11.*'", "python_full_version < '3.11'", @@ -1135,7 +1137,7 @@ wheels = [ [[package]] name = "numpy" version = "2.4.3" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } resolution-markers = [ "python_full_version >= '3.12'", ] @@ -1217,7 +1219,7 @@ wheels = [ [[package]] name = "openai" version = "2.26.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "anyio" }, { name = "distro" }, @@ -1236,7 +1238,7 @@ wheels = [ [[package]] name = "orjson" version = "3.11.7" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/53/45/b268004f745ede84e5798b48ee12b05129d19235d0e15267aa57dcdb400b/orjson-3.11.7.tar.gz", hash = "sha256:9b1a67243945819ce55d24a30b59d6a168e86220452d2c96f4d1f093e71c0c49", size = 6144992, upload-time = "2026-02-02T15:38:49.29Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/de/1a/a373746fa6d0e116dd9e54371a7b54622c44d12296d5d0f3ad5e3ff33490/orjson-3.11.7-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a02c833f38f36546ba65a452127633afce4cf0dd7296b753d3bb54e55e5c0174", size = 229140, upload-time = "2026-02-02T15:37:06.082Z" }, @@ -1317,7 +1319,7 @@ wheels = [ [[package]] name = "packaging" version = "26.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, @@ -1326,10 +1328,10 @@ wheels = [ [[package]] name = "pandas" version = "2.3.3" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ - { name = "numpy", version = "1.26.4", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" }, marker = "python_full_version < '3.12'" }, - { name = "numpy", version = "2.4.3", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" }, marker = "python_full_version >= '3.12'" }, + { name = "numpy", version = "1.26.4", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.4.3", source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" }, marker = "python_full_version >= '3.12'" }, { name = "python-dateutil" }, { name = "pytz" }, { name = "tzdata" }, @@ -1388,7 +1390,7 @@ wheels = [ [[package]] name = "parso" version = "0.8.6" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/81/76/a1e769043c0c0c9fe391b702539d594731a4362334cdf4dc25d0c09761e7/parso-0.8.6.tar.gz", hash = "sha256:2b9a0332696df97d454fa67b81618fd69c35a7b90327cbe6ba5c92d2c68a7bfd", size = 401621, upload-time = "2026-02-09T15:45:24.425Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl", hash = "sha256:2c549f800b70a5c4952197248825584cb00f033b29c692671d3bf08bf380baff", size = 106894, upload-time = "2026-02-09T15:45:21.391Z" }, @@ -1397,7 +1399,7 @@ wheels = [ [[package]] name = "pexpect" version = "4.9.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "ptyprocess" }, ] @@ -1409,7 +1411,7 @@ wheels = [ [[package]] name = "pluggy" version = "1.6.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, @@ -1418,7 +1420,7 @@ wheels = [ [[package]] name = "prompt-toolkit" version = "3.0.52" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "wcwidth" }, ] @@ -1430,7 +1432,7 @@ wheels = [ [[package]] name = "protobuf" version = "6.33.5" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/ba/25/7c72c307aafc96fa87062aa6291d9f7c94836e43214d43722e86037aac02/protobuf-6.33.5.tar.gz", hash = "sha256:6ddcac2a081f8b7b9642c09406bc6a4290128fce5f471cddd165960bb9119e5c", size = 444465, upload-time = "2026-01-29T21:51:33.494Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b1/79/af92d0a8369732b027e6d6084251dd8e782c685c72da161bd4a2e00fbabb/protobuf-6.33.5-cp310-abi3-win32.whl", hash = "sha256:d71b040839446bac0f4d162e758bea99c8251161dae9d0983a3b88dee345153b", size = 425769, upload-time = "2026-01-29T21:51:21.751Z" }, @@ -1445,7 +1447,7 @@ wheels = [ [[package]] name = "ptyprocess" version = "0.7.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, @@ -1454,7 +1456,7 @@ wheels = [ [[package]] name = "pure-eval" version = "0.2.3" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, @@ -1463,7 +1465,7 @@ wheels = [ [[package]] name = "py4j" version = "0.10.9.7" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } resolution-markers = [ "python_full_version == '3.11.*'", "python_full_version < '3.11'", @@ -1476,7 +1478,7 @@ wheels = [ [[package]] name = "py4j" version = "0.10.9.9" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } resolution-markers = [ "python_full_version >= '3.12'", ] @@ -1488,7 +1490,7 @@ wheels = [ [[package]] name = "pyarrow" version = "23.0.1" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/88/22/134986a4cc224d593c1afde5494d18ff629393d74cc2eddb176669f234a4/pyarrow-23.0.1.tar.gz", hash = "sha256:b8c5873e33440b2bc2f4a79d2b47017a89c5a24116c055625e6f2ee50523f019", size = 1167336, upload-time = "2026-02-16T10:14:12.39Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/bc/a8/24e5dc6855f50a62936ceb004e6e9645e4219a8065f304145d7fb8a79d5d/pyarrow-23.0.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:3fab8f82571844eb3c460f90a75583801d14ca0cc32b1acc8c361650e006fd56", size = 34307390, upload-time = "2026-02-16T10:08:08.654Z" }, @@ -1545,7 +1547,7 @@ wheels = [ [[package]] name = "pyasn1" version = "0.6.3" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/5c/5f/6583902b6f79b399c9c40674ac384fd9cd77805f9e6205075f828ef11fb2/pyasn1-0.6.3.tar.gz", hash = "sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf", size = 148685, upload-time = "2026-03-17T01:06:53.382Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/5d/a0/7d793dce3fa811fe047d6ae2431c672364b462850c6235ae306c0efd025f/pyasn1-0.6.3-py3-none-any.whl", hash = "sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde", size = 83997, upload-time = "2026-03-17T01:06:52.036Z" }, @@ -1554,7 +1556,7 @@ wheels = [ [[package]] name = "pyasn1-modules" version = "0.4.2" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "pyasn1" }, ] @@ -1566,7 +1568,7 @@ wheels = [ [[package]] name = "pycodestyle" version = "2.14.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/11/e0/abfd2a0d2efe47670df87f3e3a0e2edda42f055053c85361f19c0e2c1ca8/pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783", size = 39472, upload-time = "2025-06-20T18:49:48.75Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d7/27/a58ddaf8c588a3ef080db9d0b7e0b97215cee3a45df74f3a94dbbf5c893a/pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d", size = 31594, upload-time = "2025-06-20T18:49:47.491Z" }, @@ -1575,7 +1577,7 @@ wheels = [ [[package]] name = "pycparser" version = "3.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, @@ -1584,7 +1586,7 @@ wheels = [ [[package]] name = "pydantic" version = "2.12.5" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "annotated-types" }, { name = "pydantic-core" }, @@ -1599,7 +1601,7 @@ wheels = [ [[package]] name = "pydantic-core" version = "2.41.5" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "typing-extensions" }, ] @@ -1717,7 +1719,7 @@ wheels = [ [[package]] name = "pyfakefs" version = "6.1.4" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/76/11/efd26f319da2a170f42594efdd387df9abdbd3c213deb2251d602d9b8e2d/pyfakefs-6.1.4.tar.gz", hash = "sha256:58d5902282085e8ff03f95316ce133858904096f7adbe622efef899b90695698", size = 226757, upload-time = "2026-03-04T18:02:29.62Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/6b/0f/8fbdc47cfda8ab497b21ad66220407201ed2fc2de91640151b62bc6571af/pyfakefs-6.1.4-py3-none-any.whl", hash = "sha256:46bbc7520a1524af2461ddcaf4a5a800596c750bfdb75afa1afd985bf1e39536", size = 239887, upload-time = "2026-03-04T18:02:28.029Z" }, @@ -1726,7 +1728,7 @@ wheels = [ [[package]] name = "pygments" version = "2.19.2" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, @@ -1735,7 +1737,7 @@ wheels = [ [[package]] name = "pyproject-hooks" version = "1.2.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228, upload-time = "2024-09-29T09:24:13.293Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216, upload-time = "2024-09-29T09:24:11.978Z" }, @@ -1744,7 +1746,7 @@ wheels = [ [[package]] name = "pytest" version = "9.0.2" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, @@ -1762,7 +1764,7 @@ wheels = [ [[package]] name = "pytest-cov" version = "7.0.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "coverage", extra = ["toml"] }, { name = "pluggy" }, @@ -1776,7 +1778,7 @@ wheels = [ [[package]] name = "pytest-mock" version = "3.15.1" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "pytest" }, ] @@ -1788,7 +1790,7 @@ wheels = [ [[package]] name = "pytest-rerunfailures" version = "16.1" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "packaging" }, { name = "pytest" }, @@ -1801,7 +1803,7 @@ wheels = [ [[package]] name = "pytest-xdist" version = "3.8.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "execnet" }, { name = "pytest" }, @@ -1814,7 +1816,7 @@ wheels = [ [[package]] name = "python-dateutil" version = "2.9.0.post0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "six" }, ] @@ -1826,7 +1828,7 @@ wheels = [ [[package]] name = "pytz" version = "2026.1.post1" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/56/db/b8721d71d945e6a8ac63c0fc900b2067181dbb50805958d4d4661cf7d277/pytz-2026.1.post1.tar.gz", hash = "sha256:3378dde6a0c3d26719182142c56e60c7f9af7e968076f31aae569d72a0358ee1", size = 321088, upload-time = "2026-03-03T07:47:50.683Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/10/99/781fe0c827be2742bcc775efefccb3b048a3a9c6ce9aec0cbf4a101677e5/pytz-2026.1.post1-py2.py3-none-any.whl", hash = "sha256:f2fd16142fda348286a75e1a524be810bb05d444e5a081f37f7affc635035f7a", size = 510489, upload-time = "2026-03-03T07:47:49.167Z" }, @@ -1835,7 +1837,7 @@ wheels = [ [[package]] name = "pyyaml" version = "6.0.3" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, @@ -1899,7 +1901,7 @@ wheels = [ [[package]] name = "regex" version = "2026.2.28" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/8b/71/41455aa99a5a5ac1eaf311f5d8efd9ce6433c03ac1e0962de163350d0d97/regex-2026.2.28.tar.gz", hash = "sha256:a729e47d418ea11d03469f321aaf67cdee8954cde3ff2cf8403ab87951ad10f2", size = 415184, upload-time = "2026-02-28T02:19:42.792Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/70/b8/845a927e078f5e5cc55d29f57becbfde0003d52806544531ab3f2da4503c/regex-2026.2.28-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fc48c500838be6882b32748f60a15229d2dea96e59ef341eaa96ec83538f498d", size = 488461, upload-time = "2026-02-28T02:15:48.405Z" }, @@ -2020,7 +2022,7 @@ wheels = [ [[package]] name = "requests" version = "2.32.5" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "certifi" }, { name = "charset-normalizer" }, @@ -2035,7 +2037,7 @@ wheels = [ [[package]] name = "requests-mock" version = "1.12.1" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "requests" }, ] @@ -2047,7 +2049,7 @@ wheels = [ [[package]] name = "requests-toolbelt" version = "1.0.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "requests" }, ] @@ -2059,7 +2061,7 @@ wheels = [ [[package]] name = "rsa" version = "4.9.1" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "pyasn1" }, ] @@ -2071,7 +2073,7 @@ wheels = [ [[package]] name = "ruff" version = "0.5.6" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f7/69/96766da2cdb5605e6a31ef2734aff0be17901cefb385b885c2ab88896d76/ruff-0.5.6.tar.gz", hash = "sha256:07c9e3c2a8e1fe377dd460371c3462671a728c981c3205a5217291422209f642", size = 2444466, upload-time = "2024-08-02T15:53:00.099Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/85/54/af603686de0e9ba6c1798bd83d335e345e7c9ce4deed95a1071448bb6563/ruff-0.5.6-py3-none-linux_armv6l.whl", hash = "sha256:a0ef5930799a05522985b9cec8290b185952f3fcd86c1772c3bdbd732667fdcd", size = 9543715, upload-time = "2024-08-02T15:51:36.229Z" }, @@ -2096,7 +2098,7 @@ wheels = [ [[package]] name = "setuptools" version = "82.0.1" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316, upload-time = "2026-03-09T12:47:17.221Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223, upload-time = "2026-03-09T12:47:15.026Z" }, @@ -2105,7 +2107,7 @@ wheels = [ [[package]] name = "six" version = "1.17.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, @@ -2114,7 +2116,7 @@ wheels = [ [[package]] name = "sniffio" version = "1.3.1" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, @@ -2123,7 +2125,7 @@ wheels = [ [[package]] name = "stack-data" version = "0.6.3" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "asttokens" }, { name = "executing" }, @@ -2137,7 +2139,7 @@ wheels = [ [[package]] name = "tenacity" version = "9.1.4" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/47/c6/ee486fd809e357697ee8a44d3d69222b344920433d3b6666ccd9b374630c/tenacity-9.1.4.tar.gz", hash = "sha256:adb31d4c263f2bd041081ab33b498309a57c77f9acf2db65aadf0898179cf93a", size = 49413, upload-time = "2026-02-07T10:45:33.841Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d7/c1/eb8f9debc45d3b7918a32ab756658a0904732f75e555402972246b0b8e71/tenacity-9.1.4-py3-none-any.whl", hash = "sha256:6095a360c919085f28c6527de529e76a06ad89b23659fa881ae0649b867a9d55", size = 28926, upload-time = "2026-02-07T10:45:32.24Z" }, @@ -2146,7 +2148,7 @@ wheels = [ [[package]] name = "tiktoken" version = "0.12.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "regex" }, { name = "requests" }, @@ -2207,7 +2209,7 @@ wheels = [ [[package]] name = "tomli" version = "2.4.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477, upload-time = "2026-01-11T11:22:38.165Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867", size = 153663, upload-time = "2026-01-11T11:21:45.27Z" }, @@ -2261,7 +2263,7 @@ wheels = [ [[package]] name = "tqdm" version = "4.67.3" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] @@ -2273,7 +2275,7 @@ wheels = [ [[package]] name = "traitlets" version = "5.14.3" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, @@ -2282,7 +2284,7 @@ wheels = [ [[package]] name = "typing-extensions" version = "4.15.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, @@ -2291,7 +2293,7 @@ wheels = [ [[package]] name = "typing-inspection" version = "0.4.2" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "typing-extensions" }, ] @@ -2303,7 +2305,7 @@ wheels = [ [[package]] name = "tzdata" version = "2025.3" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" }, @@ -2312,7 +2314,7 @@ wheels = [ [[package]] name = "urllib3" version = "2.6.3" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, @@ -2321,7 +2323,7 @@ wheels = [ [[package]] name = "uuid-utils" version = "0.14.1" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/7b/d1/38a573f0c631c062cf42fa1f5d021d4dd3c31fb23e4376e4b56b0c9fbbed/uuid_utils-0.14.1.tar.gz", hash = "sha256:9bfc95f64af80ccf129c604fb6b8ca66c6f256451e32bc4570f760e4309c9b69", size = 22195, upload-time = "2026-02-20T22:50:38.833Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/43/b7/add4363039a34506a58457d96d4aa2126061df3a143eb4d042aedd6a2e76/uuid_utils-0.14.1-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:93a3b5dc798a54a1feb693f2d1cb4cf08258c32ff05ae4929b5f0a2ca624a4f0", size = 604679, upload-time = "2026-02-20T22:50:27.469Z" }, @@ -2350,7 +2352,7 @@ wheels = [ [[package]] name = "wcwidth" version = "0.6.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/35/a2/8e3becb46433538a38726c948d3399905a4c7cabd0df578ede5dc51f0ec2/wcwidth-0.6.0.tar.gz", hash = "sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159", size = 159684, upload-time = "2026-02-06T19:19:40.919Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189, upload-time = "2026-02-06T19:19:39.646Z" }, @@ -2359,7 +2361,7 @@ wheels = [ [[package]] name = "wheel" version = "0.46.3" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } dependencies = [ { name = "packaging" }, ] @@ -2371,7 +2373,7 @@ wheels = [ [[package]] name = "widgetsnbextension" version = "4.0.15" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/bd/f4/c67440c7fb409a71b7404b7aefcd7569a9c0d6bd071299bf4198ae7a5d95/widgetsnbextension-4.0.15.tar.gz", hash = "sha256:de8610639996f1567952d763a5a41af8af37f2575a41f9852a38f947eb82a3b9", size = 1097402, upload-time = "2025-11-01T21:15:55.178Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl", hash = "sha256:8156704e4346a571d9ce73b84bee86a29906c9abfd7223b7228a28899ccf3366", size = 2196503, upload-time = "2025-11-01T21:15:53.565Z" }, @@ -2380,7 +2382,7 @@ wheels = [ [[package]] name = "xxhash" version = "3.6.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/02/84/30869e01909fb37a6cc7e18688ee8bf1e42d57e7e0777636bd47524c43c7/xxhash-3.6.0.tar.gz", hash = "sha256:f0162a78b13a0d7617b2845b90c763339d1f1d82bb04a4b07f4ab535cc5e05d6", size = 85160, upload-time = "2025-10-02T14:37:08.097Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/34/ee/f9f1d656ad168681bb0f6b092372c1e533c4416b8069b1896a175c46e484/xxhash-3.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:87ff03d7e35c61435976554477a7f4cd1704c3596a89a8300d5ce7fc83874a71", size = 32845, upload-time = "2025-10-02T14:33:51.573Z" }, @@ -2498,7 +2500,7 @@ wheels = [ [[package]] name = "zipp" version = "3.23.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, @@ -2507,7 +2509,7 @@ wheels = [ [[package]] name = "zstandard" version = "0.25.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } +source = { registry = "https://pypi-proxy.cloud.databricks.com/simple" } sdist = { url = "https://files.pythonhosted.org/packages/fd/aa/3e0508d5a5dd96529cdc5a97011299056e14c6505b678fd58938792794b1/zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b", size = 711513, upload-time = "2025-09-14T22:15:54.002Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/56/7a/28efd1d371f1acd037ac64ed1c5e2b41514a6cc937dd6ab6a13ab9f0702f/zstandard-0.25.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e59fdc271772f6686e01e1b3b74537259800f57e24280be3f29c8a0deb1904dd", size = 795256, upload-time = "2025-09-14T22:15:56.415Z" },