Skip to content

Commit df8936e

Browse files
authored
Merge pull request #26 from UiPath/feat/add_runtime_factory_registry
feat: add runtime factory registry
2 parents 5546916 + 2413c89 commit df8936e

File tree

7 files changed

+525
-5
lines changed

7 files changed

+525
-5
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ class MyRuntimeFactory:
165165
async def new_runtime(self, entrypoint: str, runtime_id: str) -> UiPathRuntimeProtocol:
166166
return MyRuntime()
167167

168-
def discover_runtimes(self) -> list[UiPathRuntimeProtocol]:
168+
async def discover_runtimes(self) -> list[UiPathRuntimeProtocol]:
169169
return []
170170

171171
def discover_entrypoints(self) -> list[str]:
@@ -311,7 +311,7 @@ class ChildRuntimeFactory:
311311
async def new_runtime(self, entrypoint: str) -> UiPathRuntimeProtocol:
312312
return ChildRuntime(name=entrypoint)
313313

314-
def discover_runtimes(self) -> list[UiPathRuntimeProtocol]:
314+
async def discover_runtimes(self) -> list[UiPathRuntimeProtocol]:
315315
return []
316316

317317
def discover_entrypoints(self) -> list[str]:

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "uipath-runtime"
3-
version = "0.0.15"
3+
version = "0.0.16"
44
description = "Runtime abstractions and interfaces for building agents and automation scripts in the UiPath ecosystem"
55
readme = { file = "README.md", content-type = "text/markdown" }
66
requires-python = ">=3.11"

src/uipath/runtime/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
UiPathRuntimeFactoryProtocol,
2121
UiPathRuntimeScannerProtocol,
2222
)
23+
from uipath.runtime.registry import UiPathRuntimeFactoryRegistry
2324
from uipath.runtime.result import (
2425
UiPathRuntimeResult,
2526
UiPathRuntimeStatus,
@@ -36,6 +37,7 @@
3637
UiPathResumeTrigger,
3738
UiPathResumeTriggerType,
3839
)
40+
from uipath.runtime.schema import UiPathRuntimeSchema
3941

4042
__all__ = [
4143
"UiPathExecuteOptions",
@@ -46,9 +48,11 @@
4648
"UiPathRuntimeCreatorProtocol",
4749
"UiPathRuntimeScannerProtocol",
4850
"UiPathRuntimeFactoryProtocol",
51+
"UiPathRuntimeFactoryRegistry",
4952
"UiPathRuntimeResult",
5053
"UiPathRuntimeStatus",
5154
"UiPathRuntimeEvent",
55+
"UiPathRuntimeSchema",
5256
"UiPathResumableStorageProtocol",
5357
"UiPathResumeTriggerProtocol",
5458
"UiPathApiTrigger",

src/uipath/runtime/factory.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
class UiPathRuntimeScannerProtocol(Protocol):
99
"""Protocol for discovering all UiPath runtime instances."""
1010

11-
def discover_runtimes(self) -> list[UiPathRuntimeProtocol]:
11+
async def discover_runtimes(self) -> list[UiPathRuntimeProtocol]:
1212
"""Discover all runtime classes."""
1313
...
1414

src/uipath/runtime/registry.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
"""Registry for UiPath runtime factories."""
2+
3+
from pathlib import Path
4+
from typing import Callable, TypeAlias
5+
6+
from uipath.runtime.context import UiPathRuntimeContext
7+
from uipath.runtime.factory import UiPathRuntimeFactoryProtocol
8+
9+
FactoryCallable: TypeAlias = Callable[
10+
[UiPathRuntimeContext | None], UiPathRuntimeFactoryProtocol
11+
]
12+
13+
14+
class UiPathRuntimeFactoryRegistry:
15+
"""Registry for UiPath runtime factories."""
16+
17+
_factories: dict[str, tuple[FactoryCallable, str]] = {}
18+
_registration_order: list[str] = []
19+
_default_name: str | None = None
20+
21+
@classmethod
22+
def register(
23+
cls, name: str, factory_callable: FactoryCallable, config_file: str
24+
) -> None:
25+
"""Register factory callable with its config file indicator.
26+
27+
Args:
28+
name: Factory identifier
29+
factory_callable: Callable that accepts context and returns a factory instance
30+
config_file: Config file name that indicates this factory should be used
31+
"""
32+
if name in cls._factories:
33+
cls._registration_order.remove(name)
34+
35+
cls._factories[name] = (factory_callable, config_file)
36+
cls._registration_order.append(name)
37+
38+
@classmethod
39+
def get(
40+
cls,
41+
name: str | None = None,
42+
search_path: str = ".",
43+
context: UiPathRuntimeContext | None = None,
44+
) -> UiPathRuntimeFactoryProtocol:
45+
"""Get factory instance by name or auto-detect from config files.
46+
47+
Args:
48+
name: Optional factory name
49+
search_path: Path to search for config files
50+
context: UiPathRuntimeContext to pass to factory
51+
52+
Returns:
53+
Factory instance
54+
"""
55+
if name:
56+
if name not in cls._factories:
57+
raise ValueError(f"Factory '{name}' not registered")
58+
factory_callable, _ = cls._factories[name]
59+
return factory_callable(context)
60+
61+
# Auto-detect based on config files in reverse registration order
62+
search_dir = Path(search_path)
63+
for factory_name in reversed(cls._registration_order):
64+
factory_callable, config_file = cls._factories[factory_name]
65+
if (search_dir / config_file).exists():
66+
return factory_callable(context)
67+
68+
# Fallback to default
69+
if cls._default_name is None:
70+
raise ValueError("No default factory registered and no config file found")
71+
factory_callable, _ = cls._factories[cls._default_name]
72+
return factory_callable(context)
73+
74+
@classmethod
75+
def set_default(cls, name: str) -> None:
76+
"""Set a factory as default."""
77+
if name not in cls._factories:
78+
raise ValueError(f"Factory '{name}' not registered")
79+
cls._default_name = name
80+
81+
@classmethod
82+
def get_all(cls) -> dict[str, str]:
83+
"""Get all registered factories.
84+
85+
Returns:
86+
Dict mapping factory names to their config files
87+
"""
88+
return {name: config_file for name, (_, config_file) in cls._factories.items()}

0 commit comments

Comments
 (0)