uipath 2.1.43__py3-none-any.whl → 2.1.45__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,254 @@
1
+ """Python script runtime implementation for executing and managing python scripts."""
2
+
3
+ import importlib.util
4
+ import inspect
5
+ import os
6
+ from dataclasses import asdict, is_dataclass
7
+ from typing import Any, Dict, Type, TypeVar, cast, get_type_hints
8
+
9
+ from pydantic import BaseModel
10
+
11
+ from ._contracts import (
12
+ UiPathErrorCategory,
13
+ UiPathRuntimeError,
14
+ )
15
+
16
+ T = TypeVar("T")
17
+
18
+
19
+ class ScriptExecutor:
20
+ def __init__(self, entrypoint: str):
21
+ self.entrypoint = entrypoint
22
+ self.validate_entrypoint()
23
+
24
+ async def __call__(self, input: Any) -> Any:
25
+ return await self._execute_python_script(input)
26
+
27
+ def validate_entrypoint(self) -> None:
28
+ """Validate runtime inputs."""
29
+ if not self.entrypoint:
30
+ raise UiPathRuntimeError(
31
+ "ENTRYPOINT_MISSING",
32
+ "No entrypoint specified",
33
+ "Please provide a path to a Python script.",
34
+ UiPathErrorCategory.USER,
35
+ )
36
+
37
+ if not os.path.exists(self.entrypoint):
38
+ raise UiPathRuntimeError(
39
+ "ENTRYPOINT_NOT_FOUND",
40
+ "Script not found",
41
+ f"Script not found at path {self.entrypoint}.",
42
+ UiPathErrorCategory.USER,
43
+ )
44
+
45
+ async def _execute_python_script(self, input_data: Any) -> Any:
46
+ """Execute the Python script with the given input."""
47
+ spec = importlib.util.spec_from_file_location("dynamic_module", self.entrypoint)
48
+ if not spec or not spec.loader:
49
+ raise UiPathRuntimeError(
50
+ "IMPORT_ERROR",
51
+ "Module import failed",
52
+ f"Could not load spec for {self.entrypoint}",
53
+ UiPathErrorCategory.USER,
54
+ )
55
+
56
+ module = importlib.util.module_from_spec(spec)
57
+ try:
58
+ spec.loader.exec_module(module)
59
+ except Exception as e:
60
+ raise UiPathRuntimeError(
61
+ "MODULE_EXECUTION_ERROR",
62
+ "Module execution failed",
63
+ f"Error executing module: {str(e)}",
64
+ UiPathErrorCategory.USER,
65
+ ) from e
66
+
67
+ for func_name in ["main", "run", "execute"]:
68
+ if hasattr(module, func_name):
69
+ main_func = getattr(module, func_name)
70
+ sig = inspect.signature(main_func)
71
+ params = list(sig.parameters.values())
72
+
73
+ # Check if the function is asynchronous
74
+ is_async = inspect.iscoroutinefunction(main_func)
75
+
76
+ # Case 1: No parameters
77
+ if not params:
78
+ try:
79
+ result = await main_func() if is_async else main_func()
80
+ return (
81
+ self._convert_from_class(result)
82
+ if result is not None
83
+ else {}
84
+ )
85
+ except Exception as e:
86
+ raise UiPathRuntimeError(
87
+ "FUNCTION_EXECUTION_ERROR",
88
+ f"Error executing {func_name} function",
89
+ f"Error: {str(e)}",
90
+ UiPathErrorCategory.USER,
91
+ ) from e
92
+
93
+ input_param = params[0]
94
+ input_type = input_param.annotation
95
+
96
+ # Case 2: Class, dataclass, or Pydantic model parameter
97
+ if input_type != inspect.Parameter.empty and (
98
+ is_dataclass(input_type)
99
+ or self._is_pydantic_model(input_type)
100
+ or hasattr(input_type, "__annotations__")
101
+ ):
102
+ try:
103
+ valid_type = cast(Type[Any], input_type)
104
+ typed_input = self._convert_to_class(input_data, valid_type)
105
+ result = (
106
+ await main_func(typed_input)
107
+ if is_async
108
+ else main_func(typed_input)
109
+ )
110
+ return (
111
+ self._convert_from_class(result)
112
+ if result is not None
113
+ else {}
114
+ )
115
+ except Exception as e:
116
+ raise UiPathRuntimeError(
117
+ "FUNCTION_EXECUTION_ERROR",
118
+ f"Error executing {func_name} function with typed input",
119
+ f"Error: {str(e)}",
120
+ UiPathErrorCategory.USER,
121
+ ) from e
122
+
123
+ # Case 3: Dict parameter
124
+ else:
125
+ try:
126
+ result = (
127
+ await main_func(input_data)
128
+ if is_async
129
+ else main_func(input_data)
130
+ )
131
+ return (
132
+ self._convert_from_class(result)
133
+ if result is not None
134
+ else {}
135
+ )
136
+ except Exception as e:
137
+ raise UiPathRuntimeError(
138
+ "FUNCTION_EXECUTION_ERROR",
139
+ f"Error executing {func_name} function with dictionary input",
140
+ f"Error: {str(e)}",
141
+ UiPathErrorCategory.USER,
142
+ ) from e
143
+
144
+ raise UiPathRuntimeError(
145
+ "ENTRYPOINT_FUNCTION_MISSING",
146
+ "No entry function found",
147
+ f"No main function (main, run, or execute) found in {self.entrypoint}",
148
+ UiPathErrorCategory.USER,
149
+ )
150
+
151
+ def _convert_to_class(self, data: Dict[str, Any], cls: Type[T]) -> T:
152
+ """Convert a dictionary to either a dataclass, Pydantic model, or regular class instance."""
153
+ # Handle Pydantic models
154
+ try:
155
+ if inspect.isclass(cls) and issubclass(cls, BaseModel):
156
+ return cls.model_validate(data)
157
+ except TypeError:
158
+ # issubclass can raise TypeError if cls is not a class
159
+ pass
160
+
161
+ # Handle dataclasses
162
+ if is_dataclass(cls):
163
+ field_types = get_type_hints(cls)
164
+ converted_data = {}
165
+
166
+ for field_name, field_type in field_types.items():
167
+ if field_name not in data:
168
+ continue
169
+
170
+ value = data[field_name]
171
+ if (
172
+ is_dataclass(field_type)
173
+ or self._is_pydantic_model(field_type)
174
+ or hasattr(field_type, "__annotations__")
175
+ ) and isinstance(value, dict):
176
+ typed_field = cast(Type[Any], field_type)
177
+ value = self._convert_to_class(value, typed_field)
178
+ converted_data[field_name] = value
179
+
180
+ return cls(**converted_data)
181
+
182
+ # Handle regular classes
183
+ else:
184
+ sig = inspect.signature(cls.__init__)
185
+ params = sig.parameters
186
+
187
+ init_args = {}
188
+
189
+ for param_name, param in params.items():
190
+ if param_name == "self":
191
+ continue
192
+
193
+ if param_name in data:
194
+ value = data[param_name]
195
+ param_type = (
196
+ param.annotation
197
+ if param.annotation != inspect.Parameter.empty
198
+ else Any
199
+ )
200
+
201
+ if (
202
+ is_dataclass(param_type)
203
+ or self._is_pydantic_model(param_type)
204
+ or hasattr(param_type, "__annotations__")
205
+ ) and isinstance(value, dict):
206
+ typed_param = cast(Type[Any], param_type)
207
+ value = self._convert_to_class(value, typed_param)
208
+
209
+ init_args[param_name] = value
210
+ elif param.default != inspect.Parameter.empty:
211
+ init_args[param_name] = param.default
212
+
213
+ return cls(**init_args)
214
+
215
+ def _is_pydantic_model(self, cls: Type[Any]) -> bool:
216
+ """Safely check if a class is a Pydantic model."""
217
+ try:
218
+ return inspect.isclass(cls) and issubclass(cls, BaseModel)
219
+ except TypeError:
220
+ # issubclass can raise TypeError if cls is not a class
221
+ return False
222
+
223
+ def _convert_from_class(self, obj: Any) -> Dict[str, Any]:
224
+ """Convert a class instance (dataclass, Pydantic model, or regular) to a dictionary."""
225
+ if obj is None:
226
+ return {}
227
+
228
+ # Handle Pydantic models
229
+ if isinstance(obj, BaseModel):
230
+ return obj.model_dump()
231
+
232
+ # Handle dataclasses
233
+ elif is_dataclass(obj):
234
+ # Make sure obj is an instance, not a class
235
+ if isinstance(obj, type):
236
+ return {}
237
+ return asdict(obj)
238
+
239
+ # Handle regular classes
240
+ elif hasattr(obj, "__dict__"):
241
+ result = {}
242
+ for key, value in obj.__dict__.items():
243
+ # Skip private attributes
244
+ if not key.startswith("_"):
245
+ if (
246
+ isinstance(value, BaseModel)
247
+ or hasattr(value, "__dict__")
248
+ or is_dataclass(value)
249
+ ):
250
+ result[key] = self._convert_from_class(value)
251
+ else:
252
+ result[key] = value
253
+ return result
254
+ return {} if obj is None else {str(type(obj).__name__): str(obj)} # Fallback
uipath/_cli/cli_dev.py CHANGED
@@ -6,7 +6,7 @@ import click
6
6
 
7
7
  from uipath._cli._dev._terminal import UiPathDevTerminal
8
8
  from uipath._cli._runtime._contracts import UiPathRuntimeContext, UiPathRuntimeFactory
9
- from uipath._cli._runtime._runtime import UiPathRuntime
9
+ from uipath._cli._runtime._runtime import UiPathScriptRuntime
10
10
  from uipath._cli._utils._console import ConsoleLogger
11
11
  from uipath._cli._utils._debug import setup_debugging
12
12
  from uipath._cli.cli_init import init # type: ignore[attr-defined]
@@ -53,7 +53,9 @@ def dev(interface: Optional[str], debug: bool, debug_port: int) -> None:
53
53
 
54
54
  try:
55
55
  if interface == "terminal":
56
- runtime_factory = UiPathRuntimeFactory(UiPathRuntime, UiPathRuntimeContext)
56
+ runtime_factory = UiPathRuntimeFactory(
57
+ UiPathScriptRuntime, UiPathRuntimeContext
58
+ )
57
59
  app = UiPathDevTerminal(runtime_factory)
58
60
  asyncio.run(app.run_async())
59
61
  else:
uipath/_cli/cli_eval.py CHANGED
@@ -10,15 +10,16 @@ import click
10
10
  from uipath._cli._evals._runtime import UiPathEvalContext, UiPathEvalRuntime
