logging
This commit is contained in:
@@ -3,6 +3,30 @@ Core tooltip management functionality.
|
||||
"""
|
||||
from typing import Dict, Optional, Union, List
|
||||
from collections import deque
|
||||
import logging
|
||||
|
||||
# Configure logging
|
||||
logger = logging.getLogger('tooltip_manager')
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
# Create handlers if no handlers exist
|
||||
if not logger.handlers:
|
||||
# Console handler
|
||||
ch = logging.StreamHandler()
|
||||
ch.setLevel(logging.DEBUG)
|
||||
|
||||
# File handler
|
||||
fh = logging.FileHandler('tooltips.log')
|
||||
fh.setLevel(logging.DEBUG)
|
||||
|
||||
# Create formatter
|
||||
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
ch.setFormatter(formatter)
|
||||
fh.setFormatter(formatter)
|
||||
|
||||
# Add handlers
|
||||
logger.addHandler(ch)
|
||||
logger.addHandler(fh)
|
||||
|
||||
class TooltipManager:
|
||||
"""
|
||||
@@ -31,45 +55,55 @@ class TooltipManager:
|
||||
Returns:
|
||||
Dictionary mapping parameter names to tooltip descriptions
|
||||
"""
|
||||
logger.info(f"Getting tooltips for node class: {node_class}")
|
||||
tooltips = {}
|
||||
visited = set()
|
||||
inheritance_chain = []
|
||||
queue = deque([(node_class, 0)]) # (class_name, depth)
|
||||
max_depth = 0
|
||||
|
||||
logger.debug(f"Starting BFS for inheritance chain of {node_class}")
|
||||
# Build inheritance chain using BFS, tracking depth
|
||||
while queue:
|
||||
current, depth = queue.popleft()
|
||||
if current in visited:
|
||||
logger.debug(f"Skipping already visited class: {current}")
|
||||
continue
|
||||
|
||||
visited.add(current)
|
||||
inheritance_chain.append((current, depth))
|
||||
max_depth = max(max_depth, depth)
|
||||
logger.debug(f"Processing class {current} at depth {depth}")
|
||||
|
||||
if current in cls.INHERITANCE_MAP:
|
||||
parents = cls.INHERITANCE_MAP[current]
|
||||
logger.debug(f"Found parents for {current}: {parents}")
|
||||
if isinstance(parents, list):
|
||||
# Add all parents at the next depth level
|
||||
queue.extend((parent, depth + 1) for parent in parents)
|
||||
else:
|
||||
queue.append((parents, depth + 1))
|
||||
|
||||
logger.debug(f"Inheritance chain for {node_class}: {inheritance_chain}")
|
||||
|
||||
# Process tooltips in natural order (depth 0 to max_depth)
|
||||
# This means we'll process derived classes first, which is what we want
|
||||
# since derived class tooltips should override base class tooltips
|
||||
for depth in range(0, max_depth + 1):
|
||||
# Get all classes at this depth level
|
||||
classes_at_depth = [cls_name for cls_name, d in inheritance_chain if d == depth]
|
||||
logger.debug(f"Processing classes at depth {depth}: {classes_at_depth}")
|
||||
for class_name in classes_at_depth:
|
||||
if class_name in cls.NODE_TOOLTIPS:
|
||||
# Add tooltips from this class, but don't override any existing tooltips
|
||||
# This ensures derived class tooltips take precedence over base class tooltips
|
||||
class_tooltips = cls.NODE_TOOLTIPS[class_name]
|
||||
logger.debug(f"Adding tooltips from {class_name}: {list(class_tooltips.keys())}")
|
||||
for param, tooltip in class_tooltips.items():
|
||||
if param not in tooltips: # Only add if not already defined
|
||||
tooltips[param] = tooltip
|
||||
logger.debug(f"Added tooltip for {param} from {class_name}")
|
||||
|
||||
logger.info(f"Final tooltips for {node_class}: {list(tooltips.keys())}")
|
||||
return tooltips
|
||||
|
||||
@classmethod
|
||||
@@ -84,8 +118,11 @@ class TooltipManager:
|
||||
Returns:
|
||||
str: Tooltip text for the parameter, or empty string if not found
|
||||
"""
|
||||
logger.debug(f"Getting tooltip for {node_class}.{param_name}")
|
||||
tooltips = cls.get_tooltips(node_class)
|
||||
return tooltips.get(param_name, "")
|
||||
result = tooltips.get(param_name, "")
|
||||
logger.debug(f"Tooltip for {node_class}.{param_name}: {'<empty>' if not result else 'found'}")
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def get_description(cls, node_class: str) -> str:
|
||||
@@ -99,6 +136,7 @@ class TooltipManager:
|
||||
Returns:
|
||||
str: Combined description text for the node
|
||||
"""
|
||||
logger.info(f"Getting description for node class: {node_class}")
|
||||
descriptions = []
|
||||
visited = set()
|
||||
queue = deque([node_class])
|
||||
@@ -106,23 +144,28 @@ class TooltipManager:
|
||||
while queue:
|
||||
current = queue.popleft()
|
||||
if current in visited:
|
||||
logger.debug(f"Skipping already visited class for description: {current}")
|
||||
continue
|
||||
|
||||
visited.add(current)
|
||||
|
||||
# Add description if it exists
|
||||
if current in cls.NODE_DESCRIPTIONS:
|
||||
logger.debug(f"Found description for {current}")
|
||||
descriptions.append(cls.NODE_DESCRIPTIONS[current])
|
||||
|
||||
# Add parent classes to queue
|
||||
if current in cls.INHERITANCE_MAP:
|
||||
parents = cls.INHERITANCE_MAP[current]
|
||||
logger.debug(f"Adding parent classes for description lookup: {parents}")
|
||||
if isinstance(parents, list):
|
||||
queue.extend(parents)
|
||||
else:
|
||||
queue.append(parents)
|
||||
|
||||
return "\n\n".join(descriptions) if descriptions else ""
|
||||
result = "\n\n".join(descriptions) if descriptions else ""
|
||||
logger.info(f"Final description for {node_class}: {'<empty>' if not result else 'found'}")
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def register_tooltips(cls, node_class: str, tooltips: dict, inherits_from: Optional[Union[str, List[str]]] = None, description: Optional[str] = None):
|
||||
@@ -135,10 +178,15 @@ class TooltipManager:
|
||||
inherits_from: Optional parent class name(s) to inherit tooltips from
|
||||
description: Optional description text for the node
|
||||
"""
|
||||
logger.info(f"Registering tooltips for {node_class}")
|
||||
logger.debug(f"Tooltip params being registered: {list(tooltips.keys())}")
|
||||
|
||||
if inherits_from:
|
||||
logger.debug(f"Setting inheritance for {node_class}: {inherits_from}")
|
||||
cls.INHERITANCE_MAP[node_class] = inherits_from
|
||||
cls.NODE_TOOLTIPS[node_class] = tooltips
|
||||
if description:
|
||||
logger.debug(f"Setting description for {node_class}")
|
||||
cls.NODE_DESCRIPTIONS[node_class] = description
|
||||
|
||||
|
||||
@@ -150,13 +198,17 @@ def apply_tooltips(node_class):
|
||||
2. Set the DESCRIPTION attribute based on registered descriptions
|
||||
Only applies to classes that have an INPUT_TYPES classmethod.
|
||||
"""
|
||||
logger.info(f"Applying tooltips decorator to class: {node_class.__name__}")
|
||||
|
||||
# Set the DESCRIPTION attribute from registered descriptions
|
||||
description = TooltipManager.get_description(node_class.__name__)
|
||||
if description:
|
||||
logger.debug(f"Setting DESCRIPTION for {node_class.__name__}")
|
||||
node_class.DESCRIPTION = description
|
||||
|
||||
# If the class doesn't have INPUT_TYPES, just return it
|
||||
if not hasattr(node_class, 'INPUT_TYPES'):
|
||||
logger.warning(f"Class {node_class.__name__} has no INPUT_TYPES, skipping tooltip application")
|
||||
return node_class
|
||||
|
||||
original_input_types = node_class.INPUT_TYPES
|
||||
@@ -167,27 +219,34 @@ def apply_tooltips(node_class):
|
||||
Wrapper for INPUT_TYPES that adds tooltips to the configuration.
|
||||
Handles tooltip application failures gracefully.
|
||||
"""
|
||||
logger.debug(f"Getting input types with tooltips for {cls.__name__}")
|
||||
|
||||
# Get the original method result first - this must succeed for the node to work
|
||||
try:
|
||||
input_types = original_input_types.__get__(cls, cls)()
|
||||
logger.debug(f"Original input types for {cls.__name__}: {list(input_types.keys()) if isinstance(input_types, dict) else 'non-dict'}")
|
||||
except Exception as e:
|
||||
# If the original method fails, the node is broken - let error propagate
|
||||
logger.error(f"Failed to get original input types for {cls.__name__}: {str(e)}", exc_info=True)
|
||||
raise
|
||||
|
||||
# If not a dict or empty, return as-is
|
||||
if not isinstance(input_types, dict) or not input_types:
|
||||
logger.warning(f"Input types for {cls.__name__} is not a dict or is empty, returning as-is")
|
||||
return input_types
|
||||
|
||||
# Now try to add tooltips - any failure here should be contained
|
||||
try:
|
||||
tooltips = TooltipManager.get_tooltips(cls.__name__)
|
||||
logger.debug(f"Retrieved tooltips for {cls.__name__}: {list(tooltips.keys())}")
|
||||
|
||||
def add_tooltip_to_config(param_name, config):
|
||||
if param_name not in tooltips:
|
||||
logger.debug(f"No tooltip found for parameter {param_name}")
|
||||
return config
|
||||
|
||||
try:
|
||||
tooltip = tooltips[param_name]
|
||||
logger.debug(f"Adding tooltip to {param_name}: {tooltip[:50]}...")
|
||||
|
||||
# Handle tuple format (type, config_dict)
|
||||
if isinstance(config, tuple) and len(config) == 2:
|
||||
@@ -203,13 +262,15 @@ def apply_tooltips(node_class):
|
||||
# If param_type is a tuple containing a method call result, preserve it
|
||||
elif isinstance(param_type, tuple) and len(param_type) > 0:
|
||||
return (param_type, {"tooltip": tooltip})
|
||||
logger.debug(f"Could not add tooltip to {param_name} - unsupported config format")
|
||||
return config
|
||||
except Exception:
|
||||
# If adding this specific tooltip fails, return config untouched
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to add tooltip to {param_name}: {str(e)}", exc_info=True)
|
||||
return config
|
||||
|
||||
# Process required parameters
|
||||
if "required" in input_types:
|
||||
logger.debug(f"Processing required parameters for {cls.__name__}")
|
||||
input_types["required"] = {
|
||||
name: add_tooltip_to_config(name, config)
|
||||
for name, config in input_types["required"].items()
|
||||
@@ -217,17 +278,18 @@ def apply_tooltips(node_class):
|
||||
|
||||
# Process optional parameters
|
||||
if "optional" in input_types:
|
||||
logger.debug(f"Processing optional parameters for {cls.__name__}")
|
||||
input_types["optional"] = {
|
||||
name: add_tooltip_to_config(name, config)
|
||||
for name, config in input_types["optional"].items()
|
||||
}
|
||||
except Exception:
|
||||
# If tooltip processing fails, return original input_types
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to process tooltips for {cls.__name__}: {str(e)}", exc_info=True)
|
||||
|
||||
return input_types
|
||||
|
||||
# Replace the original INPUT_TYPES with our wrapped version
|
||||
logger.info(f"Replacing INPUT_TYPES for {node_class.__name__} with tooltip-enabled version")
|
||||
node_class.INPUT_TYPES = input_types_with_tooltips
|
||||
|
||||
return node_class
|
||||
Reference in New Issue
Block a user