From 77a5cb626f7279f5856e1fc34a9ada16a543bf43 Mon Sep 17 00:00:00 2001 From: radu-mocanu Date: Thu, 8 Jan 2026 10:15:05 +0200 Subject: [PATCH] fix: trace resource overrides --- pyproject.toml | 2 +- src/uipath/_utils/__init__.py | 3 +- src/uipath/_utils/_bindings.py | 53 +++++++---- .../platform/action_center/_tasks_service.py | 8 +- .../connections/_connections_service.py | 4 +- .../_context_grounding_service.py | 36 +++---- .../platform/orchestrator/_assets_service.py | 8 +- .../platform/orchestrator/_buckets_service.py | 32 +++---- .../platform/orchestrator/_jobs_service.py | 4 +- .../orchestrator/_processes_service.py | 4 +- .../test_resource_overrides.py | 94 +++++++++++++++++++ uv.lock | 2 +- 12 files changed, 181 insertions(+), 69 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index fe189713c..ea5a6854a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath" -version = "2.4.3" +version = "2.4.4" description = "Python SDK and CLI for UiPath Platform, enabling programmatic interaction with automation services, process management, and deployment tools." readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.11" diff --git a/src/uipath/_utils/__init__.py b/src/uipath/_utils/__init__.py index dd098c0da..934dd129a 100644 --- a/src/uipath/_utils/__init__.py +++ b/src/uipath/_utils/__init__.py @@ -1,4 +1,4 @@ -from ._bindings import get_inferred_bindings_names, resource_override +from ._bindings import resource_override from ._endpoint import Endpoint from ._logs import setup_logging from ._request_override import header_folder @@ -12,7 +12,6 @@ "setup_logging", "RequestSpec", "header_folder", - "get_inferred_bindings_names", "resource_override", "header_user_agent", "user_agent_value", diff --git a/src/uipath/_utils/_bindings.py b/src/uipath/_utils/_bindings.py index 6816260f3..b843a0671 100644 --- a/src/uipath/_utils/_bindings.py +++ b/src/uipath/_utils/_bindings.py @@ -139,11 +139,29 @@ def resource_override( resource_identifier: str = "name", folder_identifier: str = "folder_path", ) -> Callable[..., Any]: + """Decorator for applying resource overrides for an overridable resource. + + It checks the current ContextVar to identify the requested overrides and, if any key matches, it invokes the decorated function + with the extracted resource and folder identifiers. + + Args: + 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") + + Returns: + Decorated function that receives overridden resource identifiers when applicable + + Note: + Must be applied BEFORE the @traced decorator to ensure proper execution order. + """ + def decorator(func: Callable[..., Any]): - @functools.wraps(func) - def wrapper(*args, **kwargs): + sig = inspect.signature(func) + + def process_args(args, kwargs) -> dict[str, Any]: + """Process arguments and apply resource overrides if applicable.""" # convert both args and kwargs to single dict - sig = inspect.signature(func) bound = sig.bind_partial(*args, **kwargs) bound.apply_defaults() all_args = dict(bound.arguments) @@ -177,22 +195,23 @@ def wrapper(*args, **kwargs): matched_overwrite.folder_identifier ) - return func(**all_args) + return all_args - wrapper._should_infer_bindings = True # type: ignore[attr-defined] # probably a better way to do this - wrapper._infer_bindings_mappings = { # type: ignore[attr-defined] # probably a better way to do this - "name": resource_identifier, - "folder_path": folder_identifier, - } - return wrapper + if inspect.iscoroutinefunction(func): - return decorator + @functools.wraps(func) + async def async_wrapper(*args, **kwargs): + all_args = process_args(args, kwargs) + return await func(**all_args) + return async_wrapper + else: -def get_inferred_bindings_names(cls: T): - inferred_bindings = {} - for name, method in inspect.getmembers(cls, inspect.isfunction): - if hasattr(method, "_should_infer_bindings") and method._should_infer_bindings: - inferred_bindings[name] = method._infer_bindings_mappings # type: ignore # probably a better way to do this + @functools.wraps(func) + def wrapper(*args, **kwargs): + all_args = process_args(args, kwargs) + return func(**all_args) - return inferred_bindings + return wrapper + + return decorator diff --git a/src/uipath/platform/action_center/_tasks_service.py b/src/uipath/platform/action_center/_tasks_service.py index 9e79db6cd..454d82031 100644 --- a/src/uipath/platform/action_center/_tasks_service.py +++ b/src/uipath/platform/action_center/_tasks_service.py @@ -166,12 +166,12 @@ def __init__( ) -> None: super().__init__(config=config, execution_context=execution_context) - @traced(name="tasks_create", run_type="uipath") @resource_override( resource_type="app", resource_identifier="app_name", folder_identifier="app_folder_path", ) + @traced(name="tasks_create", run_type="uipath") async def create_async( self, title: str, @@ -234,12 +234,12 @@ async def create_async( ) return Task.model_validate(json_response) - @traced(name="tasks_create", run_type="uipath") @resource_override( resource_type="app", resource_identifier="app_name", folder_identifier="app_folder_path", ) + @traced(name="tasks_create", run_type="uipath") def create( self, title: str, @@ -302,12 +302,12 @@ def create( ) return Task.model_validate(json_response) - @traced(name="tasks_retrieve", run_type="uipath") @resource_override( resource_type="app", resource_identifier="app_name", folder_identifier="app_folder_path", ) + @traced(name="tasks_retrieve", run_type="uipath") def retrieve( self, action_key: str, @@ -336,12 +336,12 @@ def retrieve( return Task.model_validate(response.json()) - @traced(name="tasks_retrieve", run_type="uipath") @resource_override( resource_type="app", resource_identifier="app_name", folder_identifier="app_folder_path", ) + @traced(name="tasks_retrieve", run_type="uipath") async def retrieve_async( self, action_key: str, diff --git a/src/uipath/platform/connections/_connections_service.py b/src/uipath/platform/connections/_connections_service.py index cff8fbd19..e0b954dda 100644 --- a/src/uipath/platform/connections/_connections_service.py +++ b/src/uipath/platform/connections/_connections_service.py @@ -37,12 +37,12 @@ def __init__( super().__init__(config=config, execution_context=execution_context) self._folders_service = folders_service + @resource_override("connection", resource_identifier="key") @traced( name="connections_retrieve", run_type="uipath", hide_output=True, ) - @resource_override("connection", resource_identifier="key") def retrieve(self, key: str) -> Connection: """Retrieve connection details by its key. @@ -248,12 +248,12 @@ async def list_async( return self._parse_and_validate_list_response(response) + @resource_override("connection", resource_identifier="key") @traced( name="connections_retrieve", run_type="uipath", hide_output=True, ) - @resource_override("connection", resource_identifier="key") async def retrieve_async(self, key: str) -> Connection: """Asynchronously retrieve connection details by its key. diff --git a/src/uipath/platform/context_grounding/_context_grounding_service.py b/src/uipath/platform/context_grounding/_context_grounding_service.py index 8d58cfef8..b7152da61 100644 --- a/src/uipath/platform/context_grounding/_context_grounding_service.py +++ b/src/uipath/platform/context_grounding/_context_grounding_service.py @@ -74,8 +74,8 @@ def __init__( super().__init__(config=config, execution_context=execution_context) # 2.3.0 prefix trace name with contextgrounding - @traced(name="add_to_index", run_type="uipath") @resource_override(resource_type="index") + @traced(name="add_to_index", run_type="uipath") def add_to_index( self, name: str, @@ -130,8 +130,8 @@ def add_to_index( self.ingest_data(index, folder_key=folder_key, folder_path=folder_path) # 2.3.0 prefix trace name with contextgrounding - @traced(name="add_to_index", run_type="uipath") @resource_override(resource_type="index") + @traced(name="add_to_index", run_type="uipath") async def add_to_index_async( self, name: str, @@ -189,8 +189,8 @@ async def add_to_index_async( index, folder_key=folder_key, folder_path=folder_path ) - @traced(name="contextgrounding_retrieve", run_type="uipath") @resource_override(resource_type="index") + @traced(name="contextgrounding_retrieve", run_type="uipath") def retrieve( self, name: str, @@ -231,8 +231,8 @@ def retrieve( except StopIteration as e: raise Exception("ContextGroundingIndex not found") from e - @traced(name="contextgrounding_retrieve", run_type="uipath") @resource_override(resource_type="index") + @traced(name="contextgrounding_retrieve", run_type="uipath") async def retrieve_async( self, name: str, @@ -343,8 +343,8 @@ async def retrieve_by_id_async( return response.json() - @traced(name="contextgrounding_create_index", run_type="uipath") @resource_override(resource_type="index") + @traced(name="contextgrounding_create_index", run_type="uipath") def create_index( self, name: str, @@ -398,8 +398,8 @@ def create_index( return ContextGroundingIndex.model_validate(response.json()) - @traced(name="contextgrounding_create_index", run_type="uipath") @resource_override(resource_type="index") + @traced(name="contextgrounding_create_index", run_type="uipath") async def create_index_async( self, name: str, @@ -453,8 +453,8 @@ async def create_index_async( return ContextGroundingIndex.model_validate(response.json()) - @traced(name="contextgrounding_retrieve_deep_rag", run_type="uipath") @resource_override(resource_type="index", resource_identifier="index_name") + @traced(name="contextgrounding_retrieve_deep_rag", run_type="uipath") def retrieve_deep_rag( self, id: str, @@ -482,8 +482,8 @@ def retrieve_deep_rag( ) return DeepRagResponse.model_validate(response.json()) - @traced(name="contextgrounding_retrieve_deep_rag_async", run_type="uipath") @resource_override(resource_type="index", resource_identifier="index_name") + @traced(name="contextgrounding_retrieve_deep_rag_async", run_type="uipath") async def retrieve_deep_rag_async( self, id: str, @@ -513,8 +513,8 @@ async def retrieve_deep_rag_async( return DeepRagResponse.model_validate(response.json()) - @traced(name="contextgrounding_start_batch_transform", run_type="uipath") @resource_override(resource_type="index", resource_identifier="index_name") + @traced(name="contextgrounding_start_batch_transform", run_type="uipath") def start_batch_transform( self, name: str, @@ -571,8 +571,8 @@ def start_batch_transform( ) return BatchTransformCreationResponse.model_validate(response.json()) - @traced(name="contextgrounding_start_batch_transform_async", run_type="uipath") @resource_override(resource_type="index", resource_identifier="index_name") + @traced(name="contextgrounding_start_batch_transform_async", run_type="uipath") async def start_batch_transform_async( self, name: str, @@ -629,8 +629,8 @@ async def start_batch_transform_async( ) return BatchTransformCreationResponse.model_validate(response.json()) - @traced(name="contextgrounding_retrieve_batch_transform", run_type="uipath") @resource_override(resource_type="index", resource_identifier="index_name") + @traced(name="contextgrounding_retrieve_batch_transform", run_type="uipath") def retrieve_batch_transform( self, id: str, @@ -655,8 +655,8 @@ def retrieve_batch_transform( ) return BatchTransformResponse.model_validate(response.json()) - @traced(name="contextgrounding_retrieve_batch_transform_async", run_type="uipath") @resource_override(resource_type="index", resource_identifier="index_name") + @traced(name="contextgrounding_retrieve_batch_transform_async", run_type="uipath") async def retrieve_batch_transform_async( self, id: str, @@ -681,8 +681,8 @@ async def retrieve_batch_transform_async( ) return BatchTransformResponse.model_validate(response.json()) - @traced(name="contextgrounding_download_batch_transform_result", run_type="uipath") @resource_override(resource_type="index", resource_identifier="index_name") + @traced(name="contextgrounding_download_batch_transform_result", run_type="uipath") def download_batch_transform_result( self, id: str, @@ -727,10 +727,10 @@ 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") @traced( name="contextgrounding_download_batch_transform_result_async", run_type="uipath" ) - @resource_override(resource_type="index", resource_identifier="index_name") async def download_batch_transform_result_async( self, id: str, @@ -777,8 +777,8 @@ async def download_batch_transform_result_async( with open(destination_path, "wb") as file: file.write(file_content) - @traced(name="contextgrounding_start_deep_rag", run_type="uipath") @resource_override(resource_type="index", resource_identifier="index_name") + @traced(name="contextgrounding_start_deep_rag", run_type="uipath") def start_deep_rag( self, name: str, @@ -829,8 +829,8 @@ def start_deep_rag( return DeepRagCreationResponse.model_validate(response.json()) - @traced(name="contextgrounding_start_deep_rag_async", run_type="uipath") @resource_override(resource_type="index", resource_identifier="index_name") + @traced(name="contextgrounding_start_deep_rag_async", run_type="uipath") async def start_deep_rag_async( self, name: str, @@ -882,8 +882,8 @@ async def start_deep_rag_async( return DeepRagCreationResponse.model_validate(response.json()) - @traced(name="contextgrounding_search", run_type="uipath") @resource_override(resource_type="index") + @traced(name="contextgrounding_search", run_type="uipath") def search( self, name: str, @@ -931,8 +931,8 @@ def search( response.json() ) - @traced(name="contextgrounding_search", run_type="uipath") @resource_override(resource_type="index") + @traced(name="contextgrounding_search", run_type="uipath") async def search_async( self, name: str, diff --git a/src/uipath/platform/orchestrator/_assets_service.py b/src/uipath/platform/orchestrator/_assets_service.py index c9eeee660..177c2c846 100644 --- a/src/uipath/platform/orchestrator/_assets_service.py +++ b/src/uipath/platform/orchestrator/_assets_service.py @@ -21,10 +21,10 @@ def __init__( super().__init__(config=config, execution_context=execution_context) self._base_url = "assets" + @resource_override(resource_type="asset") @traced( name="assets_retrieve", run_type="uipath", hide_input=True, hide_output=True ) - @resource_override(resource_type="asset") def retrieve( self, name: str, @@ -77,10 +77,10 @@ def retrieve( else: return Asset.model_validate(response.json()["value"][0]) + @resource_override(resource_type="asset") @traced( name="assets_retrieve", run_type="uipath", hide_input=True, hide_output=True ) - @resource_override(resource_type="asset") async def retrieve_async( self, name: str, @@ -124,10 +124,10 @@ async def retrieve_async( else: return Asset.model_validate(response.json()["value"][0]) + @resource_override(resource_type="asset") @traced( name="assets_credential", run_type="uipath", hide_input=True, hide_output=True ) - @resource_override(resource_type="asset") def retrieve_credential( self, name: str, @@ -179,10 +179,10 @@ def retrieve_credential( return user_asset.credential_password + @resource_override(resource_type="asset") @traced( name="assets_credential", run_type="uipath", hide_input=True, hide_output=True ) - @resource_override(resource_type="asset") async def retrieve_credential_async( self, name: str, diff --git a/src/uipath/platform/orchestrator/_buckets_service.py b/src/uipath/platform/orchestrator/_buckets_service.py index e5db2edef..ebb6bcbe3 100644 --- a/src/uipath/platform/orchestrator/_buckets_service.py +++ b/src/uipath/platform/orchestrator/_buckets_service.py @@ -329,8 +329,8 @@ async def create_async( bucket = Bucket.model_validate(response) return bucket - @traced(name="buckets_delete", run_type="uipath") @resource_override(resource_type="bucket") + @traced(name="buckets_delete", run_type="uipath") def delete( self, *, @@ -364,8 +364,8 @@ def delete( headers={**self.folder_headers}, ) - @traced(name="buckets_delete", run_type="uipath") @resource_override(resource_type="bucket") + @traced(name="buckets_delete", run_type="uipath") async def delete_async( self, *, @@ -385,8 +385,8 @@ async def delete_async( headers={**self.folder_headers}, ) - @traced(name="buckets_download", run_type="uipath") @resource_override(resource_type="bucket") + @traced(name="buckets_download", run_type="uipath") def download( self, *, @@ -440,8 +440,8 @@ def download( file_content = self.custom_client.get(read_uri, headers=headers).content file.write(file_content) - @traced(name="buckets_download", run_type="uipath") @resource_override(resource_type="bucket") + @traced(name="buckets_download", run_type="uipath") async def download_async( self, *, @@ -501,8 +501,8 @@ async def download_async( await asyncio.to_thread(Path(destination_path).write_bytes, file_content) - @traced(name="buckets_upload", run_type="uipath") @resource_override(resource_type="bucket") + @traced(name="buckets_upload", run_type="uipath") def upload( self, *, @@ -593,8 +593,8 @@ def upload( write_uri, headers=headers, content=file_content ) - @traced(name="buckets_upload", run_type="uipath") @resource_override(resource_type="bucket") + @traced(name="buckets_upload", run_type="uipath") async def upload_async( self, *, @@ -690,8 +690,8 @@ async def upload_async( write_uri, headers=headers, content=file_content ) - @traced(name="buckets_retrieve", run_type="uipath") @resource_override(resource_type="bucket") + @traced(name="buckets_retrieve", run_type="uipath") def retrieve( self, *, @@ -764,8 +764,8 @@ def retrieve( bucket = Bucket.model_validate(bucket_data) return bucket - @traced(name="buckets_retrieve", run_type="uipath") @resource_override(resource_type="bucket") + @traced(name="buckets_retrieve", run_type="uipath") async def retrieve_async( self, *, @@ -842,8 +842,8 @@ async def retrieve_async( bucket = Bucket.model_validate(bucket_data) return bucket - @traced(name="buckets_list_files", run_type="uipath") @resource_override(resource_type="bucket") + @traced(name="buckets_list_files", run_type="uipath") def list_files( self, *, @@ -955,8 +955,8 @@ def list_files( has_more=next_token is not None, ) - @traced(name="buckets_list_files", run_type="uipath") @resource_override(resource_type="bucket") + @traced(name="buckets_list_files", run_type="uipath") async def list_files_async( self, *, @@ -1042,8 +1042,8 @@ async def list_files_async( has_more=next_token is not None, ) - @traced(name="buckets_exists_file", run_type="uipath") @resource_override(resource_type="bucket") + @traced(name="buckets_exists_file", run_type="uipath") def exists_file( self, *, @@ -1125,8 +1125,8 @@ def exists_file( return False - @traced(name="buckets_exists_file", run_type="uipath") @resource_override(resource_type="bucket") + @traced(name="buckets_exists_file", run_type="uipath") async def exists_file_async( self, *, @@ -1199,8 +1199,8 @@ async def exists_file_async( return False - @traced(name="buckets_delete_file", run_type="uipath") @resource_override(resource_type="bucket") + @traced(name="buckets_delete_file", run_type="uipath") def delete_file( self, *, @@ -1235,8 +1235,8 @@ def delete_file( headers=spec.headers, ) - @traced(name="buckets_delete_file", run_type="uipath") @resource_override(resource_type="bucket") + @traced(name="buckets_delete_file", run_type="uipath") async def delete_file_async( self, *, @@ -1271,8 +1271,8 @@ async def delete_file_async( headers=spec.headers, ) - @traced(name="buckets_get_files", run_type="uipath") @resource_override(resource_type="bucket") + @traced(name="buckets_get_files", run_type="uipath") def get_files( self, *, @@ -1436,8 +1436,8 @@ def get_files( top=top, ) - @traced(name="buckets_get_files", run_type="uipath") @resource_override(resource_type="bucket") + @traced(name="buckets_get_files", run_type="uipath") async def get_files_async( self, *, diff --git a/src/uipath/platform/orchestrator/_jobs_service.py b/src/uipath/platform/orchestrator/_jobs_service.py index e5d244387..a89758673 100644 --- a/src/uipath/platform/orchestrator/_jobs_service.py +++ b/src/uipath/platform/orchestrator/_jobs_service.py @@ -471,8 +471,8 @@ async def exists_async( except LookupError: return False - @traced(name="jobs_retrieve", run_type="uipath") @resource_override(resource_type="process", resource_identifier="process_name") + @traced(name="jobs_retrieve", run_type="uipath") def retrieve( self, job_key: str, @@ -518,8 +518,8 @@ def retrieve( raise LookupError(f"Job with key '{job_key}' not found") from e raise - @traced(name="jobs_retrieve_async", run_type="uipath") @resource_override(resource_type="process", resource_identifier="process_name") + @traced(name="jobs_retrieve_async", run_type="uipath") async def retrieve_async( self, job_key: str, diff --git a/src/uipath/platform/orchestrator/_processes_service.py b/src/uipath/platform/orchestrator/_processes_service.py index 08806b524..1632f29b9 100644 --- a/src/uipath/platform/orchestrator/_processes_service.py +++ b/src/uipath/platform/orchestrator/_processes_service.py @@ -28,8 +28,8 @@ def __init__( self._attachments_service = attachment_service super().__init__(config=config, execution_context=execution_context) - @traced(name="processes_invoke", run_type="uipath") @resource_override(resource_type="process") + @traced(name="processes_invoke", run_type="uipath") def invoke( self, name: str, @@ -92,8 +92,8 @@ def invoke( return Job.model_validate(response.json()["value"][0]) - @traced(name="processes_invoke", run_type="uipath") @resource_override(resource_type="process") + @traced(name="processes_invoke", run_type="uipath") async def invoke_async( self, name: str, diff --git a/tests/resource_overrides/test_resource_overrides.py b/tests/resource_overrides/test_resource_overrides.py index fc7917700..5aa0c4604 100644 --- a/tests/resource_overrides/test_resource_overrides.py +++ b/tests/resource_overrides/test_resource_overrides.py @@ -7,6 +7,7 @@ import pytest from click.testing import CliRunner +from opentelemetry.sdk.trace.export import SpanExporter from pytest_httpx import HTTPXMock from uipath._cli import cli @@ -49,6 +50,44 @@ def _make_config(script_path: str = "main.py", function_name: str = "main"): return _make_config +@pytest.fixture() +def tracer_provider_with_memory_exporter(): + """Create a TracerProvider with in-memory span exporter.""" + from opentelemetry import trace + from opentelemetry.sdk.trace import TracerProvider + from opentelemetry.sdk.trace.export import SimpleSpanProcessor, SpanExportResult + + original_provider = trace.get_tracer_provider() + captured_spans = [] + + class InMemorySpanExporter(SpanExporter): + def export(self, spans): + captured_spans.extend(spans) + return SpanExportResult.SUCCESS + + def force_flush(self, timeout_millis=30000): + return True + + def shutdown(self): + pass + + provider = TracerProvider() + span_processor = SimpleSpanProcessor(InMemorySpanExporter()) + provider.add_span_processor(span_processor) + + trace._TRACER_PROVIDER_SET_ONCE._done = False + trace._TRACER_PROVIDER = None + trace.set_tracer_provider(provider) + + yield provider, captured_spans + + span_processor.shutdown() + provider.shutdown() + trace._TRACER_PROVIDER_SET_ONCE._done = False + trace._TRACER_PROVIDER = None + trace.set_tracer_provider(original_provider) + + class TestResourceOverrides: """Tests for resource override functionality.""" @@ -335,3 +374,58 @@ async def mock_get_resource_overwrites(): os.chdir(current_dir_copy) self._assert(result, httpx_mock) + + +class TestResourceOverrideWithTracing: + """Tests for resource_override decorator integration with tracing.""" + + @pytest.mark.anyio + async def test_traced_span_shows_overridden_resource_name( + self, tracer_provider_with_memory_exporter + ): + """Verify that spans show the overridden resource name, not the original value.""" + + from uipath._utils import resource_override + from uipath._utils._bindings import ( + GenericResourceOverwrite, + ResourceOverwritesContext, + ) + from uipath.tracing import traced + + provider, captured_spans = tracer_provider_with_memory_exporter + + @resource_override(resource_type="bucket") + @traced(name="test_bucket_operation", run_type="uipath") + async def retrieve_resource(name: str, folder_path: str): + return {"resource_name": name, "folder": folder_path} + + async def get_overwrites(): + return { + "bucket.original_bucket.original_folder": GenericResourceOverwrite( + resource_type="bucket", + name="overridden_resource", + folder_path="overridden_folder", + ) + } + + async with ResourceOverwritesContext(get_overwrites): + result = await retrieve_resource("original_bucket", "original_folder") + + provider.force_flush() + + assert result["resource_name"] == "overridden_resource" + assert result["folder"] == "overridden_folder" + + assert len(captured_spans) > 0 + + span = captured_spans[-1] + attrs = dict(span.attributes) if span.attributes else {} + + assert span.name == "test_bucket_operation" + + input_attrs_dict = json.loads((attrs["input.value"])) + output_attrs_dict = json.loads((attrs["output.value"])) + assert input_attrs_dict["name"] == "overridden_resource" + assert input_attrs_dict["folder_path"] == "overridden_folder" + assert output_attrs_dict["resource_name"] == "overridden_resource" + assert output_attrs_dict["folder"] == "overridden_folder" diff --git a/uv.lock b/uv.lock index 73dfaffb8..8e85506a1 100644 --- a/uv.lock +++ b/uv.lock @@ -2477,7 +2477,7 @@ wheels = [ [[package]] name = "uipath" -version = "2.4.3" +version = "2.4.4" source = { editable = "." } dependencies = [ { name = "click" },