mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-01-11 23:00:51 +08:00
Fix compatibility with Python 3.9, 3.10, fix Configuration class declaration issue
This commit is contained in:
parent
747ab861c1
commit
123c512a84
@ -93,7 +93,7 @@ Ctrl can also be replaced with Cmd instead for macOS users
|
|||||||
|
|
||||||
## Installing
|
## Installing
|
||||||
|
|
||||||
You must have Python 3.12, 3.11 or 3.10 installed. On Windows, download the latest Python from their website. You can also [directly download 3.11.4 here](https://www.python.org/ftp/python/3.11.4/python-3.11.4-amd64.exe).
|
You must have Python 3.10, 3.11 or 3.12 installed. On Windows, download the latest Python from their website. You can also [directly download 3.11.4 here](https://www.python.org/ftp/python/3.11.4/python-3.11.4-amd64.exe).
|
||||||
|
|
||||||
On macOS, install exactly Python 3.11 using `brew`, which you can download from https://brew.sh, using this command: `brew install python@3.11`. Do not use 3.9 or older, and do not use 3.12 or newer. Its compatibility with Stable Diffusion in both directions is broken.
|
On macOS, install exactly Python 3.11 using `brew`, which you can download from https://brew.sh, using this command: `brew install python@3.11`. Do not use 3.9 or older, and do not use 3.12 or newer. Its compatibility with Stable Diffusion in both directions is broken.
|
||||||
|
|
||||||
@ -178,6 +178,10 @@ On macOS, install exactly Python 3.11 using `brew`, which you can download from
|
|||||||
(cd tests-ui && npm ci && npm run test:generate && npm test)
|
(cd tests-ui && npm ci && npm run test:generate && npm test)
|
||||||
```
|
```
|
||||||
You can use `comfyui` as an API. Visit the [OpenAPI specification](comfy/api/openapi.yaml). This file can be used to generate typed clients for your preferred language.
|
You can use `comfyui` as an API. Visit the [OpenAPI specification](comfy/api/openapi.yaml). This file can be used to generate typed clients for your preferred language.
|
||||||
|
7. To create the standalone binary:
|
||||||
|
```shell
|
||||||
|
python -m pyinstaller --onefile --noupx -n ComfyUI --add-data="comfy/;comfy/" --paths $(pwd) --paths comfy/cmd main.py
|
||||||
|
```
|
||||||
|
|
||||||
### Authoring Custom Nodes
|
### Authoring Custom Nodes
|
||||||
|
|
||||||
@ -252,7 +256,7 @@ You would also be able to add the `comfyui` git hash and custom nodes packages b
|
|||||||
|
|
||||||
> I see a message like `RuntimeError: '"upsample_bilinear2d_channels_last" not implemented for 'Half''`
|
> I see a message like `RuntimeError: '"upsample_bilinear2d_channels_last" not implemented for 'Half''`
|
||||||
|
|
||||||
You must use Python 3.10 or 3.11 on macOS devices, and update to at least Ventura.
|
You must use Python 3.11 on macOS devices, and update to at least Ventura.
|
||||||
|
|
||||||
> I see a message like `Error while deserializing header: HeaderTooLarge`
|
> I see a message like `Error while deserializing header: HeaderTooLarge`
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import typing
|
import typing
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import configargparse as argparse
|
|||||||
import enum
|
import enum
|
||||||
from . import options
|
from . import options
|
||||||
from .cli_args_types import LatentPreviewMethod, Configuration
|
from .cli_args_types import LatentPreviewMethod, Configuration
|
||||||
|
import sys
|
||||||
|
|
||||||
class EnumAction(argparse.Action):
|
class EnumAction(argparse.Action):
|
||||||
"""
|
"""
|
||||||
@ -106,11 +106,12 @@ parser.add_argument("--deterministic", action="store_true", help="Make pytorch u
|
|||||||
|
|
||||||
parser.add_argument("--dont-print-server", action="store_true", help="Don't print server output.")
|
parser.add_argument("--dont-print-server", action="store_true", help="Don't print server output.")
|
||||||
parser.add_argument("--quick-test-for-ci", action="store_true", help="Quick test for CI.")
|
parser.add_argument("--quick-test-for-ci", action="store_true", help="Quick test for CI.")
|
||||||
parser.add_argument("--windows-standalone-build", action="store_true", help="Windows standalone build: Enable convenient things that most people using the standalone windows build will probably enjoy (like auto opening the page on startup).")
|
parser.add_argument("--windows-standalone-build", default=hasattr(sys, 'frozen') and getattr(sys, 'frozen'), action="store_true", help="Windows standalone build: Enable convenient things that most people using the standalone windows build will probably enjoy (like auto opening the page on startup).")
|
||||||
|
|
||||||
parser.add_argument("--disable-metadata", action="store_true", help="Disable saving prompt metadata in files.")
|
parser.add_argument("--disable-metadata", action="store_true", help="Disable saving prompt metadata in files.")
|
||||||
|
|
||||||
parser.add_argument("--multi-user", action="store_true", help="Enables per-user storage.")
|
parser.add_argument("--multi-user", action="store_true", help="Enables per-user storage.")
|
||||||
|
parser.add_argument("--create-directories", action="store_true", help="Creates the default models/, input/, output/ and temp/ directories, then exits.")
|
||||||
|
|
||||||
parser.add_argument("--plausible-analytics-base-url", required=False,
|
parser.add_argument("--plausible-analytics-base-url", required=False,
|
||||||
help="Enables server-side analytics events sent to the provided URL.")
|
help="Enables server-side analytics events sent to the provided URL.")
|
||||||
|
|||||||
@ -69,64 +69,64 @@ class Configuration(dict):
|
|||||||
plausible_analytics_domain (Optional[str]): Domain for analytics events.
|
plausible_analytics_domain (Optional[str]): Domain for analytics events.
|
||||||
analytics_use_identity_provider (bool): Use platform identifiers for analytics.
|
analytics_use_identity_provider (bool): Use platform identifiers for analytics.
|
||||||
write_out_config_file (bool): Enable writing out the configuration file.
|
write_out_config_file (bool): Enable writing out the configuration file.
|
||||||
|
create_directories (bool): Creates the default models/, input/, output/ and temp/ directories, then exits.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
config: Optional[str]
|
|
||||||
cwd: Optional[str]
|
|
||||||
listen: str
|
|
||||||
port: int
|
|
||||||
enable_cors_header: Optional[str]
|
|
||||||
max_upload_size: float
|
|
||||||
extra_model_paths_config: Optional[List[str]]
|
|
||||||
output_directory: Optional[str]
|
|
||||||
temp_directory: Optional[str]
|
|
||||||
input_directory: Optional[str]
|
|
||||||
auto_launch: bool
|
|
||||||
disable_auto_launch: bool
|
|
||||||
cuda_device: Optional[int]
|
|
||||||
cuda_malloc: bool
|
|
||||||
disable_cuda_malloc: bool
|
|
||||||
dont_upcast_attention: bool
|
|
||||||
force_fp32: bool
|
|
||||||
force_fp16: bool
|
|
||||||
bf16_unet: bool
|
|
||||||
fp16_unet: bool
|
|
||||||
fp8_e4m3fn_unet: bool
|
|
||||||
fp8_e5m2_unet: bool
|
|
||||||
fp16_vae: bool
|
|
||||||
fp32_vae: bool
|
|
||||||
bf16_vae: bool
|
|
||||||
cpu_vae: bool
|
|
||||||
fp8_e4m3fn_text_enc: bool
|
|
||||||
fp8_e5m2_text_enc: bool
|
|
||||||
fp16_text_enc: bool
|
|
||||||
fp32_text_enc: bool
|
|
||||||
directml: Optional[int]
|
|
||||||
disable_ipex_optimize: bool
|
|
||||||
preview_method: LatentPreviewMethod
|
|
||||||
use_split_cross_attention: bool
|
|
||||||
use_quad_cross_attention: bool
|
|
||||||
use_pytorch_cross_attention: bool
|
|
||||||
disable_xformers: bool
|
|
||||||
gpu_only: bool
|
|
||||||
highvram: bool
|
|
||||||
normalvram: bool
|
|
||||||
lowvram: bool
|
|
||||||
novram: bool
|
|
||||||
cpu: bool
|
|
||||||
disable_smart_memory: bool
|
|
||||||
deterministic: bool
|
|
||||||
dont_print_server: bool
|
|
||||||
quick_test_for_ci: bool
|
|
||||||
windows_standalone_build: bool
|
|
||||||
disable_metadata: bool
|
|
||||||
multi_user: bool
|
|
||||||
plausible_analytics_base_url: Optional[str]
|
|
||||||
plausible_analytics_domain: Optional[str]
|
|
||||||
analytics_use_identity_provider: bool
|
|
||||||
write_out_config_file: bool
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__()
|
||||||
|
self.cwd: Optional[str] = None
|
||||||
|
self.listen: str = "127.0.0.1"
|
||||||
|
self.port: int = 8188
|
||||||
|
self.enable_cors_header: Optional[str] = None
|
||||||
|
self.max_upload_size: float = 100.0
|
||||||
|
self.extra_model_paths_config: Optional[List[str]] = []
|
||||||
|
self.output_directory: Optional[str] = None
|
||||||
|
self.temp_directory: Optional[str] = None
|
||||||
|
self.input_directory: Optional[str] = None
|
||||||
|
self.auto_launch: bool = False
|
||||||
|
self.disable_auto_launch: bool = False
|
||||||
|
self.cuda_device: Optional[int] = None
|
||||||
|
self.cuda_malloc: bool = True
|
||||||
|
self.disable_cuda_malloc: bool = False
|
||||||
|
self.dont_upcast_attention: bool = False
|
||||||
|
self.force_fp32: bool = False
|
||||||
|
self.force_fp16: bool = False
|
||||||
|
self.bf16_unet: bool = False
|
||||||
|
self.fp16_unet: bool = False
|
||||||
|
self.fp8_e4m3fn_unet: bool = False
|
||||||
|
self.fp8_e5m2_unet: bool = False
|
||||||
|
self.fp16_vae: bool = False
|
||||||
|
self.fp32_vae: bool = False
|
||||||
|
self.bf16_vae: bool = False
|
||||||
|
self.cpu_vae: bool = False
|
||||||
|
self.fp8_e4m3fn_text_enc: bool = False
|
||||||
|
self.fp8_e5m2_text_enc: bool = False
|
||||||
|
self.fp16_text_enc: bool = False
|
||||||
|
self.fp32_text_enc: bool = False
|
||||||
|
self.directml: Optional[int] = None
|
||||||
|
self.disable_ipex_optimize: bool = False
|
||||||
|
self.preview_method: str = "none"
|
||||||
|
self.use_split_cross_attention: bool = False
|
||||||
|
self.use_quad_cross_attention: bool = False
|
||||||
|
self.use_pytorch_cross_attention: bool = False
|
||||||
|
self.disable_xformers: bool = False
|
||||||
|
self.gpu_only: bool = False
|
||||||
|
self.highvram: bool = False
|
||||||
|
self.normalvram: bool = False
|
||||||
|
self.lowvram: bool = False
|
||||||
|
self.novram: bool = False
|
||||||
|
self.cpu: bool = False
|
||||||
|
self.disable_smart_memory: bool = False
|
||||||
|
self.deterministic: bool = False
|
||||||
|
self.dont_print_server: bool = False
|
||||||
|
self.quick_test_for_ci: bool = False
|
||||||
|
self.windows_standalone_build: bool = False
|
||||||
|
self.disable_metadata: bool = False
|
||||||
|
self.multi_user: bool = False
|
||||||
|
self.plausible_analytics_base_url: Optional[str] = None
|
||||||
|
self.plausible_analytics_domain: Optional[str] = None
|
||||||
|
self.analytics_use_identity_provider: bool = False
|
||||||
|
self.write_out_config_file: bool = False
|
||||||
|
self.create_directories: bool = False
|
||||||
for key, value in kwargs.items():
|
for key, value in kwargs.items():
|
||||||
self[key] = value
|
self[key] = value
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import gc
|
import gc
|
||||||
import uuid
|
import uuid
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import copy
|
import copy
|
||||||
from typing import TypeAlias
|
from typing import TypeAlias, Union
|
||||||
|
|
||||||
from comfy.api.components.schema.prompt import PromptDict, Prompt
|
from comfy.api.components.schema.prompt import PromptDict, Prompt
|
||||||
|
|
||||||
JSON: TypeAlias = dict[str, "JSON"] | list["JSON"] | str | int | float | bool | None
|
JSON: TypeAlias = Union[dict[str, "JSON"], list["JSON"], str, int, float, bool, None]
|
||||||
_BASE_PROMPT: JSON = {
|
_BASE_PROMPT: JSON = {
|
||||||
"4": {
|
"4": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
|
|||||||
@ -8,7 +8,8 @@ import sys
|
|||||||
import threading
|
import threading
|
||||||
import traceback
|
import traceback
|
||||||
import typing
|
import typing
|
||||||
from typing import List, Optional, Tuple
|
from typing import List, Optional, Tuple, Union
|
||||||
|
from typing_extensions import TypedDict
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
|
|
||||||
@ -655,19 +656,19 @@ def full_type_name(klass):
|
|||||||
return module + '.' + klass.__qualname__
|
return module + '.' + klass.__qualname__
|
||||||
|
|
||||||
|
|
||||||
class ValidationErrorExtraInfoDict(typing.TypedDict):
|
class ValidationErrorExtraInfoDict(TypedDict):
|
||||||
exception_type: str
|
exception_type: str
|
||||||
traceback: List[str]
|
traceback: List[str]
|
||||||
|
|
||||||
|
|
||||||
class ValidationErrorDict(typing.TypedDict):
|
class ValidationErrorDict(TypedDict):
|
||||||
type: str
|
type: str
|
||||||
message: str
|
message: str
|
||||||
details: str
|
details: str
|
||||||
extra_info: ValidationErrorExtraInfoDict | dict
|
extra_info: ValidationErrorExtraInfoDict | dict
|
||||||
|
|
||||||
|
|
||||||
ValidationTuple = typing.Tuple[bool, ValidationErrorDict | None, typing.List[str], dict | list]
|
ValidationTuple = typing.Tuple[bool, Optional[ValidationErrorDict], typing.List[str], Union[dict, list]]
|
||||||
|
|
||||||
|
|
||||||
def validate_prompt(prompt: typing.Mapping[str, typing.Any]) -> ValidationTuple:
|
def validate_prompt(prompt: typing.Mapping[str, typing.Any]) -> ValidationTuple:
|
||||||
|
|||||||
@ -267,3 +267,11 @@ def get_save_image_path(filename_prefix, output_dir, image_width=0, image_height
|
|||||||
os.makedirs(full_output_folder, exist_ok=True)
|
os.makedirs(full_output_folder, exist_ok=True)
|
||||||
counter = 1
|
counter = 1
|
||||||
return full_output_folder, filename, counter, subfolder, filename_prefix
|
return full_output_folder, filename, counter, subfolder, filename_prefix
|
||||||
|
|
||||||
|
|
||||||
|
def create_directories():
|
||||||
|
for _, (paths, _) in folder_names_and_paths.items():
|
||||||
|
default_path = paths[0]
|
||||||
|
os.makedirs(default_path, exist_ok=True)
|
||||||
|
for path in (temp_directory, input_directory, output_directory, user_directory):
|
||||||
|
os.makedirs(path, exist_ok=True)
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
from .. import options
|
from .. import options
|
||||||
|
|
||||||
options.enable_args_parsing()
|
options.enable_args_parsing()
|
||||||
@ -206,6 +208,15 @@ def main():
|
|||||||
folder_paths.set_temp_directory(temp_dir)
|
folder_paths.set_temp_directory(temp_dir)
|
||||||
cleanup_temp()
|
cleanup_temp()
|
||||||
|
|
||||||
|
# create the default directories if we're instructed to, then exit
|
||||||
|
# or, if it's a windows standalone build, the single .exe file should have its side-by-side directories always created
|
||||||
|
if args.create_directories:
|
||||||
|
folder_paths.create_directories()
|
||||||
|
return
|
||||||
|
|
||||||
|
if args.windows_standalone_build:
|
||||||
|
folder_paths.create_directories()
|
||||||
|
|
||||||
loop = asyncio.new_event_loop()
|
loop = asyncio.new_event_loop()
|
||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
server = server_module.PromptServer(loop)
|
server = server_module.PromptServer(loop)
|
||||||
|
|||||||
@ -576,8 +576,10 @@ class PromptServer(ExecutorToClientProgress):
|
|||||||
upload_dir = PromptServer.get_upload_dir()
|
upload_dir = PromptServer.get_upload_dir()
|
||||||
async with aiofiles.open(os.path.join(upload_dir, part.filename), mode='wb') as file:
|
async with aiofiles.open(os.path.join(upload_dir, part.filename), mode='wb') as file:
|
||||||
await file.write(file_data)
|
await file.write(file_data)
|
||||||
except IOError | MemoryError as ioError:
|
except IOError as ioError:
|
||||||
return web.Response(status=507, reason=str(ioError))
|
return web.Response(status=507, reason=str(ioError))
|
||||||
|
except MemoryError as memoryError:
|
||||||
|
return web.Response(status=507, reason=str(memoryError))
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
return web.Response(status=400, reason=str(ex))
|
return web.Response(status=400, reason=str(ex))
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import typing
|
import typing
|
||||||
from abc import ABCMeta, abstractmethod
|
from abc import ABCMeta, abstractmethod
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,7 @@
|
|||||||
from typing import Optional, Literal, Protocol, TypedDict, NotRequired
|
from __future__ import annotations # for Python 3.7-3.9
|
||||||
|
|
||||||
|
from typing_extensions import NotRequired, TypedDict
|
||||||
|
from typing import Optional, Literal, Protocol
|
||||||
|
|
||||||
from comfy.component_model.queue_types import BinaryEventTypes
|
from comfy.component_model.queue_types import BinaryEventTypes
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,8 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import NamedTuple, Optional, TypedDict, List, Literal, NotRequired
|
from typing import NamedTuple, Optional, List, Literal
|
||||||
|
from typing_extensions import NotRequired, TypedDict
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from asyncio import AbstractEventLoop
|
from asyncio import AbstractEventLoop
|
||||||
from typing import Optional, Dict, List, Mapping, Tuple, Callable
|
from typing import Optional, Dict, List, Mapping, Tuple, Callable
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
from typing import Optional, OrderedDict, List, Dict
|
from typing import Optional, OrderedDict, List, Dict
|
||||||
import collections
|
import collections
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import asyncio
|
|||||||
import pytest
|
import pytest
|
||||||
import torch
|
import torch
|
||||||
|
|
||||||
|
from comfy.cli_args_types import Configuration
|
||||||
from comfy.client.embedded_comfy_client import EmbeddedComfyClient
|
from comfy.client.embedded_comfy_client import EmbeddedComfyClient
|
||||||
from comfy.client.sdxl_with_refiner_workflow import sdxl_workflow_with_refiner
|
from comfy.client.sdxl_with_refiner_workflow import sdxl_workflow_with_refiner
|
||||||
|
|
||||||
@ -34,6 +35,14 @@ async def test_embedded_comfy():
|
|||||||
outputs = await client.queue_prompt(prompt)
|
outputs = await client.queue_prompt(prompt)
|
||||||
assert outputs["13"]["images"][0]["abs_path"] is not None
|
assert outputs["13"]["images"][0]["abs_path"] is not None
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_configuration_options():
|
||||||
|
config = Configuration()
|
||||||
|
async with EmbeddedComfyClient(configuration=config) as client:
|
||||||
|
prompt = sdxl_workflow_with_refiner("test")
|
||||||
|
outputs = await client.queue_prompt(prompt)
|
||||||
|
assert outputs["13"]["images"][0]["abs_path"] is not None
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_multithreaded_comfy():
|
async def test_multithreaded_comfy():
|
||||||
async with EmbeddedComfyClient(max_workers=2) as client:
|
async with EmbeddedComfyClient(max_workers=2) as client:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user