mirror of
https://github.com/deepmodeling/Uni-Lab-OS
synced 2026-04-23 17:19:58 +00:00
update workbench example
update aksk desc print res query logs Fix skills exec error with action type Update Skills Update Skills addr Change uni-lab. to leap-lab. Support unit in pylabrobot Support async func. change to leap-lab backend. Support feedback interval. Reduce cocurrent lags. fix create_resource_with_slot update unilabos_formulation & batch-submit-exp scale multi exec thread up to 48 update handle creation api fit cocurrent gap add running status debounce allow non @topic_config support update skill add placeholder keys always free 提交实验技能 disable samples correct sample demo ret value 新增试剂reagent update registry 新增manual_confirm add workstation creation skill add virtual_sample_demo 样品追踪测试设备 add external devices param fix registry upload missing type fast registry load minor fix on skill & registry stripe ros2 schema desc add create-device-skill new registry system backwards to yaml remove not exist resource new registry sys exp. support with add device correct raise create resource error ret info fix revert ret info fix fix prcxi check add create_resource schema re signal host ready event add websocket connection timeout and improve reconnection logic add open_timeout parameter to websocket connection add TimeoutError and InvalidStatus exception handling implement exponential backoff for reconnection attempts simplify reconnection logic flow
This commit is contained in:
@@ -139,6 +139,7 @@ def scan_directory(
|
||||
executor: ThreadPoolExecutor = None,
|
||||
exclude_files: Optional[set] = None,
|
||||
cache: Optional[Dict[str, Any]] = None,
|
||||
include_files: Optional[List[Union[str, Path]]] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Recursively scan .py files under *root_dir* for @device and @resource
|
||||
@@ -164,6 +165,7 @@ def scan_directory(
|
||||
exclude_files: 要排除的文件名集合 (如 {"lab_resources.py"})
|
||||
cache: Mutable cache dict (``load_scan_cache()`` result). Hits are read
|
||||
from here; misses are written back so the caller can persist later.
|
||||
include_files: 指定扫描的文件列表,提供时跳过目录递归收集,直接扫描这些文件。
|
||||
"""
|
||||
if executor is None:
|
||||
raise ValueError("executor is required and must not be None")
|
||||
@@ -175,7 +177,10 @@ def scan_directory(
|
||||
python_path = Path(python_path).resolve()
|
||||
|
||||
# --- Collect files (depth/count limited) ---
|
||||
py_files = _collect_py_files(root_dir, max_depth=max_depth, max_files=max_files, exclude_files=exclude_files)
|
||||
if include_files is not None:
|
||||
py_files = [Path(f).resolve() for f in include_files if Path(f).resolve().exists()]
|
||||
else:
|
||||
py_files = _collect_py_files(root_dir, max_depth=max_depth, max_files=max_files, exclude_files=exclude_files)
|
||||
|
||||
cache_files: Dict[str, Any] = cache.get("files", {}) if cache else {}
|
||||
|
||||
@@ -674,14 +679,17 @@ def _resolve_name(name: str, import_map: Dict[str, str]) -> str:
|
||||
return name
|
||||
|
||||
|
||||
_DECORATOR_ENUM_CLASSES = frozenset({"Side", "DataSource", "NodeType"})
|
||||
|
||||
|
||||
def _resolve_attribute(node: ast.Attribute, import_map: Dict[str, str]) -> str:
|
||||
"""
|
||||
Resolve an attribute access like Side.NORTH or DataSource.HANDLE.
|
||||
|
||||
Returns a string like "NORTH" for enum values, or
|
||||
"module.path:Class.attr" for imported references.
|
||||
对于来自 ``unilabos.registry.decorators`` 的枚举类 (Side / DataSource / NodeType),
|
||||
直接返回枚举成员名 (如 ``"NORTH"`` / ``"HANDLE"`` / ``"MANUAL_CONFIRM"``),
|
||||
省去消费端二次 rsplit 解析。其它 import 仍返回完整模块路径。
|
||||
"""
|
||||
# Get the full dotted path
|
||||
parts = []
|
||||
current = node
|
||||
while isinstance(current, ast.Attribute):
|
||||
@@ -691,21 +699,20 @@ def _resolve_attribute(node: ast.Attribute, import_map: Dict[str, str]) -> str:
|
||||
parts.append(current.id)
|
||||
|
||||
parts.reverse()
|
||||
# parts = ["Side", "NORTH"] or ["DataSource", "HANDLE"]
|
||||
# parts = ["Side", "NORTH"] or ["DataSource", "HANDLE"] or ["NodeType", "MANUAL_CONFIRM"]
|
||||
|
||||
if len(parts) >= 2:
|
||||
base = parts[0]
|
||||
attr = ".".join(parts[1:])
|
||||
|
||||
# If the base is an imported name, resolve it
|
||||
if base in _DECORATOR_ENUM_CLASSES:
|
||||
source = import_map.get(base, "")
|
||||
if not source or _REGISTRY_DECORATOR_MODULE in source:
|
||||
return parts[-1]
|
||||
|
||||
if base in import_map:
|
||||
return f"{import_map[base]}.{attr}"
|
||||
|
||||
# For known enum-like patterns, return just the value
|
||||
# e.g. Side.NORTH -> "NORTH"
|
||||
if base in ("Side", "DataSource"):
|
||||
return parts[-1]
|
||||
|
||||
return ".".join(parts)
|
||||
|
||||
|
||||
@@ -818,6 +825,7 @@ def _extract_class_body(
|
||||
action_args.setdefault("placeholder_keys", {})
|
||||
action_args.setdefault("always_free", False)
|
||||
action_args.setdefault("is_protocol", False)
|
||||
action_args.setdefault("feedback_interval", 1.0)
|
||||
action_args.setdefault("description", "")
|
||||
action_args.setdefault("auto_prefix", False)
|
||||
action_args.setdefault("parent", False)
|
||||
|
||||
Reference in New Issue
Block a user