refactor: Enhance profile picture copying with optimized I/O and error handling (#6083)
* feat: enhance profile picture copying with optimized I/O and error handling * fix: correct docstring for copy_profile_pictures function
This commit is contained in:
parent
d24e989a91
commit
f8b08a446c
1 changed files with 49 additions and 11 deletions
|
|
@ -35,12 +35,7 @@ from langflow.services.database.models.folder.utils import (
|
||||||
get_default_folder_id,
|
get_default_folder_id,
|
||||||
)
|
)
|
||||||
from langflow.services.database.models.user.crud import get_user_by_username
|
from langflow.services.database.models.user.crud import get_user_by_username
|
||||||
from langflow.services.deps import (
|
from langflow.services.deps import get_settings_service, get_storage_service, get_variable_service, session_scope
|
||||||
get_settings_service,
|
|
||||||
get_storage_service,
|
|
||||||
get_variable_service,
|
|
||||||
session_scope,
|
|
||||||
)
|
|
||||||
from langflow.template.field.prompt import DEFAULT_PROMPT_INTUT_TYPES
|
from langflow.template.field.prompt import DEFAULT_PROMPT_INTUT_TYPES
|
||||||
from langflow.utils.util import escape_json_dump
|
from langflow.utils.util import escape_json_dump
|
||||||
|
|
||||||
|
|
@ -393,10 +388,27 @@ async def load_starter_projects(retries=3, delay=1) -> list[tuple[anyio.Path, di
|
||||||
|
|
||||||
|
|
||||||
async def copy_profile_pictures() -> None:
|
async def copy_profile_pictures() -> None:
|
||||||
|
"""Asynchronously copies profile pictures from the source directory to the target configuration directory.
|
||||||
|
|
||||||
|
This function copies profile pictures while optimizing I/O operations by:
|
||||||
|
1. Using a set to track existing files and avoid redundant filesystem checks
|
||||||
|
2. Performing bulk copy operations concurrently using asyncio.gather
|
||||||
|
3. Offloading blocking I/O to threads
|
||||||
|
|
||||||
|
The directory structure is:
|
||||||
|
profile_pictures/
|
||||||
|
├── People/
|
||||||
|
│ └── [profile images]
|
||||||
|
└── Space/
|
||||||
|
└── [profile images]
|
||||||
|
"""
|
||||||
|
# Get config directory from settings
|
||||||
config_dir = get_storage_service().settings_service.settings.config_dir
|
config_dir = get_storage_service().settings_service.settings.config_dir
|
||||||
if config_dir is None:
|
if config_dir is None:
|
||||||
msg = "Config dir is not set in the settings"
|
msg = "Config dir is not set in the settings"
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
|
||||||
|
# Setup source and target paths
|
||||||
origin = anyio.Path(__file__).parent / "profile_pictures"
|
origin = anyio.Path(__file__).parent / "profile_pictures"
|
||||||
target = anyio.Path(config_dir) / "profile_pictures"
|
target = anyio.Path(config_dir) / "profile_pictures"
|
||||||
|
|
||||||
|
|
@ -404,15 +416,41 @@ async def copy_profile_pictures() -> None:
|
||||||
msg = f"The source folder '{origin}' does not exist."
|
msg = f"The source folder '{origin}' does not exist."
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
|
||||||
|
# Create target dir if needed
|
||||||
if not await target.exists():
|
if not await target.exists():
|
||||||
await target.mkdir(parents=True)
|
await target.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await asyncio.to_thread(shutil.copytree, str(origin), str(target), dirs_exist_ok=True)
|
# Get set of existing files in target to avoid redundant checks
|
||||||
logger.debug(f"Folder copied from '{origin}' to '{target}'")
|
target_files = {str(f.relative_to(target)) async for f in target.rglob("*") if await f.is_file()}
|
||||||
|
|
||||||
except Exception: # noqa: BLE001
|
# Define a helper coroutine to copy a single file concurrently
|
||||||
logger.exception("Error copying the folder")
|
async def copy_file(src_file, dst_file, rel_path):
|
||||||
|
# Create parent directories if needed
|
||||||
|
await dst_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
# Offload blocking I/O to a thread
|
||||||
|
await asyncio.to_thread(shutil.copy2, str(src_file), str(dst_file))
|
||||||
|
logger.debug(f"Copied file '{rel_path}'")
|
||||||
|
|
||||||
|
tasks = []
|
||||||
|
async for src_file in origin.rglob("*"):
|
||||||
|
if not await src_file.is_file():
|
||||||
|
continue
|
||||||
|
|
||||||
|
rel_path = src_file.relative_to(origin)
|
||||||
|
if str(rel_path) not in target_files:
|
||||||
|
dst_file = target / rel_path
|
||||||
|
tasks.append(copy_file(src_file, dst_file, rel_path))
|
||||||
|
else:
|
||||||
|
logger.debug(f"Skipped existing file: '{rel_path}'")
|
||||||
|
|
||||||
|
if tasks:
|
||||||
|
await asyncio.gather(*tasks)
|
||||||
|
|
||||||
|
except Exception as exc:
|
||||||
|
logger.exception("Error copying profile pictures")
|
||||||
|
msg = "An error occurred while copying profile pictures."
|
||||||
|
raise RuntimeError(msg) from exc
|
||||||
|
|
||||||
|
|
||||||
def get_project_data(project):
|
def get_project_data(project):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue