mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(nodes): change expected structure for custom nodes
This commit is contained in:
parent
8604943e89
commit
824702de99
@ -7,10 +7,13 @@ from invokeai.app.services.config.config_default import InvokeAIAppConfig
|
||||
|
||||
custom_nodes_path = Path(InvokeAIAppConfig.get_config().custom_nodes_path.absolute())
|
||||
custom_nodes_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
custom_nodes_init_path = str(custom_nodes_path / "__init__.py")
|
||||
custom_nodes_readme_path = str(custom_nodes_path / "README.md")
|
||||
|
||||
# copy our custom nodes __init__.py to the custom nodes directory
|
||||
shutil.copy(Path(__file__).parent / "_custom_nodes_init.py", custom_nodes_init_path)
|
||||
shutil.copy(Path(__file__).parent / "custom_nodes/init.py", custom_nodes_init_path)
|
||||
shutil.copy(Path(__file__).parent / "custom_nodes/README.md", custom_nodes_readme_path)
|
||||
|
||||
# Import custom nodes, see https://docs.python.org/3/library/importlib.html#importing-programmatically
|
||||
spec = spec_from_file_location("custom_nodes", custom_nodes_init_path)
|
||||
@ -21,5 +24,5 @@ sys.modules[spec.name] = module
|
||||
spec.loader.exec_module(module)
|
||||
|
||||
# add core nodes to __all__
|
||||
python_files = filter(lambda f: not f.name.startswith("_"), Path(__file__).parent.rglob("*.py"))
|
||||
python_files = filter(lambda f: not f.name.startswith("_"), Path(__file__).parent.glob("*.py"))
|
||||
__all__ = list(f.stem for f in python_files) # type: ignore
|
||||
|
@ -1,37 +0,0 @@
|
||||
"""
|
||||
InvokeAI custom nodes initialization
|
||||
|
||||
This file is responsible for loading all custom nodes from this directory.
|
||||
|
||||
All python files are loaded on app startup. Custom nodes will be initialized and available for use
|
||||
in workflows.
|
||||
|
||||
The app must be restarted for changes to be picked up.
|
||||
|
||||
This file is overwritten on launch. Do not edit this file directly.
|
||||
"""
|
||||
import sys
|
||||
from importlib import import_module
|
||||
from importlib.util import module_from_spec, spec_from_file_location
|
||||
from pathlib import Path
|
||||
|
||||
from invokeai.backend.util.logging import InvokeAILogger
|
||||
|
||||
logger = InvokeAILogger.get_logger()
|
||||
count = 0
|
||||
for f in Path(__file__).parent.rglob("*.py"):
|
||||
module_name = f.stem
|
||||
if (not module_name.startswith("_")) and (module_name not in globals()):
|
||||
spec = spec_from_file_location(module_name, f.absolute())
|
||||
if spec is None or spec.loader is None:
|
||||
logger.warn(f"Could not load {f}")
|
||||
continue
|
||||
module = module_from_spec(spec)
|
||||
sys.modules[spec.name] = module
|
||||
spec.loader.exec_module(module)
|
||||
count += 1
|
||||
del f, module_name
|
||||
|
||||
logger.info(f"Loaded {count} modules from {Path(__file__).parent}")
|
||||
|
||||
del import_module, Path
|
51
invokeai/app/invocations/custom_nodes/README.md
Normal file
51
invokeai/app/invocations/custom_nodes/README.md
Normal file
@ -0,0 +1,51 @@
|
||||
# Custom Nodes / Node Packs
|
||||
|
||||
Copy your node packs to this directory.
|
||||
|
||||
When nodes are added or changed, you must restart the app to see the changes.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
For a node pack to be loaded, it must be placed in a directory alongside this
|
||||
file. Here's an example structure:
|
||||
|
||||
```py
|
||||
.
|
||||
├── __init__.py # Invoke-managed custom node loader
|
||||
│
|
||||
├── cool_node
|
||||
│ ├── __init__.py # see example below
|
||||
│ └── cool_node.py
|
||||
│
|
||||
└── my_node_pack
|
||||
├── __init__.py # see example below
|
||||
├── tasty_node.py
|
||||
├── bodacious_node.py
|
||||
├── utils.py
|
||||
└── extra_nodes
|
||||
└── fancy_node.py
|
||||
```
|
||||
|
||||
## Node Pack `__init__.py`
|
||||
|
||||
Each node pack must have an `__init__.py` file that imports its nodes.
|
||||
|
||||
The structure of each node or node pack is otherwise not important.
|
||||
|
||||
Here are examples, based on the example directory structure.
|
||||
|
||||
### `cool_node/__init__.py`
|
||||
|
||||
```py
|
||||
from .cool_node import CoolInvocation
|
||||
```
|
||||
|
||||
### `my_node_pack/__init__.py`
|
||||
|
||||
```py
|
||||
from .tasty_node import TastyInvocation
|
||||
from .bodacious_node import BodaciousInvocation
|
||||
from .extra_nodes.fancy_node import FancyInvocation
|
||||
```
|
||||
|
||||
Only nodes imported in the `__init__.py` file are loaded.
|
51
invokeai/app/invocations/custom_nodes/init.py
Normal file
51
invokeai/app/invocations/custom_nodes/init.py
Normal file
@ -0,0 +1,51 @@
|
||||
"""
|
||||
Invoke-managed custom node loader. See README.md for more information.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from importlib.util import module_from_spec, spec_from_file_location
|
||||
from pathlib import Path
|
||||
|
||||
from invokeai.backend.util.logging import InvokeAILogger
|
||||
|
||||
logger = InvokeAILogger.get_logger()
|
||||
loaded_count = 0
|
||||
|
||||
|
||||
for d in Path(__file__).parent.iterdir():
|
||||
# skip files
|
||||
if not d.is_dir():
|
||||
continue
|
||||
|
||||
# skip hidden directories
|
||||
if d.name.startswith("_") or d.name.startswith("."):
|
||||
continue
|
||||
|
||||
# skip directories without an `__init__.py`
|
||||
init = d / "__init__.py"
|
||||
if not init.exists():
|
||||
continue
|
||||
|
||||
module_name = init.parent.stem
|
||||
|
||||
# skip if already imported
|
||||
if module_name in globals():
|
||||
continue
|
||||
|
||||
# we have a legit module to import
|
||||
spec = spec_from_file_location(module_name, init.absolute())
|
||||
|
||||
if spec is None or spec.loader is None:
|
||||
logger.warn(f"Could not load {init}")
|
||||
continue
|
||||
|
||||
module = module_from_spec(spec)
|
||||
sys.modules[spec.name] = module
|
||||
spec.loader.exec_module(module)
|
||||
|
||||
loaded_count += 1
|
||||
|
||||
del init, module_name
|
||||
|
||||
|
||||
logger.info(f"Loaded {loaded_count} modules from {Path(__file__).parent}")
|
Loading…
Reference in New Issue
Block a user