11
11
  from uipath._cli._runtime._contracts import (
12
12
  UiPathRuntimeContext,
13
- UiPathRuntimeContextBuilder,
14
13
  UiPathRuntimeFactory,
15
14
  )
16
- from uipath._cli._runtime._runtime import UiPathRuntime
15
+ from uipath._cli._runtime._runtime import UiPathScriptRuntime
17
16
  from uipath._cli.middlewares import MiddlewareResult, Middlewares
17
+ from uipath.eval._helpers import auto_discover_entrypoint
18
18
 
19
19
  from .._utils.constants import ENV_JOB_ID
20
20
  from ..telemetry import track
21
21
  from ._utils._console import ConsoleLogger
22
+ from ._utils._eval_set import EvalHelpers
22
23
 
23
24
  console = ConsoleLogger()
24
25
 
@@ -39,42 +40,35 @@ def eval_agent_middleware(
39
40
  no_report: bool = False,
40
41
  **kwargs,
41
42
  ) -> MiddlewareResult:
42
- def generate_eval_context(
43
- runtime_context: UiPathRuntimeContext,
44
- ) -> UiPathEvalContext:
45
- os.makedirs("evals/results", exist_ok=True)
46
- timestamp = datetime.now(timezone.utc).strftime("%M-%H-%d-%m-%Y")
47
- base_context = UiPathRuntimeContextBuilder().with_defaults().build()
48
- # TODO: the name should include the eval_set name. those files should not be commited to SW
49
- base_context.execution_output_file = (
50
- f"evals/results/{timestamp}.json"
51
- if not os.getenv("UIPATH_JOB_KEY")
52
- else None
53
- )
54
- return UiPathEvalContext(
55
- runtime_context=runtime_context,
56
- no_report=no_report,
57
- workers=workers,
58
- eval_set=eval_set,
59
- eval_ids=eval_ids,
60
- **kwargs,
61
- **base_context.model_dump(),
62
- )
43
+ """Middleware to run an evaluation set against the agent."""
44
+ timestamp = datetime.now(timezone.utc).strftime("%M-%H-%d-%m-%Y")
45
+
46
+ eval_context = UiPathEvalContext.with_defaults()
47
+ eval_context.no_report = no_report
48
+ eval_context.workers = workers
49
+ eval_context.eval_set = eval_set or EvalHelpers.auto_discover_eval_set()
50
+ eval_context.eval_ids = eval_ids
51
+ eval_context.execution_output_file = (
52
+ f"evals/results/{timestamp}.json" if not os.getenv("UIPATH_JOB_KEY") else None
53
+ )
54
+
55
+ runtime_entrypoint = entrypoint or auto_discover_entrypoint()
56
+
57
+ def generate_runtime_context(**context_kwargs) -> UiPathRuntimeContext:
58
+ runtime_context = UiPathRuntimeContext.with_defaults(**context_kwargs)
59
+ runtime_context.entrypoint = runtime_entrypoint
60
+ return runtime_context
63
61
 
