From fbd6e7a54f2d9f60c2b80aedafe17e71baac4c5b Mon Sep 17 00:00:00 2001 From: radu-mocanu Date: Thu, 29 Jan 2026 09:30:57 +0200 Subject: [PATCH] feat: support system indexes --- src/uipath/_cli/_utils/_common.py | 39 +++- src/uipath/_cli/_utils/_studio_project.py | 12 +- src/uipath/_cli/cli_debug.py | 1 - src/uipath/_utils/_bindings.py | 157 +++++++++++--- .../_context_grounding_service.py | 192 ++++++++++++++++-- tests/sdk/test_bindings.py | 22 +- 6 files changed, 363 insertions(+), 60 deletions(-) diff --git a/src/uipath/_cli/_utils/_common.py b/src/uipath/_cli/_utils/_common.py index 024afd78d..7b6e972a9 100644 --- a/src/uipath/_cli/_utils/_common.py +++ b/src/uipath/_cli/_utils/_common.py @@ -5,11 +5,13 @@ from urllib.parse import urlparse import click +from pydantic import TypeAdapter from uipath.platform.common import UiPathConfig from ..._utils._bindings import ResourceOverwrite, ResourceOverwriteParser from ..._utils.constants import ENV_UIPATH_ACCESS_TOKEN +from ..models.runtime_schema import Bindings from ..spinner import Spinner from ._console import ConsoleLogger from ._studio_project import ( @@ -180,19 +182,41 @@ async def may_override_files( ) +def extract_binding_keys(bindings_file_content: str) -> list[str]: + bindings_list: list[str] = [] + bindings = TypeAdapter(Bindings).validate_python(json.loads(bindings_file_content)) + for resource in bindings.resources: + bindings_list.append(f"{resource.resource}.{resource.key}") + return bindings_list + + +def fill_missing_binding_keys( + overwrites: dict[str, ResourceOverwrite | None], binding_keys: list[str] +): + """Ensure all binding keys exist in the overwrites dict, defaulting to None.""" + for binding_key in binding_keys: + if binding_key not in overwrites: + overwrites[binding_key] = None + + async def read_resource_overwrites_from_file( directory_path: str | None = None, -) -> dict[str, ResourceOverwrite]: +) -> dict[str, ResourceOverwrite | None]: """Read resource overwrites from a JSON file.""" config_file_name = UiPathConfig.config_file_name if directory_path is not None: - file_path = Path(f"{directory_path}/{config_file_name}") + file_path = Path(directory_path) / config_file_name else: - file_path = Path(f"{config_file_name}") + file_path = Path(config_file_name) - overwrites_dict = {} + overwrites_dict: dict[str, ResourceOverwrite | None] = {} try: + # read configured bindings + with open(UiPathConfig.bindings_file_path, "r") as f: + bindings_list = extract_binding_keys(f.read()) + + # read overwrites with open(file_path, "r") as f: data = json.load(f) resource_overwrites = ( @@ -200,8 +224,11 @@ async def read_resource_overwrites_from_file( .get("internalArguments", {}) .get("resourceOverwrites", {}) ) - for key, value in resource_overwrites.items(): - overwrites_dict[key] = ResourceOverwriteParser.parse(key, value) + for key, value in resource_overwrites.items(): + overwrites_dict[key] = ResourceOverwriteParser.parse(key, value) + + fill_missing_binding_keys(overwrites_dict, bindings_list) + return overwrites_dict # Return empty dict if file doesn't exist or invalid json except FileNotFoundError: diff --git a/src/uipath/_cli/_utils/_studio_project.py b/src/uipath/_cli/_utils/_studio_project.py index 320440503..cb7222830 100644 --- a/src/uipath/_cli/_utils/_studio_project.py +++ b/src/uipath/_cli/_utils/_studio_project.py @@ -538,18 +538,25 @@ async def _get_existing_resources(self) -> List[dict[str, Any]]: ] return self._resources_cache - async def get_resource_overwrites(self) -> dict[str, ResourceOverwrite]: + async def get_resource_overwrites(self) -> dict[str, ResourceOverwrite | None]: """Get resource overwrites from the solution. Returns: dict[str, ResourceOverwrite]: Dict of resource overwrites """ + from uipath._cli._utils._common import ( + extract_binding_keys, + fill_missing_binding_keys, + ) + if not os.path.exists(UiPathConfig.bindings_file_path): return {} with open(UiPathConfig.bindings_file_path, "rb") as f: file_content = f.read() + bindings_list = extract_binding_keys(file_content.decode("utf-8")) + solution_id = await self._get_solution_id() tenant_id = os.getenv(ENV_TENANT_ID, None) @@ -572,11 +579,12 @@ async def get_resource_overwrites(self) -> dict[str, ResourceOverwrite]: files=files, ) data = response.json() - overwrites = {} + overwrites: dict[str, ResourceOverwrite | None] = {} for key, value in data.items(): overwrites[key] = ResourceOverwriteParser.parse(key, value) + fill_missing_binding_keys(overwrites, bindings_list) return overwrites async def create_virtual_resource( diff --git a/src/uipath/_cli/cli_debug.py b/src/uipath/_cli/cli_debug.py index 8bd1135c3..27f949a35 100644 --- a/src/uipath/_cli/cli_debug.py +++ b/src/uipath/_cli/cli_debug.py @@ -181,7 +181,6 @@ async def execute_debug_runtime(): # Load simulation config and set up execution context for tool mocking mocking_ctx = load_simulation_config() - span_collector: ExecutionSpanCollector | None = None execution_id = str(uuid.uuid4()) if mocking_ctx: diff --git a/src/uipath/_utils/_bindings.py b/src/uipath/_utils/_bindings.py index d65ac8503..b58ff203c 100644 --- a/src/uipath/_utils/_bindings.py +++ b/src/uipath/_utils/_bindings.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import functools import inspect from abc import ABC, abstractmethod @@ -8,7 +10,6 @@ Callable, Coroutine, Literal, - Optional, TypeVar, Union, ) @@ -35,11 +36,25 @@ def resource_identifier(self) -> str: @property @abstractmethod - def folder_identifier(self) -> str: + def folder_identifier(self) -> str | None: """The folder location identifier for this resource.""" pass +class SystemResourceOverwrite(ResourceOverwrite): + resource_type: Literal["index"] + name: str = Field(alias="name") + folder_key: str = Field(alias="folderKey") + + @property + def resource_identifier(self) -> str: + return self.name + + @property + def folder_identifier(self) -> str: + return self.folder_key + + class GenericResourceOverwrite(ResourceOverwrite): resource_type: Literal["process", "index", "app", "asset", "bucket", "mcpServer"] name: str = Field(alias="name") @@ -111,8 +126,11 @@ def parse(cls, key: str, value: dict[str, Any]) -> ResourceOverwrite: return cls._adapter.validate_python(value_with_type) -_resource_overwrites: ContextVar[Optional[dict[str, ResourceOverwrite]]] = ContextVar( - "resource_overwrites", default=None +# this context var holds a dictionary in the following format: +# {"binding_key: applied_overwrite | None"} +# for system resources (e.g. system indexes) we need to make sure that a binding is set with no corresponding overwrite +_binding_overwrites: ContextVar[dict[str, ResourceOverwrite | None] | None] = ( + ContextVar("binding_overwrites", default=None) ) @@ -120,28 +138,37 @@ class ResourceOverwritesContext: def __init__( self, get_overwrites_callable: Callable[ - [], Coroutine[Any, Any, dict[str, ResourceOverwrite]] + [], Coroutine[Any, Any, dict[str, ResourceOverwrite | None]] ], ): self.get_overwrites_callable = get_overwrites_callable - self._token: Optional[Token[Optional[dict[str, ResourceOverwrite]]]] = None + self._token: Token[dict[str, ResourceOverwrite | None] | None] | None = None self.overwrites_count = 0 - async def __aenter__(self) -> "ResourceOverwritesContext": + async def __aenter__(self) -> ResourceOverwritesContext: overwrites = await self.get_overwrites_callable() - self._token = _resource_overwrites.set(overwrites) + self._token = _binding_overwrites.set(overwrites) self.overwrites_count = len(overwrites) return self async def __aexit__(self, exc_type, exc_val, exc_tb): if self._token: - _resource_overwrites.reset(self._token) + _binding_overwrites.reset(self._token) + + +class ArgumentsProcessingResponse(BaseModel): + arguments: dict[str, Any] + can_apply_resolution: bool = False def resource_override( resource_type: str, resource_identifier: str = "name", folder_identifier: str = "folder_path", + resolution_func_name: str | None = None, + resolution_coroutine_name: str | None = None, + resolution_resource_identifier: str = "name", + resolution_folder_identifier: str = "folder_path", ) -> Callable[..., Any]: """Decorator for applying resource overrides for an overridable resource. @@ -152,6 +179,11 @@ def resource_override( resource_type: Type of resource to check for overrides (e.g., "asset", "bucket") resource_identifier: Key name for the resource ID in override data (default: "name") folder_identifier: Key name for the folder path in override data (default: "folder_path") + resolution_func_name: Optional sync callable for resolving the overwrite when binding is set but overwrite not present. + resolution_coroutine_name: Optional async callable for resolving the overwrite when binding is set but overwrite not present. + Note: those are passed string reference since decorators are evaluated at class definition time. + resolution_resource_identifier: Key name for the resource ID in resolution data (default: "name") + resolution_folder_identifier: Key name for the folder identifier in resolution data (default: "folder_path") Returns: Decorated function that receives overridden resource identifiers when applicable @@ -163,12 +195,34 @@ def resource_override( def decorator(func: Callable[..., Any]): sig = inspect.signature(func) - def process_args(args, kwargs) -> dict[str, Any]: + def apply_overwrite( + all_args: dict[str, Any], + matched_overwrite: ResourceOverwrite, + is_resolution_overwrite=False, + ) -> None: + resource_id = ( + resolution_resource_identifier + if is_resolution_overwrite + else resource_identifier + ) + folder_id = ( + resolution_folder_identifier + if is_resolution_overwrite + else folder_identifier + ) + + if resource_id in sig.parameters: + all_args[resource_id] = matched_overwrite.resource_identifier + if folder_id in sig.parameters: + all_args[folder_id] = matched_overwrite.folder_identifier + + def process_args(args, kwargs) -> ArgumentsProcessingResponse: """Process arguments and apply resource overrides if applicable.""" # convert both args and kwargs to single dict bound = sig.bind_partial(*args, **kwargs) bound.apply_defaults() all_args = dict(bound.arguments) + if ( "kwargs" in sig.parameters and sig.parameters["kwargs"].kind == inspect.Parameter.VAR_KEYWORD @@ -177,7 +231,8 @@ def process_args(args, kwargs) -> dict[str, Any]: all_args.update(extra_kwargs) # Get overwrites from context variable - context_overwrites = _resource_overwrites.get() + + context_overwrites = _binding_overwrites.get() if context_overwrites is not None: resource_identifier_value = all_args.get(resource_identifier) @@ -192,34 +247,90 @@ def process_args(args, kwargs) -> dict[str, Any]: else key ) - matched_overwrite = context_overwrites.get(key) + try: + matched_overwrite = context_overwrites[key] + except KeyError: + # binding not set, default to original parameters + return ArgumentsProcessingResponse(arguments=all_args) # Apply the matched overwrite if matched_overwrite is not None: - if resource_identifier in sig.parameters: - all_args[resource_identifier] = ( - matched_overwrite.resource_identifier - ) - if folder_identifier in sig.parameters: - all_args[folder_identifier] = ( - matched_overwrite.folder_identifier - ) + apply_overwrite(all_args, matched_overwrite) + return ArgumentsProcessingResponse(arguments=all_args) + + # binding is set but no corresponding overwrite exists + # we can try to apply the resolution + return ArgumentsProcessingResponse( + arguments=all_args, can_apply_resolution=True + ) + + return ArgumentsProcessingResponse(arguments=all_args) - return all_args + def filter_function_args(func: Callable[..., Any], all_args: dict[str, Any]): + callable_sig = inspect.signature(func) + filtered_args = {} + for param_name in callable_sig.parameters: + if param_name != "self" and param_name in all_args: + filtered_args[param_name] = all_args[param_name] + return filtered_args if inspect.iscoroutinefunction(func): @functools.wraps(func) async def async_wrapper(*args, **kwargs): - all_args = process_args(args, kwargs) + process_args_response = process_args(args, kwargs) + all_args = process_args_response.arguments + if not ( + process_args_response.can_apply_resolution + and resolution_coroutine_name + ): + return await func(**all_args) + + # apply resolution coroutine + invoked_class_instance = args[0] # self + resolution_coroutine: ( + Callable[..., Coroutine[Any, Any, ResourceOverwrite | None]] | None + ) = getattr(invoked_class_instance, resolution_coroutine_name) + if resolution_coroutine and ( + resource_overwrite := await resolution_coroutine( + **filter_function_args( + resolution_coroutine, process_args_response.arguments + ) + ) + ): + apply_overwrite( + all_args, resource_overwrite, is_resolution_overwrite=True + ) return await func(**all_args) return async_wrapper + else: @functools.wraps(func) def wrapper(*args, **kwargs): - all_args = process_args(args, kwargs) + process_args_response = process_args(args, kwargs) + all_args = process_args_response.arguments + if not ( + process_args_response.can_apply_resolution and resolution_func_name + ): + return func(**all_args) + + # apply resolution function + invoked_class_instance = args[0] # self + resolution_func: Callable[..., ResourceOverwrite | None] | None = ( + getattr(invoked_class_instance, resolution_func_name) + ) + if resolution_func and ( + resource_overwrite := resolution_func( + **filter_function_args( + resolution_func, process_args_response.arguments + ) + ) + ): + apply_overwrite( + all_args, resource_overwrite, is_resolution_overwrite=True + ) return func(**all_args) return wrapper diff --git a/src/uipath/platform/context_grounding/_context_grounding_service.py b/src/uipath/platform/context_grounding/_context_grounding_service.py index 31cadcc0a..093c77fcf 100644 --- a/src/uipath/platform/context_grounding/_context_grounding_service.py +++ b/src/uipath/platform/context_grounding/_context_grounding_service.py @@ -5,6 +5,9 @@ from pydantic import Field, TypeAdapter from ..._utils import Endpoint, RequestSpec, header_folder, resource_override +from ..._utils._bindings import ( + SystemResourceOverwrite, +) from ..._utils._ssl_context import get_httpx_client_kwargs from ..._utils.constants import ( LLMV4_REQUEST, @@ -234,7 +237,10 @@ def retrieve( raise Exception("ContextGroundingIndex not found") from e @resource_override(resource_type="index") - @traced(name="contextgrounding_retrieve", run_type="uipath") + @traced( + name="contextgrounding_retrieve", + run_type="uipath", + ) async def retrieve_async( self, name: str, @@ -343,7 +349,6 @@ async def retrieve_by_id_async( return response.json() - @resource_override(resource_type="index") @traced(name="contextgrounding_create_index", run_type="uipath") def create_index( self, @@ -398,7 +403,6 @@ def create_index( return ContextGroundingIndex.model_validate(response.json()) - @resource_override(resource_type="index") @traced(name="contextgrounding_create_index", run_type="uipath") async def create_index_async( self, @@ -453,7 +457,6 @@ async def create_index_async( return ContextGroundingIndex.model_validate(response.json()) - @resource_override(resource_type="index") @traced(name="contextgrounding_create_ephemeral_index", run_type="uipath") def create_ephemeral_index( self, @@ -483,7 +486,6 @@ def create_ephemeral_index( return ContextGroundingIndex.model_validate(response.json()) - @resource_override(resource_type="index") @traced(name="contextgrounding_create_ephemeral_index", run_type="uipath") async def create_ephemeral_index_async( self, @@ -513,7 +515,12 @@ async def create_ephemeral_index_async( return ContextGroundingIndex.model_validate(response.json()) - @resource_override(resource_type="index", resource_identifier="index_name") + @resource_override( + resource_type="index", + resource_identifier="index_name", + resolution_func_name="_resolve_system_index", + resolution_folder_identifier="folder_key", + ) @traced(name="contextgrounding_retrieve_deep_rag", run_type="uipath") def retrieve_deep_rag( self, @@ -542,7 +549,12 @@ def retrieve_deep_rag( ) return DeepRagResponse.model_validate(response.json()) - @resource_override(resource_type="index", resource_identifier="index_name") + @resource_override( + resource_type="index", + resource_identifier="index_name", + resolution_coroutine_name="_resolve_system_index_async", + resolution_folder_identifier="folder_key", + ) @traced(name="contextgrounding_retrieve_deep_rag_async", run_type="uipath") async def retrieve_deep_rag_async( self, @@ -573,7 +585,12 @@ async def retrieve_deep_rag_async( return DeepRagResponse.model_validate(response.json()) - @resource_override(resource_type="index", resource_identifier="index_name") + @resource_override( + resource_type="index", + resource_identifier="index_name", + resolution_func_name="_resolve_system_index", + resolution_folder_identifier="folder_key", + ) @traced(name="contextgrounding_start_batch_transform", run_type="uipath") def start_batch_transform( self, @@ -631,7 +648,12 @@ def start_batch_transform( ) return BatchTransformCreationResponse.model_validate(response.json()) - @resource_override(resource_type="index", resource_identifier="index_name") + @resource_override( + resource_type="index", + resource_identifier="index_name", + resolution_coroutine_name="_resolve_system_index_async", + resolution_folder_identifier="folder_key", + ) @traced(name="contextgrounding_start_batch_transform_async", run_type="uipath") async def start_batch_transform_async( self, @@ -689,7 +711,12 @@ async def start_batch_transform_async( ) return BatchTransformCreationResponse.model_validate(response.json()) - @resource_override(resource_type="index", resource_identifier="index_name") + @resource_override( + resource_type="index", + resource_identifier="index_name", + resolution_func_name="_resolve_system_index", + resolution_folder_identifier="folder_key", + ) @traced(name="contextgrounding_retrieve_batch_transform", run_type="uipath") def retrieve_batch_transform( self, @@ -715,7 +742,12 @@ def retrieve_batch_transform( ) return BatchTransformResponse.model_validate(response.json()) - @resource_override(resource_type="index", resource_identifier="index_name") + @resource_override( + resource_type="index", + resource_identifier="index_name", + resolution_coroutine_name="_resolve_system_index_async", + resolution_folder_identifier="folder_key", + ) @traced(name="contextgrounding_retrieve_batch_transform_async", run_type="uipath") async def retrieve_batch_transform_async( self, @@ -741,7 +773,12 @@ async def retrieve_batch_transform_async( ) return BatchTransformResponse.model_validate(response.json()) - @resource_override(resource_type="index", resource_identifier="index_name") + @resource_override( + resource_type="index", + resource_identifier="index_name", + resolution_func_name="_resolve_system_index", + resolution_folder_identifier="folder_key", + ) @traced(name="contextgrounding_download_batch_transform_result", run_type="uipath") def download_batch_transform_result( self, @@ -787,7 +824,12 @@ def download_batch_transform_result( file_content = client.get(uri_response.uri).content file.write(file_content) - @resource_override(resource_type="index", resource_identifier="index_name") + @resource_override( + resource_type="index", + resource_identifier="index_name", + resolution_coroutine_name="_resolve_system_index_async", + resolution_folder_identifier="folder_key", + ) @traced( name="contextgrounding_download_batch_transform_result_async", run_type="uipath" ) @@ -837,7 +879,12 @@ async def download_batch_transform_result_async( with open(destination_path, "wb") as file: file.write(file_content) - @resource_override(resource_type="index", resource_identifier="index_name") + @resource_override( + resource_type="index", + resource_identifier="index_name", + resolution_func_name="_resolve_system_index", + resolution_folder_identifier="folder_key", + ) @traced(name="contextgrounding_start_deep_rag", run_type="uipath") def start_deep_rag( self, @@ -889,7 +936,12 @@ def start_deep_rag( return DeepRagCreationResponse.model_validate(response.json()) - @resource_override(resource_type="index", resource_identifier="index_name") + @resource_override( + resource_type="index", + resource_identifier="index_name", + resolution_coroutine_name="_resolve_system_index_async", + resolution_folder_identifier="folder_key", + ) @traced(name="contextgrounding_start_deep_rag_async", run_type="uipath") async def start_deep_rag_async( self, @@ -942,7 +994,11 @@ async def start_deep_rag_async( return DeepRagCreationResponse.model_validate(response.json()) - @resource_override(resource_type="index") + @resource_override( + resource_type="index", + resolution_func_name="_resolve_system_index", + resolution_folder_identifier="folder_key", + ) @traced(name="contextgrounding_search", run_type="uipath") def search( self, @@ -991,7 +1047,11 @@ def search( response.json() ) - @resource_override(resource_type="index") + @resource_override( + resource_type="index", + resolution_coroutine_name="_resolve_system_index_async", + resolution_folder_identifier="folder_key", + ) @traced(name="contextgrounding_search", run_type="uipath") async def search_async( self, @@ -1423,6 +1483,16 @@ def _search_spec( }, ) + def _retrieve_system_index_spec( + self, + name: str, + ) -> RequestSpec: + return RequestSpec( + method="GET", + endpoint=Endpoint("/ecs_/v2/indexes/AllSystemIndexes"), + params={"$filter": f"Name eq '{name}'"}, + ) + def _deep_rag_creation_spec( self, index_id: str, @@ -1564,3 +1634,91 @@ def _extract_bucket_info(self, index: ContextGroundingIndex) -> Tuple[str, str]: raise UnsupportedDataSourceException("add_to_index") return bucket_name, folder + + async def _resolve_system_index_async( + self, + name: str, + **kwargs, + ) -> SystemResourceOverwrite | None: + """Asynchronously retrieve context grounding system index by its name. + + Args: + name (str): The name of the context index to retrieve. + + Returns: + SystemResourceOverwrite: The index name and folder key, if found. + + """ + spec = self._retrieve_system_index_spec( + name, + ) + + response = ( + await self.request_async( + spec.method, + spec.endpoint, + params=spec.params, + headers=spec.headers, + ) + ).json() + try: + context_grounding_index = next( + ContextGroundingIndex.model_validate(item) + for item in response["value"] + if item["name"] == name + ) + except StopIteration: + return None + + assert context_grounding_index.name is not None + assert context_grounding_index.folder_key is not None + + return SystemResourceOverwrite( + resource_type="index", + name=context_grounding_index.name, + folder_key=context_grounding_index.folder_key, + ) + + def _resolve_system_index( + self, + name: str, + **kwargs, + ) -> SystemResourceOverwrite | None: + """Retrieve context grounding system index by its name. + + Args: + name (str): The name of the context index to retrieve. + + Returns: + SystemResourceOverwrite: The index name and folder key, if found. + + """ + spec = self._retrieve_system_index_spec( + name, + ) + + response = ( + self.request( + spec.method, + spec.endpoint, + params=spec.params, + headers=spec.headers, + ) + ).json() + try: + context_grounding_index = next( + ContextGroundingIndex.model_validate(item) + for item in response["value"] + if item["name"] == name + ) + except StopIteration: + return None + + assert context_grounding_index.name is not None + assert context_grounding_index.folder_key is not None + + return SystemResourceOverwrite( + resource_type="index", + name=context_grounding_index.name, + folder_key=context_grounding_index.folder_key, + ) diff --git a/tests/sdk/test_bindings.py b/tests/sdk/test_bindings.py index 5aaf776bd..890b598ff 100644 --- a/tests/sdk/test_bindings.py +++ b/tests/sdk/test_bindings.py @@ -8,7 +8,7 @@ GenericResourceOverwrite, ResourceOverwriteParser, ResourceOverwritesContext, - _resource_overwrites, + _binding_overwrites, ) @@ -26,12 +26,12 @@ def dummy_func(name, folder_path): return name, folder_path # Set the context variable - token = _resource_overwrites.set(overwrites) + token = _binding_overwrites.set(overwrites) try: result = dummy_func("old_name", "old_folder") assert result == ("new_name", "new_folder") finally: - _resource_overwrites.reset(token) + _binding_overwrites.reset(token) def test_infer_bindings_overwrites_without_folder_path(self): """Test that infer_bindings overwrites when key doesn't include folder_path.""" @@ -46,12 +46,12 @@ def dummy_func(name, folder_path): return name, folder_path # Set the context variable - token = _resource_overwrites.set(overwrites) + token = _binding_overwrites.set(overwrites) try: result = dummy_func("old_name", "old_folder") assert result == ("new_name", "new_folder") finally: - _resource_overwrites.reset(token) + _binding_overwrites.reset(token) def test_infer_bindings_skips_when_no_context(self): """Test that infer_bindings doesn't overwrite when context variable is not set.""" @@ -76,12 +76,12 @@ def dummy_func(name, folder_path): return name, folder_path # Set the context variable - token = _resource_overwrites.set(overwrites) + token = _binding_overwrites.set(overwrites) try: result = dummy_func("old_name", "old_folder") assert result == ("old_name", "old_folder") finally: - _resource_overwrites.reset(token) + _binding_overwrites.reset(token) def test_infer_bindings_only_name_present(self): """Test that infer_bindings works when only name parameter is present.""" @@ -96,12 +96,12 @@ def dummy_func(name, folder_path=None): return name, folder_path # Set the context variable - token = _resource_overwrites.set(overwrites) + token = _binding_overwrites.set(overwrites) try: result = dummy_func("old_name") assert result == ("new_name", "new_folder") finally: - _resource_overwrites.reset(token) + _binding_overwrites.reset(token) def test_infer_bindings_prefers_specific_folder_path_key(self): """Test that infer_bindings prefers the more specific key with folder_path.""" @@ -123,12 +123,12 @@ def dummy_func(name, folder_path): return name, folder_path # Set the context variable - token = _resource_overwrites.set(overwrites) + token = _binding_overwrites.set(overwrites) try: result = dummy_func("my_bucket", "specific_folder") assert result == ("specific_name", "specific_folder") finally: - _resource_overwrites.reset(token) + _binding_overwrites.reset(token) class TestResourceOverwritesContext: