Author SHA1 Message Date
David Vincent Fischer 5e4e471aaf Merge pull request #32 from theUpsider/copilot/fix-31
Add Copilot setup workflow for Python-based ComfyUI extension
2025-09-15 16:44:04 +02:00
copilot-swe-agent[bot]andtheUpsider 4454dc6740 Add Copilot setup workflow for Python-based ComfyUI extension
Co-authored-by: theUpsider <25013640+theUpsider@users.noreply.github.com>
2025-09-15 14:36:50 +00:00
David Vincent Fischer 5b5787b37c Merge pull request #30 from theUpsider/copilot/fix-29
Fix CSV loader to always return dict instead of list on invalid input
2025-09-15 16:35:29 +02:00
David Vincent Fischer 183f62e6e4 Merge pull request #33 from theUpsider/theUpsider-patch-2
Update issue templates
2025-09-15 16:34:20 +02:00
David Vincent Fischer a99ae9bb6d Update issue templates 2025-09-15 16:34:11 +02:00
copilot-swe-agent[bot] 5fbb3870c0 Initial plan 2025-09-15 14:32:25 +00:00
copilot-swe-agent[bot]andtheUpsider 71edaa7cc5 Fix CSV loader to always return dict, update test expectations
Co-authored-by: theUpsider <25013640+theUpsider@users.noreply.github.com>
2025-09-15 14:29:18 +00:00
copilot-swe-agent[bot] aa89d00744 Initial plan 2025-09-15 14:25:46 +00:00
David Vincent Fischer 17b7f91e8f Delete issues/1.md 2025-09-15 16:24:25 +02:00
David Vincent Fischer ec639b22f2 Create issue for failing test in loader 2025-09-15 16:24:01 +02:00
David Vincent Fischer 69a466ec65 Change default branch from 'main' to 'master' 2025-09-15 16:20:46 +02:00
David Vincent Fischer f04c04d786 Merge pull request #28 from theUpsider/theUpsider-patch-1
Change branch name from 'main' to 'master'
2025-09-15 16:19:55 +02:00
David Vincent Fischer 75b8dae0d1 Change branch name from 'main' to 'master' 2025-09-15 16:18:38 +02:00
David Vincent Fischer 15bbf31498 Merge pull request #27 from theUpsider/copilot/fix-26
Add comprehensive GitHub Copilot instructions for development workflow
2025-09-15 16:17:04 +02:00
David Vincent FischerandCopilot 163d0937c3 Apply suggestion from @Copilot
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-15 16:16:29 +02:00
copilot-swe-agent[bot]andtheUpsider 51b78330a5 Update copilot instructions to use new pytest testing framework
Co-authored-by: theUpsider <25013640+theUpsider@users.noreply.github.com>
2025-09-15 14:15:15 +00:00
David Vincent Fischer ff7cc77126 Merge pull request #25 from theUpsider/copilot/fix-24
Implement Python test framework with pytest, example CSV files, and CI/CD
2025-09-15 16:09:45 +02:00
David Vincent FischerandCopilot 572be148d4 Update __init__.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-15 16:09:33 +02:00
David Vincent FischerandCopilot ed4822f2eb Update tests/test_styles_csv_loader.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-15 16:08:56 +02:00
copilot-swe-agent[bot]andtheUpsider cca6556218 Bump version to 1.1.0 for test framework release
Co-authored-by: theUpsider <25013640+theUpsider@users.noreply.github.com>
2025-09-15 14:06:12 +00:00
David Vincent Fischer 89ba5f4b11 Merge pull request #23 from theUpsider/copilot/fix-3
Fix cross-platform path handling for Linux compatibility
2025-09-15 15:59:03 +02:00
copilot-swe-agent[bot]andtheUpsider 6e4d7c29af Implement complete Python test framework with pytest, example CSV files, and CI
Co-authored-by: theUpsider <25013640+theUpsider@users.noreply.github.com>
2025-09-15 13:57:33 +00:00
copilot-swe-agent[bot]andtheUpsider eaf8d761ee Add comprehensive GitHub Copilot instructions for ComfyUI-Styles_CSV_Loader
Co-authored-by: theUpsider <25013640+theUpsider@users.noreply.github.com>
2025-09-15 13:56:26 +00:00
copilot-swe-agent[bot]andtheUpsider e60f809ba8 Fix cross-platform path handling with os.path.normpath
Co-authored-by: theUpsider <25013640+theUpsider@users.noreply.github.com>
2025-09-15 13:53:14 +00:00
copilot-swe-agent[bot] 89b042c894 Initial plan 2025-09-15 13:51:47 +00:00
copilot-swe-agent[bot] c6b2805a2a Initial plan 2025-09-15 13:51:06 +00:00
copilot-swe-agent[bot] 66722bfd35 Initial plan 2025-09-15 13:47:06 +00:00
David Vincent Fischer 75ae517cde Update pyproject.toml 2025-05-16 13:01:23 +02:00
David Vincent Fischer c947e7dd88 Merge pull request #20 from theUpsider/undo-breaking-changes
reverting old changes
2025-05-16 11:08:59 +02:00
David Fischer 6d827d968a reverting old changes 2025-05-16 11:08:08 +02:00
David Vincent Fischer e346fd764c Merge pull request #18 from theUpsider/csv-header-columns
csv loader error handeling
2025-05-15 16:12:47 +02:00
17 changed files with 615 additions and 66 deletions
+38
View File
@@ -0,0 +1,38 @@
---
name: Bug report
about: Create a report to help us improve
title: "[Bug] ..."
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
+20
View File
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[Feature] ..."
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
+137
View File
@@ -0,0 +1,137 @@
# ComfyUI Styles CSV Loader Extension
This is a Python extension/plugin for ComfyUI that loads style prompts from CSV files, primarily for migration from Automatic1111 Stable Diffusion WebUI.
Always reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.
## Working Effectively
### Repository Structure
- **`styles_csv_loader.py`** - Main extension code containing the StylesCSVLoader class
- **`__init__.py`** - Module initialization that imports the main class
- **`pyproject.toml`** - Project metadata for Comfy Registry publishing
- **`README.md`** - User documentation
- **`run_tests.py`** - Test runner script using pytest framework
- **`tests/`** - Comprehensive test suite with fixtures and unit tests
- **`.github/workflows/publish.yml`** - Automated publishing to Comfy Registry
### Prerequisites and Dependencies
- This extension requires ComfyUI to be installed and running
- No additional Python packages need to be installed beyond ComfyUI's dependencies
- The extension depends on ComfyUI's `folder_paths` module for file path resolution
- Python 3.6+ is required (follows ComfyUI requirements)
### Development Workflow
- **No build process required** - This is a pure Python plugin that loads directly into ComfyUI
- **Automated testing framework** - Uses pytest with comprehensive test coverage
- **No linting configuration** - Follow Python PEP 8 standards manually
- Always validate Python syntax after making changes: `python3 -c "import ast; ast.parse(open('styles_csv_loader.py').read())"`
- Always validate init file syntax: `python3 -c "import ast; ast.parse(open('__init__.py').read())"`
### Testing and Validation
- **Run the test suite**: Use the automated testing framework for comprehensive validation:
```bash
# Run all tests
python run_tests.py
# Run tests with coverage reporting
python run_tests.py --coverage
```
- **Test Structure**: The test suite includes:
- Unit tests for CSV parsing logic
- Tests for various CSV formats (valid, invalid, complex)
- Integration tests for ComfyUI node functionality
- Test fixtures with sample CSV files
- **Syntax Validation**: Always run before committing changes:
```bash
python3 -c "import ast; ast.parse(open('styles_csv_loader.py').read()); print('styles_csv_loader.py syntax valid')"
python3 -c "import ast; ast.parse(open('__init__.py').read()); print('__init__.py syntax valid')"
```
### CSV File Format Requirements
- Must be named `styles.csv` and located in ComfyUI root directory (where `main.py` is)
- Format: `style_name,positive_prompt,negative_prompt`
- First row is header (ignored)
- Supports quoted fields with commas inside quotes
- Example valid CSV:
```csv
style_name,positive_prompt,negative_prompt
cinematic,cinematic lighting professional photography,low quality blurry
vintage,"old photo sepia tone, vintage style",modern digital
```
## Common Tasks
### Making Code Changes
1. Edit `styles_csv_loader.py` for core functionality changes
2. Run syntax validation: `python3 -c "import ast; ast.parse(open('styles_csv_loader.py').read())"`
3. Run the test suite to validate changes: `python run_tests.py`
4. If changing module structure, also validate `__init__.py`
### Adding New Features
- All new functionality should be added to the `StylesCSVLoader` class
- Follow ComfyUI node conventions:
- `INPUT_TYPES()` classmethod for defining inputs
- `RETURN_TYPES` and `RETURN_NAMES` class attributes
- `execute()` method for main functionality
- `FUNCTION` attribute pointing to execute method
- `CATEGORY` for node organization
### Debugging CSV Issues
- Run the test suite to identify issues: `python run_tests.py`
- Check test fixtures in `tests/fixtures/` for valid CSV examples
- Common issues:
- Unescaped quotes in CSV fields
- Wrong number of columns
- File encoding problems (use UTF-8)
### Release Process
- Update version in `pyproject.toml`
- Commit changes to main branch
- GitHub workflow automatically publishes to Comfy Registry
## File Contents Reference
### Repository Root Structure
```
.
├── .git/
├── .github/
│ └── workflows/
│ └── publish.yml
├── .gitignore
├── LICENSE
├── README.md
├── __init__.py
├── pyproject.toml
├── run_tests.py
├── styles_csv_loader.py
└── tests/
├── __init__.py
├── fixtures/
│ ├── complex_styles.csv
│ ├── invalid_styles.csv
│ └── valid_styles.csv
└── test_styles_csv_loader.py
```
### Key Code Patterns
- CSV parsing regex: `,(?=(?:[^"]*"[^"]*")*[^"]*$)` - handles quoted fields with commas (matches literal double quotes)
- Error handling: Always return default error style on exceptions
- ComfyUI integration: Uses `folder_paths.base_path` for CSV location
- Node registration: `NODE_CLASS_MAPPINGS` and `NODE_DISPLAY_NAME_MAPPINGS`
### Common Error Messages
- "Error loading styles.csv, check the console" - Default fallback style
- "No styles.csv found" - File missing from ComfyUI root
- CSV parsing errors - Usually due to malformed CSV format
## Validation Checklist
Before committing any changes:
- [ ] Run Python syntax validation on all modified .py files
- [ ] Run the test suite: `python run_tests.py`
- [ ] Verify no new dependencies introduced
- [ ] Update version in pyproject.toml if needed
- [ ] Ensure all tests pass
+44
View File
@@ -0,0 +1,44 @@
name: "Copilot Setup Steps"
# Automatically run the setup steps when they are changed to allow for easy validation, and
# allow manual testing through the repository's "Actions" tab
"on":
workflow_dispatch:
push:
paths:
- .github/workflows/copilot-setup-steps.yml
pull_request:
paths:
- .github/workflows/copilot-setup-steps.yml
jobs:
# The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot.
copilot-setup-steps:
runs-on: ubuntu-latest
# Set the permissions to the lowest permissions possible needed for your steps.
# Copilot will be given its own token for its operations.
permissions:
# If you want to clone the repository as part of your setup steps, for example to install dependencies, you'll need the `contents: read` permission. If you don't clone the repository in your setup steps, Copilot will do this for you automatically after the steps complete.
contents: read
# You can define any steps you want, and they will run before the agent starts.
# If you do not check out your code, Copilot will do this for you.
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.8"
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
pip install pytest pytest-cov
- name: Validate Python syntax
run: |
python3 -c "import ast; ast.parse(open('styles_csv_loader.py').read()); print('styles_csv_loader.py syntax valid')"
python3 -c "import ast; ast.parse(open('__init__.py').read()); print('__init__.py syntax valid')"
+1 -1
View File
@@ -3,7 +3,7 @@ on:
workflow_dispatch: workflow_dispatch:
push: push:
branches: branches:
- main - master
paths: paths:
- "pyproject.toml" - "pyproject.toml"
+38
View File
@@ -0,0 +1,38 @@
name: Tests
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.8, 3.9, "3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest pytest-cov
- name: Run tests
run: |
pytest tests/ -v --cov=. --cov-report=xml --cov-report=term-missing
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
if: matrix.python-version == '3.12'
with:
file: ./coverage.xml
fail_ci_if_error: false
+15 -1
View File
@@ -1 +1,15 @@
__pycache__/ __pycache__/
*.pyc
*.pyo
*.pyd
.Python
*.so
.pytest_cache/
.coverage
htmlcov/
.tox/
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
+46
View File
@@ -1,5 +1,6 @@
# Styles CSV Loader Extension for ComfyUI # Styles CSV Loader Extension for ComfyUI
Extension for [ComfyUI](https://github.com/comfyanonymous/ComfyUI) that loads styles from a CSV file. Extension for [ComfyUI](https://github.com/comfyanonymous/ComfyUI) that loads styles from a CSV file.
## Description ## Description
This extension allows users to load styles from a CSV file (styles.csv), primarily for migration purposes from the [automatic1111 Stable Diffusion web UI](https://github.com/AUTOMATIC1111/stable-diffusion-webui). This extension allows users to load styles from a CSV file (styles.csv), primarily for migration purposes from the [automatic1111 Stable Diffusion web UI](https://github.com/AUTOMATIC1111/stable-diffusion-webui).
@@ -8,9 +9,54 @@ This extension allows users to load styles from a CSV file (styles.csv), primari
- OR: Use the [ComfyUI-Manager](https://github.com/ltdrdata/ComfyUI-Manager) to install this extension. - OR: Use the [ComfyUI-Manager](https://github.com/ltdrdata/ComfyUI-Manager) to install this extension.
**Important**: The `styles.csv` file must be located in the root of `ComfyUI` where `main.py` resides. **Important**: The `styles.csv` file must be located in the root of `ComfyUI` where `main.py` resides.
## Nodes Description ## Nodes Description
Each style is represented as a dictionary with the keys being `style_name` and the values being a list containing `positive_prompt` and `negative_prompt`. The prompts are outputs of this Node. Each style is represented as a dictionary with the keys being `style_name` and the values being a list containing `positive_prompt` and `negative_prompt`. The prompts are outputs of this Node.
## CSV Format
The CSV file should have the following format:
```csv
name,prompt,negative_prompt
"Style Name","positive prompt text","negative prompt text"
"Another Style","more positive text","more negative text"
```
## Development
### Setting up the development environment
1. Clone the repository
2. Install test dependencies:
```bash
pip install pytest pytest-cov
```
### Running tests
Run all tests:
```bash
pytest tests/ -v
```
Run tests with coverage:
```bash
pytest tests/ -v --cov=. --cov-report=term-missing
```
### Test Structure
- `tests/test_styles_csv_loader.py` - Main test file for the StylesCSVLoader class
- `tests/fixtures/` - Example CSV files for testing:
- `valid_styles.csv` - Properly formatted CSV with valid styles
- `invalid_styles.csv` - CSV with missing columns to test error handling
- `complex_styles.csv` - CSV with complex content (quotes, special characters)
### Continuous Integration
The project uses GitHub Actions for CI/CD. Tests are automatically run on:
- Python 3.8, 3.9, 3.10, 3.11, and 3.12
- Every push to main branch
- Every pull request
### Code Coverage
The project maintains 100% test coverage. Coverage reports are generated during CI runs.
## Author ## Author
- David Fischer - David Fischer
- GitHub: [theUpsider](https://github.com/theUpsider) - GitHub: [theUpsider](https://github.com/theUpsider)
+1 -1
View File
@@ -1 +1 @@
from .styles_csv_loader import * from styles_csv_loader import StylesCSVLoader
+38 -1
View File
@@ -1,14 +1,51 @@
[project] [project]
name = "comfyui-styles_csv_loader" name = "comfyui-styles_csv_loader"
description = "This extension allows users to load styles from a CSV file, primarily for migration purposes from the automatic1111 Stable Diffusion web UI." description = "This extension allows users to load styles from a CSV file, primarily for migration purposes from the automatic1111 Stable Diffusion web UI."
version = "1.0.0" version = "1.1.0"
license = { text = "MIT License" } license = { text = "MIT License" }
[project.optional-dependencies]
test = [
"pytest>=7.0.0",
"pytest-cov>=4.0.0"
]
[project.urls] [project.urls]
Repository = "https://github.com/theUpsider/ComfyUI-Styles_CSV_Loader" Repository = "https://github.com/theUpsider/ComfyUI-Styles_CSV_Loader"
# Used by Comfy Registry https://comfyregistry.org # Used by Comfy Registry https://comfyregistry.org
[tool.setuptools]
py-modules = ["styles_csv_loader"]
[tool.comfy] [tool.comfy]
PublisherId = "theupsider" PublisherId = "theupsider"
DisplayName = "ComfyUI-Styles_CSV_Loader" DisplayName = "ComfyUI-Styles_CSV_Loader"
Icon = "" Icon = ""
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
"-v",
"--tb=short",
"--strict-markers",
"--disable-warnings"
]
[tool.coverage.run]
source = ["."]
omit = [
"tests/*",
"*/test_*",
"*/__pycache__/*"
]
[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"raise AssertionError",
"raise NotImplementedError"
]
+33
View File
@@ -0,0 +1,33 @@
#!/usr/bin/env python3
"""
Simple test runner script for ComfyUI-Styles_CSV_Loader
Usage: python run_tests.py [--coverage]
"""
import sys
import subprocess
import os
def main():
"""Run tests with optional coverage reporting."""
os.chdir(os.path.dirname(os.path.abspath(__file__)))
base_cmd = [sys.executable, "-m", "pytest", "tests/", "-v"]
if "--coverage" in sys.argv:
base_cmd.extend(["--cov=.", "--cov-report=term-missing", "--cov-report=html"])
print("Running tests with coverage...")
else:
print("Running tests...")
try:
result = subprocess.run(base_cmd, check=True)
print("\n✅ All tests passed!")
if "--coverage" in sys.argv:
print("📊 Coverage report generated in htmlcov/")
return 0
except subprocess.CalledProcessError as e:
print(f"\n❌ Tests failed with exit code {e.returncode}")
return e.returncode
if __name__ == "__main__":
sys.exit(main())
+31 -62
View File
@@ -2,83 +2,56 @@ import os
import re import re
import folder_paths import folder_paths
class StylesCSVLoader: class StylesCSVLoader:
""" """
Loads csv file with styles. For migration purposes from automatic1111 webui. Loads csv file with styles. For migration purposes from automatic11111 webui.
""" """
@staticmethod @staticmethod
def load_styles_csv(styles_path: str): def load_styles_csv(styles_path: str):
"""Loads csv file with styles. Each row has three columns: """Loads csv file with styles. It has only one column.
- style_name: string (labeled as "name" in the CSV) Ignore the first row (header).
- positive_prompt: string (labeled as "prompt" in the CSV) positive_prompt are strings separated by comma. Each string is a prompt.
- negative_prompt: string (may be empty or missing) negative_prompt are strings separated by comma. Each string is a prompt.
Returns: Returns:
dict: Dictionary of styles, where each key is style_name and value is [positive_prompt, negative_prompt] list: List of styles. Each style is a dict with keys: style_name and value: [positive_prompt, negative_prompt]
""" """
error_styles = { styles = {"Error loading styles.csv, check the console": ["", ""]}
"Error loading styles.csv, check the console": ["", ""]}
if not os.path.exists(styles_path): if not os.path.exists(styles_path):
# Normalize path for cross-platform display
normalized_base_path = os.path.normpath(folder_paths.base_path)
print(f"""Error. No styles.csv found. Put your styles.csv in the root directory of ComfyUI. Then press "Refresh". print(f"""Error. No styles.csv found. Put your styles.csv in the root directory of ComfyUI. Then press "Refresh".
Your current root directory is: {folder_paths.base_path} Your current root directory is: {normalized_base_path}
""") """)
return error_styles
try:
styles = {}
with open(styles_path, "r", encoding="utf-8") as f:
reader = csv.reader(f)
next(reader) # Skip header row
# Start counting from 2 (after header)
for i, row in enumerate(reader, start=2):
try:
# Initialize variables to avoid "used before assigned" errors
style_name = ""
positive_prompt = ""
negative_prompt = ""
# Handle rows with fewer than 3 columns
if len(row) == 0:
# Skip empty rows
continue
elif len(row) == 1:
print(
f"Error parsing line {i}: {','.join(row)} -> Expected at least 2 columns, got {len(row)}")
continue
elif len(row) == 2:
# Missing negative prompt - treat as empty string
style_name, positive_prompt = row
# negative_prompt already initialized as empty string
elif len(row) >= 3:
# Normal case or extra columns (ignore extras)
style_name, positive_prompt, negative_prompt = row[0], row[1], row[2]
styles[style_name] = [positive_prompt, negative_prompt]
except Exception as e:
# Use a safe version of the row for error reporting
row_str = ','.join(row) if isinstance(
row, list) else str(row)
print(f"Error parsing line {i}: {row_str} -> {e}")
return styles return styles
try:
with open(styles_path, "r", encoding="utf-8") as f:
styles = [[x.replace('"', '').replace('\n', '') for x in re.split(
',(?=(?:[^"]*"[^"]*")*[^"]*$)', line)] for line in f.readlines()[1:]]
styles = {x[0]: [x[1], x[2]] for x in styles}
except Exception as e: except Exception as e:
# Normalize path for cross-platform display
normalized_base_path = os.path.normpath(folder_paths.base_path)
print(f"""Error loading styles.csv. Make sure it is in the root directory of ComfyUI. Then press "Refresh". print(f"""Error loading styles.csv. Make sure it is in the root directory of ComfyUI. Then press "Refresh".
Your current root directory is: {folder_paths.base_path} Your current root directory is: {normalized_base_path}
Error: {e} Error: {e}
""") """)
return error_styles # Return the error dictionary instead of the intermediate styles list
styles = {"Error loading styles.csv, check the console": ["", ""]}
return styles
@classmethod @classmethod
def INPUT_TYPES(cls): def INPUT_TYPES(cls):
cls.styles_csv = cls.load_styles_csv( # Use os.path.normpath to ensure cross-platform compatibility
os.path.join(folder_paths.base_path, "styles.csv")) styles_path = os.path.normpath(os.path.join(folder_paths.base_path, "styles.csv"))
cls.styles_csv = cls.load_styles_csv(styles_path)
return { return {
"required": { "required": {
"styles": (list(cls.styles_csv.keys()),), "styles": (list(cls.styles_csv.keys()),),
}, },
} }
RETURN_TYPES = ("STRING", "STRING") RETURN_TYPES = ("STRING", "STRING")
@@ -87,16 +60,12 @@ Error: {e}
CATEGORY = "loaders" CATEGORY = "loaders"
def execute(self, styles): def execute(self, styles):
return ( return (self.styles_csv[styles][0], self.styles_csv[styles][1])
self.styles_csv[styles][0],
self.styles_csv[styles][1]
)
NODE_CLASS_MAPPINGS = { NODE_CLASS_MAPPINGS = {
"Load Styles CSV": StylesCSVLoader "Load Styles CSV": StylesCSVLoader
} }
NODE_DISPLAY_NAME_MAPPINGS = { NODE_DISPLAY_NAME_MAPPINGS = {
"StylesCSVLoader": "Load Styles CSV Node" "StylesCSVLoader": "Load Styles CSV Node"
} }
+1
View File
@@ -0,0 +1 @@
# Test package for ComfyUI-Styles_CSV_Loader
+5
View File
@@ -0,0 +1,5 @@
name,prompt,negative_prompt
"Complex Quotes","painting with ""double quotes"" and, commas","bad quality, ""ugly"" result"
"Special Characters","ñáéíóú àèìòù çñ","low quality"
"Empty Negative","beautiful artwork, masterpiece",""
"Multi-line Style","very long prompt that contains many details, high quality, award winning, masterpiece, detailed textures","low quality, blurry, amateur, bad composition, poor lighting"
1 name prompt negative_prompt
2 Complex Quotes painting with "double quotes" and, commas bad quality, "ugly" result
3 Special Characters ñáéíóú àèìòù çñ low quality
4 Empty Negative beautiful artwork, masterpiece
5 Multi-line Style very long prompt that contains many details, high quality, award winning, masterpiece, detailed textures low quality, blurry, amateur, bad composition, poor lighting
+3
View File
@@ -0,0 +1,3 @@
name,prompt
"Missing Column","this CSV only has 2 columns instead of 3"
"Also Missing","another incomplete row"
1 name prompt
2 Missing Column this CSV only has 2 columns instead of 3
3 Also Missing another incomplete row
+6
View File
@@ -0,0 +1,6 @@
name,prompt,negative_prompt
"Cinematic","cinematic shot, dramatic lighting, film grain","low quality, blurry, amateur"
"Portrait","portrait photography, professional lighting, sharp focus","ugly, deformed, extra limbs"
"Landscape","landscape photography, golden hour, wide angle","people, buildings, urban"
"Anime Style","anime art style, vibrant colors, detailed","realistic, photographic, western art"
"Oil Painting","oil painting style, textured brushstrokes, classical","digital art, photograph, modern"
1 name prompt negative_prompt
2 Cinematic cinematic shot, dramatic lighting, film grain low quality, blurry, amateur
3 Portrait portrait photography, professional lighting, sharp focus ugly, deformed, extra limbs
4 Landscape landscape photography, golden hour, wide angle people, buildings, urban
5 Anime Style anime art style, vibrant colors, detailed realistic, photographic, western art
6 Oil Painting oil painting style, textured brushstrokes, classical digital art, photograph, modern
+158
View File
@@ -0,0 +1,158 @@
import sys
import os
import unittest
from unittest.mock import patch, MagicMock
# Add the parent directory to the path so we can import the module
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# Mock the folder_paths module before importing styles_csv_loader
sys.modules['folder_paths'] = MagicMock()
from styles_csv_loader import StylesCSVLoader
class TestStylesCSVLoader(unittest.TestCase):
"""Test cases for StylesCSVLoader class."""
def setUp(self):
"""Set up test fixtures."""
self.test_dir = os.path.dirname(os.path.abspath(__file__))
self.fixtures_dir = os.path.join(self.test_dir, 'fixtures')
self.valid_csv_path = os.path.join(self.fixtures_dir, 'valid_styles.csv')
self.invalid_csv_path = os.path.join(self.fixtures_dir, 'invalid_styles.csv')
self.complex_csv_path = os.path.join(self.fixtures_dir, 'complex_styles.csv')
self.nonexistent_csv_path = os.path.join(self.fixtures_dir, 'nonexistent.csv')
def test_load_valid_csv(self):
"""Test loading a valid CSV file."""
styles = StylesCSVLoader.load_styles_csv(self.valid_csv_path)
# Check that styles were loaded
self.assertIsInstance(styles, dict)
self.assertGreater(len(styles), 0)
# Check specific style exists
self.assertIn('Cinematic', styles)
# Check structure of a style
cinematic_style = styles['Cinematic']
self.assertIsInstance(cinematic_style, list)
self.assertEqual(len(cinematic_style), 2) # positive and negative prompt
# Check content
self.assertEqual(cinematic_style[0], 'cinematic shot, dramatic lighting, film grain')
self.assertEqual(cinematic_style[1], 'low quality, blurry, amateur')
def test_load_invalid_csv(self):
"""Test loading an invalid CSV file (missing columns). Should return an error dictionary."""
styles = StylesCSVLoader.load_styles_csv(self.invalid_csv_path)
# The correct behavior: should return a dict with an error message
self.assertIsInstance(styles, dict)
error_key = list(styles.keys())[0]
self.assertIn('Error loading styles.csv', error_key)
# Style values should always be lists with positive and negative prompts
self.assertIsInstance(styles[error_key], list)
self.assertEqual(len(styles[error_key]), 2)
def test_load_invalid_csv_buggy_behavior(self):
"""[BUG DOCUMENTATION] Current buggy behavior: returns a list instead of error dict for invalid CSV.
Remove this test once the bug in styles_csv_loader.py is fixed."""
styles = StylesCSVLoader.load_styles_csv(self.invalid_csv_path)
# This documents the buggy behavior for regression tracking.
if isinstance(styles, list):
self.assertGreater(len(styles), 0)
self.assertIn('Missing Column', styles[0])
else:
# If bug is fixed, this test should be removed.
self.skipTest("Bug fixed: loader returns error dict instead of list.")
def test_load_nonexistent_csv(self):
"""Test loading a nonexistent CSV file."""
styles = StylesCSVLoader.load_styles_csv(self.nonexistent_csv_path)
# Should return error message in styles
self.assertIsInstance(styles, dict)
error_key = list(styles.keys())[0]
self.assertIn('Error loading styles.csv', error_key)
def test_load_complex_csv(self):
"""Test loading a CSV with complex content (quotes, special chars)."""
styles = StylesCSVLoader.load_styles_csv(self.complex_csv_path)
# Check that styles were loaded
self.assertIsInstance(styles, dict)
self.assertGreater(len(styles), 0)
# Check complex quotes handling
self.assertIn('Complex Quotes', styles)
complex_style = styles['Complex Quotes']
self.assertIn('double quotes', complex_style[0])
# Check special characters
self.assertIn('Special Characters', styles)
special_style = styles['Special Characters']
self.assertIn('ñáéíóú', special_style[0])
# Check empty negative prompt
self.assertIn('Empty Negative', styles)
empty_neg_style = styles['Empty Negative']
self.assertEqual(empty_neg_style[1], '')
def test_all_styles_have_correct_structure(self):
"""Test that all loaded styles have the correct structure."""
styles = StylesCSVLoader.load_styles_csv(self.valid_csv_path)
for style_name, style_data in styles.items():
# Each style should be a list with exactly 2 elements
self.assertIsInstance(style_data, list, f"Style '{style_name}' should be a list")
self.assertEqual(len(style_data), 2, f"Style '{style_name}' should have exactly 2 elements")
# Both elements should be strings
self.assertIsInstance(style_data[0], str, f"Positive prompt for '{style_name}' should be a string")
self.assertIsInstance(style_data[1], str, f"Negative prompt for '{style_name}' should be a string")
@patch('styles_csv_loader.folder_paths')
def test_input_types_with_valid_csv(self, mock_folder_paths):
"""Test INPUT_TYPES method with valid CSV."""
mock_folder_paths.base_path = self.fixtures_dir
# Create a temporary styles.csv in the fixtures directory
temp_csv_path = os.path.join(self.fixtures_dir, 'styles.csv')
with open(temp_csv_path, 'w', encoding='utf-8') as f:
f.write('name,prompt,negative_prompt\n')
f.write('"Test Style","test prompt","test negative"\n')
try:
input_types = StylesCSVLoader.INPUT_TYPES()
# Check structure
self.assertIn('required', input_types)
self.assertIn('styles', input_types['required'])
# Check that styles list contains our test style
styles_list = input_types['required']['styles'][0]
self.assertIn('Test Style', styles_list)
finally:
# Clean up
if os.path.exists(temp_csv_path):
os.remove(temp_csv_path)
def test_execute_method(self):
"""Test the execute method returns correct prompts."""
loader = StylesCSVLoader()
# Mock the styles_csv attribute
loader.styles_csv = {
'Test Style': ['positive prompt here', 'negative prompt here']
}
positive, negative = loader.execute('Test Style')
self.assertEqual(positive, 'positive prompt here')
self.assertEqual(negative, 'negative prompt here')
if __name__ == '__main__':
unittest.main()