Skip to content

Commit 64374b4

Browse files
committed
feat: add runtime factory registry
1 parent 5546916 commit 64374b4

File tree

5 files changed

+437
-2
lines changed

5 files changed

+437
-2
lines changed

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: 2 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,
@@ -46,6 +47,7 @@
4647
"UiPathRuntimeCreatorProtocol",
4748
"UiPathRuntimeScannerProtocol",
4849
"UiPathRuntimeFactoryProtocol",
50+
"UiPathRuntimeFactoryRegistry",
4951
"UiPathRuntimeResult",
5052
"UiPathRuntimeStatus",
5153
"UiPathRuntimeEvent",

src/uipath/runtime/registry.py

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

0 commit comments

Comments
 (0)