64
62
  try:
65
- runtime_factory = UiPathRuntimeFactory(UiPathRuntime, UiPathRuntimeContext)
66
- context = (
67
- UiPathRuntimeContextBuilder()
68
- .with_defaults(**kwargs)
69
- .with_entrypoint(entrypoint)
70
- .with_entrypoint(entrypoint)
71
- .mark_eval_run()
72
- .build()
63
+ runtime_factory = UiPathRuntimeFactory(
64
+ UiPathScriptRuntime,
65
+ UiPathRuntimeContext,
66
+ context_generator=generate_runtime_context,
73
67
  )
74
68
 
75
69
  async def execute():
76
- async with UiPathEvalRuntime.from__eval_context(
77
- factory=runtime_factory, context=generate_eval_context(context)
70
+ async with UiPathEvalRuntime.from_eval_context(
71
+ factory=runtime_factory, context=eval_context
78
72
  ) as eval_runtime:
79
73
  await eval_runtime.execute()
80
74
 
uipath/_cli/cli_run.py CHANGED
@@ -1,9 +1,8 @@
1
1
  # type: ignore
2
2
  import asyncio
3
3
  import os
4
- import traceback
5
4
  from os import environ as env
6
- from typing import Optional, Tuple
5
+ from typing import Optional
7
6
 
8
7
  import click
9
8
 
@@ -15,138 +14,16 @@ from .._utils.constants import (
15
14
  from ..telemetry import track
16
15
  from ._runtime._contracts import (
17
16
  UiPathRuntimeContext,
18
- UiPathRuntimeContextBuilder,
19
17
  UiPathRuntimeError,
20
18
  UiPathRuntimeFactory,
21
19
  )
22
- from ._runtime._runtime import UiPathRuntime
20
+ from ._runtime._runtime import UiPathScriptRuntime
23
21
  from ._utils._console import ConsoleLogger
24
- from .middlewares import MiddlewareResult, Middlewares
22
+ from .middlewares import Middlewares
25
23
 
26
24
  console = ConsoleLogger()
27
25
 
28
26
 
29
- def python_run_middleware(
30
- entrypoint: Optional[str],
31
- input: Optional[str],
32
- resume: bool,
33
- **kwargs,
34
- ) -> MiddlewareResult:
35
- """Middleware to handle Python script execution.
36
-
37
- Args:
38
- entrypoint: Path to the Python script to execute
39
- input: JSON string with input data
40
- resume: Flag indicating if this is a resume execution
41
- debug: Enable debugging with debugpy
42
- debug_port: Port for debug server (default: 5678)
43
-
44
- Returns:
45
- MiddlewareResult with execution status and messages
46
- """
47
- if not entrypoint:
48
- return MiddlewareResult(
49
- should_continue=False,
50
- error_message="""No entrypoint specified. Please provide a path to a Python script.
51
- Usage: `uipath run <entrypoint_path> <input_arguments> [-f <input_json_file_path>]`""",
52
- )
53
-
54
- if not os.path.exists(entrypoint):
55
- return MiddlewareResult(
56
- should_continue=False,
57
- error_message=f"""Script not found at path {entrypoint}.
58
- Usage: `uipath run <entrypoint_path> <input_arguments> [-f <input_json_file_path>]`""",
59
- )
60
-
61
- try:
62
- runtime_factory = UiPathRuntimeFactory(UiPathRuntime, UiPathRuntimeContext)
63
- context = (
64
- UiPathRuntimeContextBuilder()
65
- .with_defaults(**kwargs)
66
- .with_entrypoint(entrypoint)
67
- .with_input(input)
68
- .with_resume(resume)
69
- .build()
70
- )
71
-
72
- asyncio.run(runtime_factory.execute(context))
73
-
74
- return MiddlewareResult(should_continue=False)
75
-
76
- except UiPathRuntimeError as e:
77
- return MiddlewareResult(
78
- should_continue=False,
79
- error_message=f"Error: {e.error_info.title} - {e.error_info.detail}",
80
- should_include_stacktrace=False,
81
- )
82
- except Exception as e:
83
- # Handle unexpected errors
84
- return MiddlewareResult(
85
- should_continue=False,
86
- error_message=f"Error: Unexpected error occurred - {str(e)}",
87
- should_include_stacktrace=True,
88
- )
89
-
90
-
91
- def run_core(
92
- entrypoint: Optional[str],
93
- resume: bool,
94
- input: Optional[str] = None,
95
- input_file: Optional[str] = None,
96
- execution_output_file: Optional[str] = None,
97
- logs_file: Optional[str] = None,
98
- **kwargs,
99
- ) -> Tuple[bool, Optional[str], Optional[str]]:
100
- """Core execution logic that can be called programmatically.
101
-
102
- Args:
103
- entrypoint: Path to the Python script to execute
104
- input: JSON string with input data
105
- resume: Flag indicating if this is a resume execution
106
- input_file: Path to input JSON file
107
- execution_output_file: Path to execution output file
108
- logs_file: Path where execution output will be written
109
- **kwargs: Additional arguments to be forwarded to the middleware
110
-
111
- Returns:
112
- Tuple containing:
113
- - success: True if execution was successful
114
- - error_message: Error message if any
115
- - info_message: Info message if any
116
- """
117
- # Process through middleware chain
118
- result = Middlewares.next(
119
- "run",
120
- entrypoint,
121
- input,
122
- resume,
123
- input_file=input_file,
124
- execution_output_file=execution_output_file,
125
- logs_file=logs_file,
126
- **kwargs,
127
- )
128
-
129
- if result.should_continue:
130
- result = python_run_middleware(
131
- entrypoint=entrypoint,
132
- input=input,
133
- resume=resume,
134
- input_file=input_file,
135
- execution_output_file=execution_output_file,
136
- logs_file=logs_file,
137
- **kwargs,
138
- )
139
-
140
- if result.should_continue:
141
- return False, "Could not process the request with any available handler.", None
142
-
143
- return (
144
- not bool(result.error_message),
145
- result.error_message,
146
- result.info_message,
147
- )
148
-
149
-
150
27
  @click.command()
151
28
  @click.argument("entrypoint", required=False)
152
29
  @click.argument("input", required=False, default="{}")
@@ -198,27 +75,53 @@ def run(
198
75
  if not setup_debugging(debug, debug_port):
199
76
  console.error(f"Failed to start debug server on port {debug_port}")
200
77
 
201
- success, error_message, info_message = run_core(
202
- entrypoint=entrypoint,
203
- input=input,
204
- resume=resume,
78
+ result = Middlewares.next(
79
+ "run",
80
+ entrypoint,
81
+ input,
82
+ resume,
205
83
  input_file=input_file,
206
84
  execution_output_file=output_file,
207
85
  debug=debug,
208
86
  debug_port=debug_port,
209
87
  )
210
88
 
211
- if error_message:
212
- console.error(error_message, include_traceback=True)
213
- if not success: # If there was an error and execution failed
214
- console.error(traceback.format_exc())
215
- click.get_current_context().exit(1)
216
-
217
- if info_message:
218
- console.info(info_message)
89
+ if result.error_message:
90
+ console.error(result.error_message)
219
91
 
220
- if success:
221
- console.success("Successful execution.")
92
+ if result.should_continue:
93
+ if not entrypoint:
94
+ console.error("""No entrypoint specified. Please provide a path to a Python script.
95
+ Usage: `uipath run <entrypoint_path> <input_arguments> [-f <input_json_file_path>]`""")
96
+
97
+ if not os.path.exists(entrypoint):
98
+ console.error(f"""Script not found at path {entrypoint}.
99
+ Usage: `uipath run <entrypoint_path> <input_arguments> [-f <input_json_file_path>]`""")
100
+
101
+ try:
102
+ runtime_factory = UiPathRuntimeFactory(
103
+ UiPathScriptRuntime, UiPathRuntimeContext
104
+ )
105
+ context = UiPathRuntimeContext.with_defaults(
106
+ entrypoint=entrypoint,
107
+ input=input,
108
+ input_file=input_file,
109
+ resume=resume,
110
+ execution_output_file=output_file,
111
+ debug=debug,
112
+ )
113
+
114
+ asyncio.run(runtime_factory.execute(context))
115
+
116
+ except UiPathRuntimeError as e:
117
+ console.error(f"{e.error_info.title} - {e.error_info.detail}")
118
+ except Exception as e:
119
+ # Handle unexpected errors
120
+ console.error(
121
+ f"Error: Unexpected error occurred - {str(e)}", include_traceback=True
122
+ )
123
+
124
+ console.success("Successful execution.")
222
125
 
223
126
 
224
127
  if __name__ == "__main__":
@@ -7,7 +7,7 @@ from .._config import Config
7
7
  from .._execution_context import ExecutionContext
8
8
  from .._folder_context import FolderContext
9
9
  from .._utils import Endpoint, RequestSpec, header_folder, infer_bindings
10
- from .._utils.constants import ENV_JOB_ID, HEADER_JOB_KEY
10
+ from .._utils.constants import ENV_JOB_KEY, HEADER_JOB_KEY
11
11
  from ..models.job import Job
12
12
  from ..tracing._traced import traced
13
13
  from . import AttachmentsService
@@ -241,7 +241,7 @@ class ProcessesService(FolderContext, BaseService):
241
241
  **header_folder(folder_key, folder_path),
242
242
  },
243
243
  )
244
- job_key = os.environ.get(ENV_JOB_ID, None)
244
+ job_key = os.environ.get(ENV_JOB_KEY, None)
245
245
  if job_key:
246
246
  request_scope.headers[HEADER_JOB_KEY] = job_key
247
247
  return request_scope
uipath/models/errors.py CHANGED
@@ -1,7 +1,7 @@
1
1
  class BaseUrlMissingError(Exception):
2
2
  def __init__(
3
3
  self,
4
- message="Authentication required. Please run \033[1muipath auth\033[22m.",
4
+ message="Authentication required. Please run \033[1muipath auth\033[22m or set the base URL via the UIPATH_URL environment variable.",
5
5
  ):
6
6
  self.message = message
7
7
  super().__init__(self.message)
@@ -10,7 +10,7 @@ class BaseUrlMissingError(Exception):
10
10
  class SecretMissingError(Exception):
11
11
  def __init__(
12
12
  self,
13
- message="Authentication required. Please run \033[1muipath auth\033[22m.",
13
+ message="Authentication required. Please run \033[1muipath auth\033[22m or set the UIPATH_ACCESS_TOKEN environment variable to a valid access token.",
14
14
  ):
15
15
  self.message = message
16
16
  super().__init__(self.message)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: uipath
3
- Version: 2.1.43
3
+ Version: 2.1.45
4
4
  Summary: Python SDK and CLI for UiPath Platform, enabling programmatic interaction with automation services, process management, and deployment tools.
5
5
  Project-URL: Homepage, https://uipath.com
6
6
  Project-URL: Repository, https://github.com/UiPath/uipath-python