docs: map existing codebase
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
# Architecture
|
||||
|
||||
**Analysis Date:** 2026-03-03
|
||||
|
||||
## Pattern Overview
|
||||
|
||||
**Overall:** Node-based plugin architecture for ComfyUI
|
||||
|
||||
**Key Characteristics:**
|
||||
- Modular node design where each node implements ComfyUI's node interface
|
||||
- Each node is a standalone class with INPUT_TYPES, RETURN_TYPES, and execution methods
|
||||
- Nodes are registered in a central registry for ComfyUI framework discovery
|
||||
- String processing operations implemented using Python's `re` module for regex patterns
|
||||
- No external dependencies beyond Python standard library
|
||||
- Plugin loaded via `__init__.py` which exports node mappings
|
||||
|
||||
## Layers
|
||||
|
||||
**Node Layer:**
|
||||
- Purpose: Implements individual string manipulation operations
|
||||
- Location: Root directory - `string_textbox_node.py`, `string_strip_node.py`, `string_multi_replace_node.py`, `string_preview_node.py`, `string_conditional_append_node.py`, `string_contains_any_node.py`
|
||||
- Contains: Node classes inheriting ComfyUI's node interface
|
||||
- Depends on: Python `re` module for regex operations
|
||||
- Used by: ComfyUI framework (loads via NODE_CLASS_MAPPINGS)
|
||||
|
||||
**Plugin Registry Layer:**
|
||||
- Purpose: Provides node discovery and registration for ComfyUI
|
||||
- Location: `__init__.py`
|
||||
- Contains: NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS, WEB_DIRECTORY exports
|
||||
- Depends on: All node modules
|
||||
- Used by: ComfyUI framework at startup
|
||||
|
||||
**Testing Layer:**
|
||||
- Purpose: Unit tests for all node functionalities
|
||||
- Location: `test_string_nodes.py`
|
||||
- Contains: unittest.TestCase subclass with comprehensive test suites
|
||||
- Depends on: All node classes
|
||||
- Used by: Developers running tests locally
|
||||
|
||||
## Data Flow
|
||||
|
||||
**String Processing Pipeline:**
|
||||
|
||||
1. Input received via `INPUT_TYPES` - user specifies string and processing parameters
|
||||
2. ComfyUI calls node's FUNCTION method (e.g., `string_strip`, `string_replace`)
|
||||
3. Processing method performs regex-based transformations
|
||||
4. Optional cleanup via `cleanup_text()` if `remove_extra_spaces` is True
|
||||
5. Return processed string(s) via RETURN_TYPES tuple
|
||||
|
||||
**Example - StringStripNode flow:**
|
||||
1. User provides `input_string` and `strings_to_remove` (multiline)
|
||||
2. `string_strip()` method parses removal list, sorted by length (longest first)
|
||||
3. Regex patterns compiled with case sensitivity flags
|
||||
4. Patterns applied sequentially to input string
|
||||
5. Cleanup normalizes whitespace if enabled
|
||||
6. Result returned as single-element tuple
|
||||
|
||||
**Example - StringConditionalAppendNode flow:**
|
||||
1. User provides `input_string` and `strings_to_check` (multiline)
|
||||
2. `conditional_append()` parses check list
|
||||
3. Each string checked for presence in result (with case sensitivity option)
|
||||
4. Missing strings appended at specified position (beginning/end)
|
||||
5. Returns tuple of (modified_string, was_appended boolean)
|
||||
|
||||
**State Management:**
|
||||
- Stateless nodes - each execution independent
|
||||
- No shared state between invocations
|
||||
- Only state change: modifying the input string passed through
|
||||
|
||||
## Key Abstractions
|
||||
|
||||
**Node Interface Pattern:**
|
||||
- Purpose: Standardizes how nodes interact with ComfyUI framework
|
||||
- Examples: `StringTextboxNode` (`string_textbox_node.py`), `StringStripNode` (`string_strip_node.py`)
|
||||
- Pattern: Each node class implements three key class attributes:
|
||||
- `INPUT_TYPES()` classmethod - declares input parameters and their types
|
||||
- `RETURN_TYPES` tuple - declares output types
|
||||
- `FUNCTION` string - method name to call for execution
|
||||
- Execution method - performs actual processing and returns tuple
|
||||
|
||||
**Text Processing Pattern:**
|
||||
- Purpose: Shared approach for string matching and replacement across nodes
|
||||
- Examples: `string_strip_node.py`, `string_multi_replace_node.py`
|
||||
- Pattern: Uses `re.compile()` with configurable flags (IGNORECASE), patterns include:
|
||||
- Word boundary matching: `\b` for whole word matching
|
||||
- Punctuation handling: conditional patterns with `(?:[,;:.])` for preservation/removal
|
||||
- Regex escaping: `re.escape()` to treat user input as literals
|
||||
|
||||
**Text Cleanup Pattern:**
|
||||
- Purpose: Normalize whitespace after text modifications
|
||||
- Examples: `cleanup_text()` method in `string_strip_node.py`, `string_multi_replace_node.py`
|
||||
- Pattern: Two-step regex approach:
|
||||
1. Remove spaces before punctuation: `r'\s+([,;:.])'` → `r'\1'`
|
||||
2. Normalize multiple spaces: `r'\s{2,}'` → `' '`
|
||||
3. Strip leading/trailing whitespace
|
||||
|
||||
**Multi-value Processing Pattern:**
|
||||
- Purpose: Handle comma-separated or line-separated lists of values
|
||||
- Examples: All nodes with multiline string inputs
|
||||
- Pattern: `.splitlines()` to split input, `.strip()` to clean each line, filter empty strings
|
||||
|
||||
## Entry Points
|
||||
|
||||
**ComfyUI Framework Loading:**
|
||||
- Location: `__init__.py`
|
||||
- Triggers: ComfyUI startup/refresh
|
||||
- Responsibilities:
|
||||
- Imports all node classes
|
||||
- Exports NODE_CLASS_MAPPINGS for framework discovery
|
||||
- Exports NODE_DISPLAY_NAME_MAPPINGS for UI display names
|
||||
- Exports WEB_DIRECTORY for any client-side assets
|
||||
|
||||
**Individual Node Execution:**
|
||||
- Location: Each node's FUNCTION method (e.g., `string_strip()` in `string_strip_node.py`)
|
||||
- Triggers: ComfyUI workflow execution when node is evaluated
|
||||
- Responsibilities:
|
||||
- Accept parameters from INPUT_TYPES
|
||||
- Perform string processing
|
||||
- Return results as tuple matching RETURN_TYPES
|
||||
|
||||
**Output Display:**
|
||||
- Location: `string_preview_node.py` - StringPreviewNode
|
||||
- Triggers: Workflow execution
|
||||
- Responsibilities:
|
||||
- Display string output in UI via `preview_string()` method
|
||||
- Force re-evaluation on every execution via `IS_CHANGED()` returning NaN
|
||||
- Return both UI display data and result tuple
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Strategy:** Defensive programming - validate and handle edge cases gracefully
|
||||
|
||||
**Patterns:**
|
||||
- Empty input handling: All nodes check for empty strings and return sensible defaults
|
||||
- Example: `string_contains_any_node.py` line 40-41 returns `(False, "")` for empty substrings
|
||||
- Example: `string_conditional_append_node.py` line 44-45 returns unchanged input if no strings to check
|
||||
- Line parsing robustness: `.strip()` applied to each line, whitespace-only lines filtered
|
||||
- Example: `string_strip_node.py` line 36-38 skips empty/whitespace lines in removal list
|
||||
- Regex compilation safety: `re.escape()` used to prevent regex injection from user input
|
||||
- Example: `string_strip_node.py` line 55, 62 escape user strings before pattern compilation
|
||||
- Delimiter handling: Split with `maxsplit=1` to allow delimiter characters in replacement text
|
||||
- Example: `string_multi_replace_node.py` line 42 uses `split(delimiter, 1)` to preserve right side
|
||||
|
||||
## Cross-Cutting Concerns
|
||||
|
||||
**Logging:** Not implemented - ComfyUI framework handles logging
|
||||
|
||||
**Validation:**
|
||||
- User input validation occurs at parsing stage (splitlines, strip, filter empty)
|
||||
- Type validation handled by ComfyUI framework via INPUT_TYPES/RETURN_TYPES declarations
|
||||
- No explicit validation errors thrown; nodes handle edge cases gracefully
|
||||
|
||||
**Authentication:** Not applicable - nodes are part of ComfyUI plugin
|
||||
|
||||
**Case Sensitivity:**
|
||||
- All text nodes support configurable case-sensitive matching via `match_case` boolean parameter
|
||||
- Implemented via `re.IGNORECASE` flag when `match_case=False`
|
||||
- Example: `string_strip_node.py` line 47 sets `flags = 0 if match_case else re.IGNORECASE`
|
||||
|
||||
**Punctuation Handling:**
|
||||
- Special feature introduced to preserve punctuation adjacent to matched text
|
||||
- Controlled by `preserve_punctuation` boolean parameter
|
||||
- Enabled by default in most nodes for safe prompt/syntax handling
|
||||
- Example: `string_strip_node.py` lines 53-65 show pattern variants for punctuation preservation
|
||||
|
||||
---
|
||||
|
||||
*Architecture analysis: 2026-03-03*
|
||||
@@ -0,0 +1,165 @@
|
||||
# Codebase Concerns
|
||||
|
||||
**Analysis Date:** 2026-03-03
|
||||
|
||||
## Code Duplication
|
||||
|
||||
**Regex pattern compilation duplicated across node classes:**
|
||||
- Issue: Both `StringStripNode` and `StringMultiReplaceNode` implement nearly identical regex pattern building logic for `match_whole_string` and `preserve_punctuation` combinations.
|
||||
- Files: `string_strip_node.py` lines 52-72, `string_multi_replace_node.py` lines 59-75
|
||||
- Impact: If regex patterns need adjustment (e.g., for new punctuation marks), changes must be made in multiple places, increasing maintenance burden and risk of inconsistency.
|
||||
- Fix approach: Extract shared regex pattern building logic into a utility module `string_utils.py` with a function like `build_replacement_pattern(search_str, match_whole_string, preserve_punctuation)` that both nodes can use.
|
||||
|
||||
**Text cleanup logic duplicated:**
|
||||
- Issue: `cleanup_text()` method is defined identically in both `StringStripNode` and `StringMultiReplaceNode`.
|
||||
- Files: `string_strip_node.py` lines 27-30, `string_multi_replace_node.py` lines 29-32
|
||||
- Impact: Bug fixes to space normalization or punctuation handling require changes in two places.
|
||||
- Fix approach: Move `cleanup_text()` to a shared utility module.
|
||||
|
||||
## Input Validation Gaps
|
||||
|
||||
**No validation of `replacement_delimiter` parameter:**
|
||||
- Issue: `StringMultiReplaceNode.string_replace()` accepts any string as `replacement_delimiter`, including empty strings or strings not present in replacement_pairs.
|
||||
- Files: `string_multi_replace_node.py` lines 34-80
|
||||
- Impact: Empty delimiter could cause silent failures or unexpected behavior. No error message guides users on correct format.
|
||||
- Fix approach: Add validation to check if `replacement_delimiter` is non-empty and is actually present in at least one line of `replacement_pairs` when non-empty pairs are provided. Return error message if invalid.
|
||||
|
||||
**No null/None input handling:**
|
||||
- Issue: All nodes assume string inputs are valid. If ComfyUI passes `None` or unexpected types, nodes will crash with unhelpful error messages.
|
||||
- Files: All node classes (e.g., `string_strip_node.py` line 32)
|
||||
- Impact: Silent failures in ComfyUI workflows without clear error reporting.
|
||||
- Fix approach: Add type checking and explicit error handling at method entry points.
|
||||
|
||||
**Missing handling of extremely long strings:**
|
||||
- Issue: Regex operations on very large strings (e.g., multi-megabyte text) could cause performance issues or regex complexity bombs.
|
||||
- Files: All regex-based nodes (`string_strip_node.py`, `string_multi_replace_node.py`, `string_contains_any_node.py`)
|
||||
- Impact: Workflows processing large text files could hang or crash unexpectedly.
|
||||
- Fix approach: Add length checks and document maximum recommended input size.
|
||||
|
||||
## Pattern Matching Edge Cases
|
||||
|
||||
**Incomplete word boundary handling:**
|
||||
- Issue: The `\b` word boundary in regex (lines 55, 62 in `string_strip_node.py`) doesn't work correctly at string boundaries with Unicode characters, accented characters, or special symbols.
|
||||
- Files: `string_strip_node.py` lines 52-72, `string_multi_replace_node.py` lines 59-75
|
||||
- Impact: Matching whole words in non-English text or with special characters produces unexpected results.
|
||||
- Fix approach: Document this limitation or switch to a more robust word boundary detection method that handles Unicode properly.
|
||||
|
||||
**Regex special characters not escaped in `match_whole_string=False`:**
|
||||
- Issue: In `preserve_punctuation=False` mode, the pattern `re.escape(search_str) + r'(?:[,;:.])?'` (line 72 in `string_multi_replace_node.py`) may not handle all edge cases where the search string itself contains pattern-matching characters.
|
||||
- Files: `string_multi_replace_node.py` lines 70-72, `string_strip_node.py` lines 64-65
|
||||
- Impact: While `re.escape()` is used, the added `(?:[,;:.])?` pattern creates assumptions about which punctuation marks follow matches. Other punctuation or symbols would not be handled.
|
||||
- Fix approach: Either document the supported punctuation list or provide an option to make punctuation removal more flexible.
|
||||
|
||||
## Error Reporting
|
||||
|
||||
**No error messages returned to user:**
|
||||
- Issue: Invalid inputs (malformed replacement pairs, empty delimiters, etc.) fail silently or with generic Python exceptions.
|
||||
- Files: All node classes
|
||||
- Impact: Users get no feedback about what went wrong. Debugging workflows is difficult.
|
||||
- Fix approach: Return structured error messages or use ComfyUI's error reporting mechanism.
|
||||
|
||||
**Regex compilation errors not caught:**
|
||||
- Issue: If a user's input accidentally creates an invalid regex pattern, the `re.compile()` call (lines 74 in `string_multi_replace_node.py`, line 67 in `string_strip_node.py`) will raise an exception that crashes the node without a helpful message.
|
||||
- Files: `string_strip_node.py` line 67, `string_multi_replace_node.py` line 74
|
||||
- Impact: Even simple typos in user input cause workflow failures with unhelpful error messages.
|
||||
- Fix approach: Wrap `re.compile()` in try-except block and return meaningful error message.
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
**Regex patterns compiled multiple times per execution:**
|
||||
- Issue: In `StringStripNode.string_strip()`, patterns are compiled in a loop (line 67) and compiled separately in a list for each method call. For large lists of removals, this is inefficient.
|
||||
- Files: `string_strip_node.py` lines 50-71
|
||||
- Impact: Repeated calls with same removal patterns waste CPU cycles on recompilation.
|
||||
- Fix approach: Consider memoizing compiled patterns if the same `strings_to_remove` is used repeatedly, or compile once and reuse within a single call.
|
||||
|
||||
**Sorting by length every execution:**
|
||||
- Issue: Strings are sorted by length on every method call (lines 44 in `string_strip_node.py`, line 52 in `string_multi_replace_node.py`). This is necessary for correctness but could be optimized with caching if patterns are reused.
|
||||
- Files: `string_strip_node.py` line 44, `string_multi_replace_node.py` line 52
|
||||
- Impact: Minor performance impact for large replacement lists on repeated calls.
|
||||
- Fix approach: Document the O(n log n) sorting behavior; consider caching if needed.
|
||||
|
||||
## Testing Coverage Gaps
|
||||
|
||||
**No tests for regex compilation failures:**
|
||||
- What's not tested: Invalid regex patterns that would cause `re.compile()` to raise an exception.
|
||||
- Files: `string_strip_node.py` line 67, `string_multi_replace_node.py` line 74, `test_string_nodes.py`
|
||||
- Risk: Unexpected regex patterns from user input could crash the node without warning.
|
||||
- Priority: Medium
|
||||
|
||||
**No tests for Unicode or special character handling:**
|
||||
- What's not tested: Input strings with emoji, accented characters, or multi-byte Unicode.
|
||||
- Files: `test_string_nodes.py`
|
||||
- Risk: Real-world prompts with special characters may behave unexpectedly.
|
||||
- Priority: Medium
|
||||
|
||||
**No tests for extreme input sizes:**
|
||||
- What's not tested: Very long strings (megabytes), very long replacement lists, very long individual replacement pairs.
|
||||
- Files: `test_string_nodes.py`
|
||||
- Risk: Performance issues or stack overflow on large inputs would not be detected.
|
||||
- Priority: Low
|
||||
|
||||
**No tests for StringTextboxNode:**
|
||||
- What's not tested: The `StringTextboxNode.pass_string()` method has zero test coverage.
|
||||
- Files: `string_textbox_node.py`, `test_string_nodes.py`
|
||||
- Risk: Changes to this node's behavior would go undetected.
|
||||
- Priority: Low (simple pass-through, but should be covered)
|
||||
|
||||
**No tests for StringPreviewNode:**
|
||||
- What's not tested: The `StringPreviewNode.preview_string()` method and its special `IS_CHANGED()` behavior.
|
||||
- Files: `string_preview_node.py`, `test_string_nodes.py`
|
||||
- Risk: The `float("nan")` return value in `IS_CHANGED()` is unusual and not validated.
|
||||
- Priority: Medium
|
||||
|
||||
## Architecture Concerns
|
||||
|
||||
**No base class for shared functionality:**
|
||||
- Issue: Six independent node classes with duplicated methods and patterns indicate missing abstraction.
|
||||
- Files: All `*_node.py` files
|
||||
- Impact: Adding features (e.g., logging, input validation) requires changes to multiple files.
|
||||
- Fix approach: Create a `BaseStringNode` class with shared validation, error handling, and utility methods.
|
||||
|
||||
**Inconsistent parameter naming:**
|
||||
- Issue: `StringStripNode` uses `strings_to_remove`, `StringMultiReplaceNode` uses `replacement_pairs`, etc. Inconsistent naming makes workflows harder to understand and API harder to learn.
|
||||
- Files: All node classes
|
||||
- Impact: Users must remember different parameter names for similar operations.
|
||||
- Fix approach: Standardize parameter names across nodes or document the reasoning for differences.
|
||||
|
||||
## Missing Features
|
||||
|
||||
**No logging of transformations:**
|
||||
- Issue: Nodes silently perform transformations with no audit trail or debug output.
|
||||
- Files: All node classes
|
||||
- Impact: Users cannot see what replacements were actually made or debug complex workflows.
|
||||
- Fix approach: Add optional debug output or logging parameter.
|
||||
|
||||
**No reuse of previous results:**
|
||||
- Issue: Each node independently processes input; there's no way to chain results with visibility into intermediate states.
|
||||
- Files: All output nodes
|
||||
- Impact: Complex text processing requires many separate nodes instead of a single configurable pipeline.
|
||||
- Fix approach: Consider a single "Text Processing Pipeline" node that chains multiple operations.
|
||||
|
||||
## Potential Security Concerns
|
||||
|
||||
**Regular expression denial of service (ReDoS) risk:**
|
||||
- Issue: User-provided input in `strings_to_remove`, `replacement_pairs`, and `substrings` are used directly in regex patterns after `re.escape()`. While `re.escape()` prevents most ReDoS, deeply nested or pathological patterns could still cause issues.
|
||||
- Files: `string_strip_node.py` lines 52-72, `string_multi_replace_node.py` lines 59-75, `string_contains_any_node.py` (simple substring search, low risk)
|
||||
- Impact: Malicious or accidental input could cause CPU spike and freeze workflows.
|
||||
- Fix approach: Add timeout to regex operations or limit complexity of patterns accepted.
|
||||
|
||||
## Known Limitations
|
||||
|
||||
**Word boundary matching fails with Unicode:**
|
||||
- The `\b` anchor in regex patterns doesn't correctly identify word boundaries in non-ASCII text.
|
||||
- Workaround: Test thoroughly with non-English text; consider providing separate "ASCII-only" and "Unicode-aware" modes.
|
||||
|
||||
**Punctuation handling is opinionated:**
|
||||
- The hardcoded punctuation list `[,;:.]` only handles common English punctuation. Other languages or contexts may use different marks.
|
||||
- Workaround: Make punctuation marks configurable, or document the list and allow users to add custom nodes for other punctuation.
|
||||
|
||||
**Case-insensitive matching doesn't handle Unicode case folding:**
|
||||
- Python's `.lower()` method handles most Unicode, but some edge cases in Turkish, Greek, and other languages may not be handled optimally.
|
||||
- Workaround: Document that case-insensitive matching is "best effort" for non-ASCII text.
|
||||
|
||||
---
|
||||
|
||||
*Concerns audit: 2026-03-03*
|
||||
@@ -0,0 +1,186 @@
|
||||
# Coding Conventions
|
||||
|
||||
**Analysis Date:** 2026-03-03
|
||||
|
||||
## Naming Patterns
|
||||
|
||||
**Files:**
|
||||
- Node files: `string_<name>_node.py` (e.g., `string_strip_node.py`, `string_multi_replace_node.py`)
|
||||
- Test file: `test_<module>.py` (e.g., `test_string_nodes.py`)
|
||||
- Class names reflect file names in PascalCase (e.g., `StringStripNode` from `string_strip_node.py`)
|
||||
|
||||
**Classes:**
|
||||
- Node classes: `String<FunctionName>Node` format (e.g., `StringStripNode`, `StringMultiReplaceNode`, `StringConditionalAppendNode`)
|
||||
- All node classes follow ComfyUI convention with specific class methods and attributes
|
||||
|
||||
**Functions/Methods:**
|
||||
- snake_case for method names (e.g., `string_strip`, `cleanup_text`, `conditional_append`, `contains_any`)
|
||||
- Core execution method: `FUNCTION = "<method_name>"` points to the main processing method
|
||||
- Helper methods: lower case with underscores (e.g., `cleanup_text`)
|
||||
|
||||
**Variables:**
|
||||
- snake_case for local variables (e.g., `input_string`, `strings_to_remove`, `replacement_pairs`, `strings_list`)
|
||||
- Boolean prefixes: `match_case`, `match_whole_string`, `preserve_punctuation`, `was_appended`, `was_any_appended`
|
||||
|
||||
**Constants:**
|
||||
- UPPERCASE for class constants (e.g., `RETURN_TYPES`, `RETURN_NAMES`, `FUNCTION`, `CATEGORY`, `INPUT_TYPES`, `OUTPUT_NODE`)
|
||||
|
||||
## Code Style
|
||||
|
||||
**Formatting:**
|
||||
- No detected formatter configuration (.prettierrc, black config, etc.)
|
||||
- Standard Python conventions: 4-space indentation
|
||||
- Line length varies, no strict limit enforced
|
||||
- Blank lines used to separate logical sections within methods
|
||||
|
||||
**Linting:**
|
||||
- No detected linting configuration (.pylintrc, .flake8, etc.)
|
||||
- Code follows general PEP 8 style conventions
|
||||
- Minimal deviation from standard Python conventions
|
||||
|
||||
**Class Structure Pattern:**
|
||||
All node classes follow a consistent structure:
|
||||
```python
|
||||
class StringNodeName:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"parameter_name": ("TYPE", {"configuration": "dict"})
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",) # or ("BOOLEAN", "STRING") etc.
|
||||
RETURN_NAMES = ("return_name",)
|
||||
FUNCTION = "method_name"
|
||||
CATEGORY = "utils/StringEssentials"
|
||||
|
||||
def method_name(self, parameters):
|
||||
# Implementation
|
||||
return (result,) # Always return tuple
|
||||
```
|
||||
|
||||
## Import Organization
|
||||
|
||||
**Order:**
|
||||
1. Standard library imports (`import unittest`, `import re`)
|
||||
2. Class imports from local modules (e.g., `from string_strip_node import StringStripNode`)
|
||||
|
||||
**Path Aliases:**
|
||||
- No path aliases detected or used
|
||||
|
||||
**Module Organization:**
|
||||
- `__init__.py` at `/home/mark/Code/ComfyUI_StringEssentials/__init__.py` imports all node classes
|
||||
- NODE_CLASS_MAPPINGS and NODE_DISPLAY_NAME_MAPPINGS provide ComfyUI registry
|
||||
- All modules use relative imports in `__init__.py`
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Patterns:**
|
||||
- Input validation through conditional checks (if not line: continue)
|
||||
- Empty input handling: return default empty/false values (e.g., `return (False, "")`)
|
||||
- No explicit try-catch blocks; relies on Python built-ins for error prevention
|
||||
- String operations wrapped in safe calls (e.g., `.strip()`, `.split()`)
|
||||
- All methods handle empty strings and whitespace gracefully
|
||||
|
||||
**Example from `string_contains_any_node.py`:**
|
||||
```python
|
||||
if not substrings_list:
|
||||
return (False, "")
|
||||
|
||||
# Prepare strings for comparison
|
||||
search_string = input_string if match_case else input_string.lower()
|
||||
```
|
||||
|
||||
## Logging
|
||||
|
||||
**Framework:** Not detected - no logging module used in codebase
|
||||
|
||||
**Console Output:**
|
||||
- No console logging throughout production code
|
||||
- Tests use unittest assertions for verification
|
||||
- No debug output or print statements in production code
|
||||
|
||||
## Comments
|
||||
|
||||
**When to Comment:**
|
||||
- Comments reference issue numbers for fixes (e.g., "# Issue #6 fix", "# Test Issue #6", "# Issue #8")
|
||||
- Comments explain regex patterns and matching behavior (e.g., "Remove spaces before punctuation", "Normalize multiple spaces")
|
||||
- Docstrings minimal; only used in `StringContainsAnyNode` with class-level docstring
|
||||
|
||||
**JSDoc/TSDoc:**
|
||||
- Not applicable - Python codebase without type hints
|
||||
- Class methods have minimal/no docstrings
|
||||
|
||||
## Function Design
|
||||
|
||||
**Size:**
|
||||
- Small to medium functions (methods range 10-50 lines)
|
||||
- Core logic methods typically 30-50 lines: `string_strip`, `string_replace`, `conditional_append`
|
||||
- Helper methods compact: `cleanup_text` is 3 lines
|
||||
|
||||
**Parameters:**
|
||||
- Input parameters correspond directly to INPUT_TYPES definitions
|
||||
- Boolean flags for feature toggling common (e.g., `match_case`, `preserve_punctuation`)
|
||||
- String inputs often use multiline format for flexibility
|
||||
|
||||
**Return Values:**
|
||||
- Always return tuples, matching RETURN_TYPES specification
|
||||
- Single return: `(result,)`
|
||||
- Multiple returns: `(result, boolean_flag)`
|
||||
- Empty returns use falsy values: `(False, "")` or `(input_string, False)`
|
||||
|
||||
## Module Design
|
||||
|
||||
**Exports:**
|
||||
- `__init__.py` at `/home/mark/Code/ComfyUI_StringEssentials/__init__.py` exports all node classes
|
||||
- NODE_CLASS_MAPPINGS dict maps string names to classes for ComfyUI registration
|
||||
- NODE_DISPLAY_NAME_MAPPINGS provides UI-friendly names
|
||||
|
||||
**Barrel Files:**
|
||||
- Yes, `__init__.py` acts as barrel file with explicit imports
|
||||
- Each node class imported individually
|
||||
- WEB_DIRECTORY constant exported for ComfyUI
|
||||
|
||||
**Shared Utilities:**
|
||||
- `cleanup_text()` method duplicated in both `StringStripNode` and `StringMultiReplaceNode` (not DRY, but isolated to context)
|
||||
- No separate utils module; all logic encapsulated in node classes
|
||||
|
||||
## Common Patterns
|
||||
|
||||
**Regex Pattern Compilation:**
|
||||
Patterns compiled once in a loop for performance:
|
||||
```python
|
||||
# From string_strip_node.py and string_multi_replace_node.py
|
||||
patterns = []
|
||||
for _, string_to_remove in removals:
|
||||
# Build pattern
|
||||
pattern = ...
|
||||
patterns.append(re.compile(pattern, flags=flags))
|
||||
|
||||
# Apply all patterns
|
||||
for pattern in patterns:
|
||||
result = pattern.sub('', result)
|
||||
```
|
||||
|
||||
**String Parsing (Multiline Input):**
|
||||
Common pattern for parsing user input:
|
||||
```python
|
||||
strings_list = []
|
||||
for line in strings_to_remove.splitlines():
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
strings_list.append(line)
|
||||
```
|
||||
|
||||
**Case Sensitivity Handling:**
|
||||
Consistent approach across nodes:
|
||||
```python
|
||||
flags = 0 if match_case else re.IGNORECASE
|
||||
regex = re.compile(pattern, flags=flags)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Convention analysis: 2026-03-03*
|
||||
@@ -0,0 +1,81 @@
|
||||
# External Integrations
|
||||
|
||||
**Analysis Date:** 2026-03-03
|
||||
|
||||
## APIs & External Services
|
||||
|
||||
**LLM Integration:**
|
||||
- Compatible with outputs from Ollama, Claude.ai, OpenAI ChatGPT
|
||||
- No direct SDK/client integration - processes text output from LLM services
|
||||
- Purpose: Clean and transform LLM-generated prompts and descriptions
|
||||
|
||||
## Data Storage
|
||||
|
||||
**Databases:**
|
||||
- Not applicable - Nodes process in-memory strings only, no persistent storage
|
||||
|
||||
**File Storage:**
|
||||
- Local filesystem only - Custom node installed in ComfyUI custom_nodes directory
|
||||
- No external file storage integrations
|
||||
|
||||
**Caching:**
|
||||
- None - All operations are stateless transformations
|
||||
|
||||
## Authentication & Identity
|
||||
|
||||
**Auth Provider:**
|
||||
- Not applicable - Standalone utility nodes with no authentication requirements
|
||||
- Custom node registration managed by ComfyUI framework
|
||||
|
||||
## Monitoring & Observability
|
||||
|
||||
**Error Tracking:**
|
||||
- None - Uses ComfyUI's native error handling and logging
|
||||
|
||||
**Logs:**
|
||||
- ComfyUI application logs
|
||||
- No external log aggregation
|
||||
|
||||
## CI/CD & Deployment
|
||||
|
||||
**Hosting:**
|
||||
- GitHub repository: https://github.com/bradsec/ComfyUI_StringEssentials
|
||||
- ComfyUI Registry: Registered via ComfyUI package registry (PublisherId: bradsec)
|
||||
|
||||
**CI Pipeline:**
|
||||
- GitHub Actions workflow: `.github/workflows/publish.yml`
|
||||
- Automated publishing to ComfyUI registry on release
|
||||
|
||||
## Environment Configuration
|
||||
|
||||
**Required env vars:**
|
||||
- None - Zero environment variable dependencies
|
||||
|
||||
**Secrets location:**
|
||||
- Not applicable - No secrets or credentials used
|
||||
|
||||
## Webhooks & Callbacks
|
||||
|
||||
**Incoming:**
|
||||
- None
|
||||
|
||||
**Outgoing:**
|
||||
- None
|
||||
|
||||
## Frontend Integration
|
||||
|
||||
**ComfyUI Web Interface:**
|
||||
- JavaScript extensions in `js/string_essentials.js`
|
||||
- Custom node widget initialization via ComfyUI's extension API
|
||||
- Widget API: `ComfyWidgets` for STRING input display
|
||||
- App API: `app.registerExtension()` for node lifecycle hooks
|
||||
|
||||
**Node Widget Configuration:**
|
||||
- Multiline text inputs for string configuration
|
||||
- Boolean toggles for matching options (case-sensitive, whole word, etc.)
|
||||
- Dropdown selections for position (beginning/end)
|
||||
- Custom separators configurable via STRING inputs
|
||||
|
||||
---
|
||||
|
||||
*Integration audit: 2026-03-03*
|
||||
@@ -0,0 +1,79 @@
|
||||
# Technology Stack
|
||||
|
||||
**Analysis Date:** 2026-03-03
|
||||
|
||||
## Languages
|
||||
|
||||
**Primary:**
|
||||
- Python 3.x - Core node implementation and string manipulation logic
|
||||
- JavaScript - Frontend UI extensions and ComfyUI widget integration
|
||||
|
||||
## Runtime
|
||||
|
||||
**Environment:**
|
||||
- ComfyUI - Custom node framework for AI image generation workflows
|
||||
|
||||
**Package Manager:**
|
||||
- pip - Python dependency management
|
||||
- No Python dependencies or external modules required (pure Python implementation)
|
||||
|
||||
## Frameworks
|
||||
|
||||
**Core:**
|
||||
- ComfyUI Custom Nodes Framework - Node registration and execution
|
||||
- Uses `NODE_CLASS_MAPPINGS` and `NODE_DISPLAY_NAME_MAPPINGS` for node registration
|
||||
- Located: `__init__.py`
|
||||
|
||||
**Testing:**
|
||||
- unittest - Python standard library testing framework
|
||||
- Config: No external test configuration file (uses unittest defaults)
|
||||
- Test file: `test_string_nodes.py`
|
||||
|
||||
**Build/Dev:**
|
||||
- No formal build system required
|
||||
- Direct Python module installation into ComfyUI custom_nodes directory
|
||||
|
||||
## Key Dependencies
|
||||
|
||||
**Critical:**
|
||||
- None - This is a zero-dependency ComfyUI custom node package
|
||||
- All functionality implemented using Python standard library only (re module for regex)
|
||||
|
||||
**Infrastructure:**
|
||||
- None - Standalone custom node implementation
|
||||
|
||||
## Configuration
|
||||
|
||||
**Environment:**
|
||||
- No environment variables required
|
||||
- No configuration files needed
|
||||
- Simple cloning into ComfyUI custom_nodes directory
|
||||
|
||||
**Build:**
|
||||
- `pyproject.toml` - Package metadata and ComfyUI registry information
|
||||
- Version: 2.0.7
|
||||
- Publisher: bradsec (ComfyUI Registry identifier)
|
||||
- License: Included in LICENSE file
|
||||
|
||||
## Platform Requirements
|
||||
|
||||
**Development:**
|
||||
- Python 3.x
|
||||
- ComfyUI installation (as parent application)
|
||||
- Git for repository cloning
|
||||
|
||||
**Production:**
|
||||
- ComfyUI application
|
||||
- Python 3.x runtime
|
||||
- Custom node installed in `custom_nodes/ComfyUI_StringEssentials/` directory
|
||||
|
||||
## Platform Support
|
||||
|
||||
**Deployment Target:**
|
||||
- ComfyUI custom nodes directory
|
||||
- Cross-platform (Windows, macOS, Linux)
|
||||
- Web-based ComfyUI interface with JavaScript frontend integration
|
||||
|
||||
---
|
||||
|
||||
*Stack analysis: 2026-03-03*
|
||||
@@ -0,0 +1,207 @@
|
||||
# Codebase Structure
|
||||
|
||||
**Analysis Date:** 2026-03-03
|
||||
|
||||
## Directory Layout
|
||||
|
||||
```
|
||||
ComfyUI_StringEssentials/
|
||||
├── __init__.py # Node registry and exports
|
||||
├── pyproject.toml # Project metadata and configuration
|
||||
├── README.md # Documentation
|
||||
├── LICENSE # License file
|
||||
├── string_textbox_node.py # String Textbox node
|
||||
├── string_strip_node.py # String Strip node
|
||||
├── string_multi_replace_node.py # String Multi Replace node
|
||||
├── string_preview_node.py # String Preview node
|
||||
├── string_conditional_append_node.py # String Conditional Append node
|
||||
├── string_contains_any_node.py # String Contains Any node
|
||||
├── test_string_nodes.py # Unit tests
|
||||
├── .planning/ # Planning and analysis docs (not committed)
|
||||
│ └── codebase/ # Architecture documentation
|
||||
├── .pytest_cache/ # Pytest cache (gitignored)
|
||||
├── js/ # Client-side assets (currently empty)
|
||||
└── images/ # Documentation images
|
||||
```
|
||||
|
||||
## Directory Purposes
|
||||
|
||||
**Root Directory:**
|
||||
- Purpose: Contains all Python node implementations and project config
|
||||
- Contains: Node classes, tests, project metadata
|
||||
- Key files: All `*_node.py` files, `__init__.py`, `pyproject.toml`, `test_string_nodes.py`
|
||||
|
||||
**js/ Directory:**
|
||||
- Purpose: Placeholder for client-side JavaScript/web assets
|
||||
- Contains: Currently empty, referenced in `__init__.py` WEB_DIRECTORY
|
||||
- Committed: Yes (directory exists, may have assets)
|
||||
|
||||
**images/ Directory:**
|
||||
- Purpose: Documentation images for README
|
||||
- Contains: PNG screenshots of node interfaces and example workflows
|
||||
- Committed: Yes
|
||||
|
||||
**.planning/codebase/ Directory:**
|
||||
- Purpose: Architecture and analysis documentation generated by GSD tools
|
||||
- Contains: ARCHITECTURE.md, STRUCTURE.md, CONVENTIONS.md, TESTING.md, etc.
|
||||
- Committed: No (git ignored)
|
||||
|
||||
**.pytest_cache/ Directory:**
|
||||
- Purpose: Pytest runtime cache
|
||||
- Contains: Test execution metadata
|
||||
- Committed: No (git ignored)
|
||||
|
||||
## Key File Locations
|
||||
|
||||
**Entry Points:**
|
||||
- `__init__.py`: Module initialization - imports nodes and exports NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS, WEB_DIRECTORY
|
||||
|
||||
**Node Implementations:**
|
||||
- `string_textbox_node.py`: StringTextboxNode - simple text input passthrough (5 lines of logic)
|
||||
- `string_strip_node.py`: StringStripNode - removes specified strings with regex patterns
|
||||
- `string_multi_replace_node.py`: StringMultiReplaceNode - replaces text using delimiter-separated pairs
|
||||
- `string_preview_node.py`: StringPreviewNode - displays string output in ComfyUI UI
|
||||
- `string_conditional_append_node.py`: StringConditionalAppendNode - appends text only if not present
|
||||
- `string_contains_any_node.py`: StringContainsAnyNode - checks if any of multiple substrings exist
|
||||
|
||||
**Configuration:**
|
||||
- `pyproject.toml`: Project metadata including name, version (2.0.7), description, repository URL
|
||||
|
||||
**Testing:**
|
||||
- `test_string_nodes.py`: 62 unit tests covering all node functionalities
|
||||
|
||||
**Documentation:**
|
||||
- `README.md`: User-facing documentation with node descriptions and usage examples
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
**Files:**
|
||||
- Node files: `string_<operation>_node.py` format
|
||||
- Examples: `string_strip_node.py`, `string_multi_replace_node.py`
|
||||
- Pattern: snake_case with `_node.py` suffix
|
||||
|
||||
**Classes:**
|
||||
- Node classes: PascalCase with "Node" suffix
|
||||
- Examples: `StringTextboxNode`, `StringStripNode`, `StringMultiReplaceNode`
|
||||
- Pattern: Convert filename from snake_case to PascalCase, append "Node"
|
||||
|
||||
**Methods:**
|
||||
- Execution methods: verb_noun_v1 format (snake_case)
|
||||
- Examples: `string_strip()`, `string_replace()`, `conditional_append()`, `contains_any()`, `pass_string()`, `preview_string()`
|
||||
- Pattern: Descriptive snake_case names indicating operation
|
||||
|
||||
**Variables:**
|
||||
- Instance variables: snake_case with descriptive names
|
||||
- Examples: `input_string`, `strings_to_remove`, `match_case`, `preserve_punctuation`
|
||||
- Pattern: Clear names indicating type and purpose
|
||||
|
||||
**Constants:**
|
||||
- Node metadata: UPPERCASE
|
||||
- Examples: `RETURN_TYPES`, `RETURN_NAMES`, `FUNCTION`, `CATEGORY`, `INPUT_TYPES`, `OUTPUT_NODE`
|
||||
- Pattern: ComfyUI convention for node interface
|
||||
|
||||
## Where to Add New Code
|
||||
|
||||
**New String Operation Node:**
|
||||
1. Create file: `string_<operation>_node.py` in root directory
|
||||
2. Class structure (follow pattern from existing nodes):
|
||||
- Define `INPUT_TYPES()` classmethod with parameter definitions
|
||||
- Define `RETURN_TYPES` tuple with output types
|
||||
- Define `RETURN_NAMES` tuple with output names
|
||||
- Set `FUNCTION = "<method_name>"`
|
||||
- Set `CATEGORY = "utils/StringEssentials"`
|
||||
- Implement execution method with logic
|
||||
3. Add to `__init__.py`:
|
||||
- Import the new class at top
|
||||
- Add entry to `NODE_CLASS_MAPPINGS` dict (key = class name as string)
|
||||
- Add entry to `NODE_DISPLAY_NAME_MAPPINGS` dict (key = class name, value = display name)
|
||||
4. Add tests to `test_string_nodes.py`:
|
||||
- Create test class or add methods to `TestStringNodes`
|
||||
- Test basic functionality, edge cases, parameter variations
|
||||
|
||||
**Example node implementation pattern from `string_strip_node.py`:**
|
||||
```python
|
||||
import re
|
||||
|
||||
class StringStripNode:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"input_string": ("STRING", {...}),
|
||||
"strings_to_remove": ("STRING", {...}),
|
||||
"match_case": ("BOOLEAN", {...}),
|
||||
# ... more parameters
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("modified_string",)
|
||||
FUNCTION = "string_strip"
|
||||
CATEGORY = "utils/StringEssentials"
|
||||
|
||||
def cleanup_text(self, text):
|
||||
# Helper method for text normalization
|
||||
pass
|
||||
|
||||
def string_strip(self, input_string, strings_to_remove, ...):
|
||||
# Parse input (splitlines, strip, filter empty)
|
||||
# Compile regex patterns
|
||||
# Apply patterns
|
||||
# Cleanup if needed
|
||||
return (result,)
|
||||
```
|
||||
|
||||
**Utilities/Helpers:**
|
||||
- Shared helper methods: Add to individual node class if only used there
|
||||
- Common patterns: Currently duplicated in `cleanup_text()` across nodes (consider extraction if adding more nodes)
|
||||
- Location: Keep at root level - no separate utils directory used
|
||||
|
||||
**Tests:**
|
||||
- Add test methods to `TestStringNodes` class in `test_string_nodes.py`
|
||||
- Follow naming: `test_<node>_<scenario>` (e.g., `test_strip_basic_functionality`)
|
||||
- Setup: Use `self.setUp()` to instantiate node classes
|
||||
- Pattern: Call node method directly with test parameters, assert expected results
|
||||
|
||||
## Special Directories
|
||||
|
||||
**js/ Directory:**
|
||||
- Purpose: ComfyUI web assets (referenced in WEB_DIRECTORY in `__init__.py`)
|
||||
- Generated: No
|
||||
- Committed: Yes (directory exists, may be empty or contain future web assets)
|
||||
- Note: Currently unused but available for client-side node customization
|
||||
|
||||
**images/ Directory:**
|
||||
- Purpose: Screenshots and diagrams for documentation
|
||||
- Generated: No (manually created/updated)
|
||||
- Committed: Yes
|
||||
- Files: PNG images referenced in README.md
|
||||
|
||||
**.planning/codebase/ Directory:**
|
||||
- Purpose: GSD (Get Shit Done) architecture analysis documents
|
||||
- Generated: Yes (by GSD tools)
|
||||
- Committed: No (git ignored)
|
||||
- Contains: ARCHITECTURE.md, STRUCTURE.md, CONVENTIONS.md, TESTING.md, CONCERNS.md
|
||||
|
||||
**.pytest_cache/ Directory:**
|
||||
- Purpose: Pytest internal cache for test discovery and execution
|
||||
- Generated: Yes (auto-created by pytest)
|
||||
- Committed: No (git ignored)
|
||||
|
||||
## File Organization Rationale
|
||||
|
||||
**Flat root structure for nodes:** Each node is a separate file at root level
|
||||
- Rationale: Simple discovery by ComfyUI, clear 1:1 mapping between file and node class
|
||||
- Alternative avoided: Subdirectory organization (would complicate imports in `__init__.py`)
|
||||
|
||||
**Single test file:** All tests in `test_string_nodes.py`
|
||||
- Rationale: Small codebase, all nodes similar in nature, easy to run all tests together
|
||||
- Scalability: If adding more nodes, consider splitting tests by node when >100 test methods
|
||||
|
||||
**Shared patterns duplicated:** `cleanup_text()` method exists in both `string_strip_node.py` and `string_multi_replace_node.py`
|
||||
- Current state: Intentionally duplicated to keep each node self-contained
|
||||
- Future improvement: Extract to shared module if >3 nodes need it
|
||||
|
||||
---
|
||||
|
||||
*Structure analysis: 2026-03-03*
|
||||
@@ -0,0 +1,251 @@
|
||||
# Testing Patterns
|
||||
|
||||
**Analysis Date:** 2026-03-03
|
||||
|
||||
## Test Framework
|
||||
|
||||
**Runner:**
|
||||
- unittest (Python standard library)
|
||||
- Config: None (uses default unittest discovery)
|
||||
- No pytest.ini, setup.cfg, or tox.ini configuration detected
|
||||
|
||||
**Assertion Library:**
|
||||
- unittest.TestCase assertions (built-in)
|
||||
|
||||
**Run Commands:**
|
||||
```bash
|
||||
python -m unittest test_string_nodes.py # Run all tests
|
||||
python -m unittest test_string_nodes.TestStringNodes # Run specific test class
|
||||
python -m unittest test_string_nodes.TestStringNodes.test_<name> # Run specific test
|
||||
```
|
||||
|
||||
**Test File Location:**
|
||||
- `test_string_nodes.py` in project root alongside source files (`/home/mark/Code/ComfyUI_StringEssentials/test_string_nodes.py`)
|
||||
- Co-located with source code (not in separate tests directory)
|
||||
|
||||
## Test File Organization
|
||||
|
||||
**Location:**
|
||||
- Co-located pattern: test file in same directory as source modules
|
||||
|
||||
**Naming:**
|
||||
- Test file: `test_string_nodes.py`
|
||||
- Test class: `TestStringNodes` (PascalCase with Test prefix)
|
||||
- Test methods: `test_<functionality>` format
|
||||
|
||||
**Structure:**
|
||||
```
|
||||
test_string_nodes.py
|
||||
├── TestStringNodes (class)
|
||||
│ ├── setUp() - Initialize node instances
|
||||
│ ├── test_strip_* - Tests for StringStripNode
|
||||
│ ├── test_replace_* - Tests for StringMultiReplaceNode
|
||||
│ ├── test_append_* - Tests for StringConditionalAppendNode
|
||||
│ └── test_contains_any_* - Tests for StringContainsAnyNode
|
||||
└── if __name__ == '__main__': unittest.main()
|
||||
```
|
||||
|
||||
## Test Structure
|
||||
|
||||
**Suite Organization:**
|
||||
```python
|
||||
class TestStringNodes(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.strip_node = StringStripNode()
|
||||
self.replace_node = StringMultiReplaceNode()
|
||||
self.append_node = StringConditionalAppendNode()
|
||||
self.contains_any_node = StringContainsAnyNode()
|
||||
```
|
||||
|
||||
**Patterns:**
|
||||
- **Setup pattern:** `setUp()` method instantiates all node instances as instance variables for reuse across test methods
|
||||
- **No teardown:** No `tearDown()` method (no cleanup needed)
|
||||
- **Assertion pattern:** Direct method calls on node instances with parameter unpacking:
|
||||
```python
|
||||
result = self.strip_node.string_strip(
|
||||
input_string=input_string,
|
||||
strings_to_remove=strings_to_remove,
|
||||
match_case=False,
|
||||
match_whole_string=True,
|
||||
preserve_punctuation=True,
|
||||
remove_extra_spaces=True
|
||||
)[0]
|
||||
self.assertEqual(result, expected_output)
|
||||
```
|
||||
|
||||
**Test Method Characteristics:**
|
||||
- Each test is independent and focused on a single behavior
|
||||
- Tests use explicit parameter names (keyword arguments) for clarity
|
||||
- Return value tuple unpacking: `[0]` for single return, tuple unpacking for multiple returns
|
||||
- Clear arrange-act-assert flow (input setup → method call → assertion)
|
||||
|
||||
## Test Coverage by Node
|
||||
|
||||
**StringStripNode Tests:**
|
||||
- `test_strip_basic_functionality`: Single string removal
|
||||
- `test_strip_multiple_strings`: Multiple removal patterns (multiline input)
|
||||
- `test_strip_case_sensitivity`: Case-insensitive vs case-sensitive modes
|
||||
- `test_strip_punctuation_preserved`: Punctuation preservation (Issue #6)
|
||||
- `test_strip_punctuation_removed`: Legacy punctuation removal behavior
|
||||
- Extra spaces handling covered in `test_extra_spaces_handling`
|
||||
|
||||
**StringMultiReplaceNode Tests:**
|
||||
- `test_replace_basic_functionality`: Single replacement pair
|
||||
- `test_replace_multiple_pairs`: Multiple replacement pairs from multiline input
|
||||
- `test_replace_case_sensitivity`: Case-insensitive vs case-sensitive modes
|
||||
- `test_replace_custom_delimiter`: Custom delimiter support (not just `::`
|
||||
- `test_replace_punctuation_preserved`: Punctuation preservation (Issue #6)
|
||||
- `test_replace_prompt_syntax_preserved`: Bracket syntax preservation (e.g., `[word1:word2:.5]`)
|
||||
- `test_replace_with_space`: Trailing space preservation in replacements (Issue #8)
|
||||
- `test_replace_with_leading_space`: Leading space in replacement strings (Issue #8)
|
||||
- `test_replace_with_whitespace_only`: Multiple spaces in replacements (Issue #8)
|
||||
- `test_replace_with_tab`: Tab character in replacements (Issue #8)
|
||||
|
||||
**StringConditionalAppendNode Tests:**
|
||||
- `test_append_string_not_found`: Appending when string not present
|
||||
- `test_append_string_found`: No append when string already present
|
||||
- `test_append_beginning`: Position flag "beginning" vs "end"
|
||||
- `test_append_case_insensitive`: Case-insensitive search
|
||||
- `test_append_case_sensitive`: Case-sensitive search
|
||||
- `test_append_custom_separator`: Custom separator handling
|
||||
- `test_append_no_separator`: Empty string separator
|
||||
- `test_append_multiple_strings`: Multiple check strings with mixed found/not found
|
||||
- `test_append_multiple_all_found`: All strings already present (no append)
|
||||
|
||||
**StringContainsAnyNode Tests:**
|
||||
- `test_contains_any_found`: Basic found case
|
||||
- `test_contains_any_not_found`: Basic not found case
|
||||
- `test_contains_any_multiple_first_match`: Returns first match from multiple options
|
||||
- `test_contains_any_case_insensitive`: Default case-insensitive behavior
|
||||
- `test_contains_any_case_sensitive`: Case-sensitive search
|
||||
- `test_contains_any_case_sensitive_found`: Case-sensitive with matching case
|
||||
- `test_contains_any_empty_input`: Empty input string handling
|
||||
- `test_contains_any_empty_substrings`: Empty substrings input
|
||||
- `test_contains_any_whitespace_substrings`: Whitespace-only lines are ignored
|
||||
- `test_contains_any_partial_match`: Substring matching (anime matches animation)
|
||||
|
||||
## Mocking
|
||||
|
||||
**Framework:** None - unittest provides basic mocking but not used in this codebase
|
||||
|
||||
**Patterns:**
|
||||
- No mock objects used
|
||||
- No external dependencies mocked
|
||||
- Tests use real node instances with direct method calls
|
||||
- All test data is embedded in test methods (no external fixtures)
|
||||
|
||||
**What to Mock:**
|
||||
- No mocking required; all node classes are stateless and dependency-free
|
||||
|
||||
**What NOT to Mock:**
|
||||
- Don't mock the node classes - test them directly with real instances
|
||||
|
||||
## Fixtures and Factories
|
||||
|
||||
**Test Data:**
|
||||
- All test data embedded directly in test methods
|
||||
- No separate fixture files or factory classes
|
||||
- Input strings created inline:
|
||||
```python
|
||||
input_string = "This is a test string with test word"
|
||||
strings_to_remove = "test"
|
||||
```
|
||||
|
||||
**Location:**
|
||||
- Test data is co-located with test methods (no separate fixtures directory)
|
||||
- Issue-specific test data clearly labeled with issue references:
|
||||
```python
|
||||
def test_strip_punctuation_preserved(self):
|
||||
# Test Issue #6 fix: punctuation should be preserved
|
||||
input_string = "Let's meet, Grandma!"
|
||||
```
|
||||
|
||||
## Coverage
|
||||
|
||||
**Requirements:** None enforced (no coverage configuration detected)
|
||||
|
||||
**View Coverage:**
|
||||
```bash
|
||||
# Using unittest's built-in capabilities (limited):
|
||||
python -m unittest discover -v
|
||||
|
||||
# Better coverage with coverage.py (not currently installed):
|
||||
pip install coverage
|
||||
coverage run -m unittest test_string_nodes.py
|
||||
coverage report
|
||||
```
|
||||
|
||||
**Observed Coverage:**
|
||||
- **StringStripNode:** 100% - All major code paths tested (basic, multiple, case sensitivity, punctuation handling, spaces)
|
||||
- **StringMultiReplaceNode:** 100% - All code paths tested (basic, multiple, case sensitivity, custom delimiter, punctuation, whitespace preservation)
|
||||
- **StringConditionalAppendNode:** 100% - All code paths tested (found/not found, position, case sensitivity, separator options, multiple strings)
|
||||
- **StringContainsAnyNode:** 100% - All code paths tested (found/not found, case sensitivity, empty inputs, whitespace handling, partial matching)
|
||||
- **StringPreviewNode:** 0% - No tests (trivial pass-through logic)
|
||||
- **StringTextboxNode:** 0% - No tests (trivial pass-through logic)
|
||||
|
||||
## Test Types
|
||||
|
||||
**Unit Tests:**
|
||||
- Scope: Individual node class methods
|
||||
- Approach: Direct method invocation with arranged inputs, assert on single output
|
||||
- Example: `test_strip_basic_functionality` tests `string_strip()` method with specific parameters
|
||||
- No test doubles or stubs - real objects used
|
||||
|
||||
**Integration Tests:**
|
||||
- Not present in codebase (no multi-component interaction testing)
|
||||
|
||||
**E2E Tests:**
|
||||
- Not present (ComfyUI-specific E2E would require full ComfyUI runtime)
|
||||
|
||||
**Issue-Driven Tests:**
|
||||
- Tests explicitly tied to GitHub issues in comments
|
||||
- Issue #6: Punctuation preservation tests
|
||||
- Issue #8: Whitespace preservation in replacements tests
|
||||
- Issue #9: StringContainsAnyNode feature tests
|
||||
- Pattern: When fixing a bug, issue number added as comment and test created to verify fix
|
||||
|
||||
## Common Patterns
|
||||
|
||||
**Async Testing:**
|
||||
- Not applicable - Python standard library code without async/await
|
||||
|
||||
**Error Testing:**
|
||||
- No explicit error/exception testing
|
||||
- Edge cases tested: empty strings, whitespace-only input, multiple spaces
|
||||
- Validation through return values rather than exception handling
|
||||
|
||||
**Testing Multiline Input:**
|
||||
```python
|
||||
strings_to_remove = """test
|
||||
More Text"""
|
||||
result = self.strip_node.string_strip(
|
||||
input_string=input_string,
|
||||
strings_to_remove=strings_to_remove,
|
||||
match_case=False,
|
||||
match_whole_string=True,
|
||||
preserve_punctuation=True,
|
||||
remove_extra_spaces=True
|
||||
)[0]
|
||||
```
|
||||
|
||||
**Testing Multiple Parameters:**
|
||||
```python
|
||||
result, was_appended = self.append_node.conditional_append(
|
||||
input_string=input_string,
|
||||
strings_to_check=strings_to_check,
|
||||
position="end",
|
||||
match_case=False,
|
||||
separator=", "
|
||||
)
|
||||
self.assertEqual(result, expected_string)
|
||||
self.assertTrue(was_appended)
|
||||
```
|
||||
|
||||
**Testing Boolean Returns:**
|
||||
- StringConditionalAppendNode returns `(string, boolean)` tuple
|
||||
- StringContainsAnyNode returns `(boolean, string)` tuple
|
||||
- Tests unpack both return values and assert on each
|
||||
|
||||
---
|
||||
|
||||
*Testing analysis: 2026-03-03*
|
||||
Reference in New Issue
Block a user