feat(dev_reload): use jurigged to hot reload changes to Python source (#4313)

This commit is contained in:
Kevin Turner 2023-08-25 14:27:40 -07:00 committed by GitHub
commit 6b462f2ed5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 43 additions and 13 deletions

View File

@ -35,18 +35,17 @@ access.
## Backend ## Backend
The backend is contained within the `./invokeai/backend` folder structure. To The backend is contained within the `./invokeai/backend` and `./invokeai/app` directories.
get started however please install the development dependencies. To get started please install the development dependencies.
From the root of the repository run the following command. Note the use of `"`. From the root of the repository run the following command. Note the use of `"`.
```zsh ```zsh
pip install ".[test]" pip install ".[dev,test]"
``` ```
This in an optional group of packages which is defined within the These are optional groups of packages which are defined within the `pyproject.toml`
`pyproject.toml` and will be required for testing the changes you make the the and will be required for testing the changes you make to the code.
code.
### Running Tests ### Running Tests
@ -76,6 +75,20 @@ pytest --cov; open ./coverage/html/index.html
![html-detail](../assets/contributing/html-detail.png) ![html-detail](../assets/contributing/html-detail.png)
### Reloading Changes
Experimenting with changes to the Python source code is a drag if you have to re-start the server —
and re-load those multi-gigabyte models —
after every change.
For a faster development workflow, add the `--dev_reload` flag when starting the server.
The server will watch for changes to all the Python files in the `invokeai` directory and apply those changes to the
running server on the fly.
This will allow you to avoid restarting the server (and reloading models) in most cases, but there are some caveats; see
the [jurigged documentation](https://github.com/breuleux/jurigged#caveats) for details.
## Front End ## Front End
<!--#TODO: get input from blessedcoolant here, for the moment inserted the frontend README via snippets extension.--> <!--#TODO: get input from blessedcoolant here, for the moment inserted the frontend README via snippets extension.-->

View File

@ -1,11 +1,11 @@
# Copyright (c) 2022-2023 Kyle Schouviller (https://github.com/kyle0654) and the InvokeAI Team # Copyright (c) 2022-2023 Kyle Schouviller (https://github.com/kyle0654) and the InvokeAI Team
import asyncio import asyncio
from inspect import signature
import logging import logging
import uvicorn
import socket import socket
from inspect import signature
from pathlib import Path
import uvicorn
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html
@ -13,7 +13,6 @@ from fastapi.openapi.utils import get_openapi
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from fastapi_events.handlers.local import local_handler from fastapi_events.handlers.local import local_handler
from fastapi_events.middleware import EventHandlerASGIMiddleware from fastapi_events.middleware import EventHandlerASGIMiddleware
from pathlib import Path
from pydantic.schema import schema from pydantic.schema import schema
from .services.config import InvokeAIAppConfig from .services.config import InvokeAIAppConfig
@ -30,9 +29,12 @@ from .api.sockets import SocketIO
from .invocations.baseinvocation import BaseInvocation, _InputField, _OutputField, UIConfigBase from .invocations.baseinvocation import BaseInvocation, _InputField, _OutputField, UIConfigBase
import torch import torch
# noinspection PyUnresolvedReferences
import invokeai.backend.util.hotfixes # noqa: F401 (monkeypatching on import) import invokeai.backend.util.hotfixes # noqa: F401 (monkeypatching on import)
if torch.backends.mps.is_available(): if torch.backends.mps.is_available():
# noinspection PyUnresolvedReferences
import invokeai.backend.util.mps_fixes # noqa: F401 (monkeypatching on import) import invokeai.backend.util.mps_fixes # noqa: F401 (monkeypatching on import)
@ -40,7 +42,6 @@ app_config = InvokeAIAppConfig.get_config()
app_config.parse_args() app_config.parse_args()
logger = InvokeAILogger.getLogger(config=app_config) logger = InvokeAILogger.getLogger(config=app_config)
# fix for windows mimetypes registry entries being borked # fix for windows mimetypes registry entries being borked
# see https://github.com/invoke-ai/InvokeAI/discussions/3684#discussioncomment-6391352 # see https://github.com/invoke-ai/InvokeAI/discussions/3684#discussioncomment-6391352
mimetypes.add_type("application/javascript", ".js") mimetypes.add_type("application/javascript", ".js")
@ -208,6 +209,17 @@ def invoke_api():
check_invokeai_root(app_config) # note, may exit with an exception if root not set up check_invokeai_root(app_config) # note, may exit with an exception if root not set up
if app_config.dev_reload:
try:
import jurigged
except ImportError as e:
logger.error(
'Can\'t start `--dev_reload` because jurigged is not found; `pip install -e ".[dev]"` to include development dependencies.',
exc_info=e,
)
else:
jurigged.watch(logger=InvokeAILogger.getLogger(name="jurigged").info)
port = find_port(app_config.port) port = find_port(app_config.port)
if port != app_config.port: if port != app_config.port:
logger.warn(f"Port {app_config.port} in use, using port {port}") logger.warn(f"Port {app_config.port} in use, using port {port}")

View File

@ -169,11 +169,13 @@ two configs are kept in separate sections of the config file:
""" """
from __future__ import annotations from __future__ import annotations
import os import os
from omegaconf import OmegaConf, DictConfig
from pathlib import Path from pathlib import Path
from typing import ClassVar, Dict, List, Literal, Union, get_type_hints, Optional
from omegaconf import OmegaConf, DictConfig
from pydantic import Field, parse_obj_as from pydantic import Field, parse_obj_as
from typing import ClassVar, Dict, List, Literal, Union, Optional, get_type_hints
from .base import InvokeAISettings from .base import InvokeAISettings
@ -233,6 +235,8 @@ class InvokeAIAppConfig(InvokeAISettings):
log_format : Literal['plain', 'color', 'syslog', 'legacy'] = Field(default="color", description='Log format. Use "plain" for text-only, "color" for colorized output, "legacy" for 2.3-style logging and "syslog" for syslog-style', category="Logging") log_format : Literal['plain', 'color', 'syslog', 'legacy'] = Field(default="color", description='Log format. Use "plain" for text-only, "color" for colorized output, "legacy" for 2.3-style logging and "syslog" for syslog-style', category="Logging")
log_level : Literal["debug", "info", "warning", "error", "critical"] = Field(default="info", description="Emit logging messages at this level or higher", category="Logging") log_level : Literal["debug", "info", "warning", "error", "critical"] = Field(default="info", description="Emit logging messages at this level or higher", category="Logging")
dev_reload : bool = Field(default=False, description="Automatically reload when Python sources are changed.", category="Development")
version : bool = Field(default=False, description="Show InvokeAI version and exit", category="Other") version : bool = Field(default=False, description="Show InvokeAI version and exit", category="Other")
# CACHE # CACHE

View File

@ -93,6 +93,7 @@ dependencies = [
"mkdocs-redirects==1.2.0", "mkdocs-redirects==1.2.0",
] ]
"dev" = [ "dev" = [
"jurigged",
"pudb", "pudb",
] ]
"test" = [ "test" = [