| | |
| | from .api_registry import ( |
| | ComfyAPIBase as ComfyAPIBase, |
| | ComfyAPIWithVersion as ComfyAPIWithVersion, |
| | register_versions as register_versions, |
| | get_all_versions as get_all_versions, |
| | ) |
| |
|
| | import asyncio |
| | from dataclasses import asdict |
| | from typing import Callable, Optional |
| |
|
| |
|
| | def first_real_override(cls: type, name: str, *, base: type=None) -> Optional[Callable]: |
| | """Return the *callable* override of `name` visible on `cls`, or None if every |
| | implementation up to (and including) `base` is the placeholder defined on `base`. |
| | |
| | If base is not provided, it will assume cls has a GET_BASE_CLASS |
| | """ |
| | if base is None: |
| | if not hasattr(cls, "GET_BASE_CLASS"): |
| | raise ValueError("base is required if cls does not have a GET_BASE_CLASS; is this a valid ComfyNode subclass?") |
| | base = cls.GET_BASE_CLASS() |
| | base_attr = getattr(base, name, None) |
| | if base_attr is None: |
| | return None |
| | base_func = base_attr.__func__ |
| | for c in cls.mro(): |
| | if c is base: |
| | break |
| | if name in c.__dict__: |
| | func = getattr(c, name).__func__ |
| | if func is not base_func: |
| | return getattr(cls, name) |
| | return None |
| |
|
| |
|
| | class _ComfyNodeInternal: |
| | """Class that all V3-based APIs inherit from for ComfyNode. |
| | |
| | This is intended to only be referenced within execution.py, as it has to handle all V3 APIs going forward.""" |
| | @classmethod |
| | def GET_NODE_INFO_V1(cls): |
| | ... |
| |
|
| |
|
| | class _NodeOutputInternal: |
| | """Class that all V3-based APIs inherit from for NodeOutput. |
| | |
| | This is intended to only be referenced within execution.py, as it has to handle all V3 APIs going forward.""" |
| | ... |
| |
|
| |
|
| | def as_pruned_dict(dataclass_obj): |
| | '''Return dict of dataclass object with pruned None values.''' |
| | return prune_dict(asdict(dataclass_obj)) |
| |
|
| | def prune_dict(d: dict): |
| | return {k: v for k,v in d.items() if v is not None} |
| |
|
| |
|
| | def is_class(obj): |
| | ''' |
| | Returns True if is a class type. |
| | Returns False if is a class instance. |
| | ''' |
| | return isinstance(obj, type) |
| |
|
| |
|
| | def copy_class(cls: type) -> type: |
| | ''' |
| | Copy a class and its attributes. |
| | ''' |
| | if cls is None: |
| | return None |
| | cls_dict = { |
| | k: v for k, v in cls.__dict__.items() |
| | if k not in ('__dict__', '__weakref__', '__module__', '__doc__') |
| | } |
| | |
| | new_cls = type( |
| | cls.__name__, |
| | (cls,), |
| | cls_dict |
| | ) |
| | |
| | new_cls.__module__ = cls.__module__ |
| | new_cls.__doc__ = cls.__doc__ |
| | return new_cls |
| |
|
| |
|
| | class classproperty(object): |
| | def __init__(self, f): |
| | self.f = f |
| | def __get__(self, obj, owner): |
| | return self.f(owner) |
| |
|
| |
|
| | |
| | def shallow_clone_class(cls, new_name=None): |
| | ''' |
| | Shallow clone a class while preserving super() functionality. |
| | ''' |
| | new_name = new_name or f"{cls.__name__}Clone" |
| | |
| | new_bases = (cls,) + cls.__bases__ |
| | return type(new_name, new_bases, dict(cls.__dict__)) |
| |
|
| | |
| | def lock_class(cls): |
| | ''' |
| | Lock a class so that its top-levelattributes cannot be modified. |
| | ''' |
| | |
| | def locked_instance_setattr(self, name, value): |
| | raise AttributeError( |
| | f"Cannot set attribute '{name}' on immutable instance of {type(self).__name__}" |
| | ) |
| | |
| | class LockedMeta(type(cls)): |
| | def __setattr__(cls_, name, value): |
| | raise AttributeError( |
| | f"Cannot modify class attribute '{name}' on locked class '{cls_.__name__}'" |
| | ) |
| | |
| | locked_dict = dict(cls.__dict__) |
| | locked_dict['__setattr__'] = locked_instance_setattr |
| |
|
| | return LockedMeta(cls.__name__, cls.__bases__, locked_dict) |
| |
|
| |
|
| | def make_locked_method_func(type_obj, func, class_clone): |
| | """ |
| | Returns a function that, when called with **inputs, will execute: |
| | getattr(type_obj, func).__func__(lock_class(class_clone), **inputs) |
| | |
| | Supports both synchronous and asynchronous methods. |
| | """ |
| | locked_class = lock_class(class_clone) |
| | method = getattr(type_obj, func).__func__ |
| |
|
| | |
| | if asyncio.iscoroutinefunction(method): |
| | async def wrapped_async_func(**inputs): |
| | return await method(locked_class, **inputs) |
| | return wrapped_async_func |
| | else: |
| | def wrapped_func(**inputs): |
| | return method(locked_class, **inputs) |
| | return wrapped_func |
| |
|