add deprecated code for compability
This commit is contained in:
233
deprecated/program/Engine/Instance.py
Normal file
233
deprecated/program/Engine/Instance.py
Normal file
@@ -0,0 +1,233 @@
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from typing import Optional, List
|
||||
from dataclasses import dataclass
|
||||
from .Environments.venv import Venv
|
||||
|
||||
|
||||
@dataclass
|
||||
class InstanceConfig:
|
||||
"""Конфигурация экземпляра приложения"""
|
||||
path: Path
|
||||
requirements_dir: Path
|
||||
manual_requirements: Path
|
||||
freezed_packages: Path
|
||||
created: bool = False
|
||||
|
||||
|
||||
class Instance:
|
||||
"""Базовый класс для управления изолированными окружениями"""
|
||||
|
||||
def __init__(self, path: str, env: str = "venv"):
|
||||
self.path = Path(path)
|
||||
self.config = InstanceConfig(
|
||||
path=self.path,
|
||||
requirements_dir=self.path / ".vaiola" / "requirements.d",
|
||||
manual_requirements=self.path / ".vaiola" / "requirements.txt",
|
||||
freezed_packages=self.path / ".vaiola" / "freezed_packages.txt",
|
||||
created=(self.path / ".vaiola").exists()
|
||||
)
|
||||
|
||||
self.freezed_packages_list: List[str] = []
|
||||
|
||||
if env == "venv":
|
||||
self.env = Venv(self.path / "venv")
|
||||
|
||||
if not self.config.created:
|
||||
self.env.create()
|
||||
self.config.requirements_dir.mkdir(parents=True, exist_ok=True)
|
||||
self.config.freezed_packages.touch()
|
||||
self.config.manual_requirements.touch()
|
||||
else:
|
||||
self._load_freezed_packages()
|
||||
|
||||
def _load_freezed_packages(self) -> None:
|
||||
"""Загружает список замороженных пакетов из файла"""
|
||||
if self.config.freezed_packages.exists():
|
||||
with open(self.config.freezed_packages, 'r') as f:
|
||||
self.freezed_packages_list = [
|
||||
line.strip() for line in f.readlines() if line.strip()
|
||||
]
|
||||
|
||||
def _parse_package_name(self, package_spec: str) -> str:
|
||||
"""Извлекает имя пакета из спецификации (убирает версию и доп. параметры)"""
|
||||
# Удаляем версии и дополнительные параметры
|
||||
name = re.split(r'[<>=!~]', package_spec)[0].strip()
|
||||
# Удаляем экстра-параметры (в квадратных скобках)
|
||||
if '[' in name:
|
||||
name = name.split('[')[0].strip()
|
||||
return name
|
||||
|
||||
def _is_package_freezed(self, package_spec: str) -> bool:
|
||||
"""Проверяет, заморожен ли пакет"""
|
||||
package_name = self._parse_package_name(package_spec)
|
||||
return package_name in self.freezed_packages_list
|
||||
|
||||
def _remove_package_from_file(self, package_spec: str, file_path: Path) -> None:
|
||||
"""Удаляет пакет из указанного файла"""
|
||||
package_name = self._parse_package_name(package_spec)
|
||||
|
||||
if file_path.exists():
|
||||
with open(file_path, 'r') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
# Фильтруем строки, убирая те, что содержат этот пакет
|
||||
filtered_lines = [
|
||||
line for line in lines
|
||||
if self._parse_package_name(line.strip()) != package_name
|
||||
]
|
||||
|
||||
with open(file_path, 'w') as f:
|
||||
f.writelines(filtered_lines)
|
||||
|
||||
|
||||
def check_consistency(self) -> bool:
|
||||
"""Проверяет целостность окружения"""
|
||||
# TODO: Реализовать проверку целостности
|
||||
return True
|
||||
|
||||
def install_requirements(
|
||||
self,
|
||||
requirements: List[str],
|
||||
name: str,
|
||||
force: bool = False,
|
||||
extra_index_url: Optional[str] = None
|
||||
) -> None:
|
||||
"""Устанавливает требования из списка"""
|
||||
requirements_file = self.config.requirements_dir / f"{name}.req"
|
||||
requirements_file.write_text("\n".join(requirements))
|
||||
|
||||
if force:
|
||||
self.env.install_requirements(requirements, extra_index_url)
|
||||
|
||||
def install_requirements_file(
|
||||
self,
|
||||
requirements_file: str,
|
||||
name: str,
|
||||
force: bool = False,
|
||||
requirement_level: str = 'req',
|
||||
extra_index_url: Optional[str] = None
|
||||
) -> None:
|
||||
"""Устанавливает требования из файла"""
|
||||
target_file = self.config.requirements_dir / f"{name}.{requirement_level}"
|
||||
shutil.copy2(requirements_file, target_file)
|
||||
|
||||
if force:
|
||||
self.env.install_requirements_file(requirements_file, extra_index_url)
|
||||
|
||||
def install_package(
|
||||
self,
|
||||
package: str,
|
||||
extra_index_url: Optional[str] = None,
|
||||
force: bool = False,
|
||||
freeze: bool = False
|
||||
) -> None:
|
||||
"""
|
||||
Устанавливает пакет с учетом всех требований
|
||||
|
||||
Args:
|
||||
package: Спецификация пакета в формате pip
|
||||
extra_index_url: Дополнительный URL репозитория
|
||||
force: Принудительная установка, даже если пакет заморожен
|
||||
freeze: Заморозить пакет после установки
|
||||
"""
|
||||
# Шаг 1: Проверка флага force
|
||||
if not force:
|
||||
# Шаг 2: Проверка на заморозку
|
||||
if self._is_package_freezed(package):
|
||||
raise ValueError(f"Пакет {self._parse_package_name(package)} заморожен и не может быть установлен")
|
||||
|
||||
# Шаг 3: Проверка целостности
|
||||
if not self.check_integrity():
|
||||
raise RuntimeError("Невозможно установить пакет: проверка целостности не пройдена")
|
||||
|
||||
# Шаг 4: Установка пакета
|
||||
try:
|
||||
self.env.install_package(package, extra_index_url)
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"Ошибка установки пакета {package}: {e}")
|
||||
|
||||
# Шаг 5: Удаление существующих упоминаний пакета
|
||||
self._remove_package_from_file(package, self.config.manual_requirements)
|
||||
|
||||
# Шаг 6: Добавление пакета в manual_requirements
|
||||
with open(self.config.manual_requirements, 'a') as f:
|
||||
f.write(f"{package}\n")
|
||||
|
||||
# Шаг 7: Проверка флага freeze
|
||||
if not freeze:
|
||||
return
|
||||
|
||||
# Шаг 8: Удаление из freezed_packages
|
||||
self._remove_package_from_file(package, self.config.freezed_packages)
|
||||
self._load_freezed_packages() # Обновляем список
|
||||
|
||||
# Шаг 9: Добавление в freezed_packages (только имя пакета)
|
||||
package_name = self._parse_package_name(package)
|
||||
with open(self.config.freezed_packages, 'a') as f:
|
||||
f.write(f"{package_name}\n")
|
||||
self.freezed_packages_list.append(package_name)
|
||||
|
||||
def install_optional_requirements(
|
||||
self,
|
||||
requirements_file: str,
|
||||
name: str,
|
||||
force: bool = False
|
||||
) -> None:
|
||||
"""Устанавливает опциональные требования"""
|
||||
self.install_requirements_file(
|
||||
requirements_file, name, force, requirement_level='opt'
|
||||
)
|
||||
|
||||
|
||||
class GenericPyTorchInstance(Instance):
|
||||
"""Базовый класс для экземпляров с PyTorch"""
|
||||
|
||||
BASE_URL = "https://download.pytorch.org/whl/"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
path: str,
|
||||
api: str,
|
||||
torch_version: str,
|
||||
torchvision_version: str,
|
||||
torchaudio_version: str,
|
||||
env: str = "venv",
|
||||
freeze_torch: bool = True
|
||||
):
|
||||
super().__init__(path, env)
|
||||
|
||||
self.url = f"{self.BASE_URL.rstrip('/')}/{api}"
|
||||
self.torch_version = torch_version
|
||||
self.torchvision_version = torchvision_version
|
||||
self.torchaudio_version = torchaudio_version
|
||||
|
||||
# Установка PyTorch компонентов
|
||||
self.install_package(f"torch=={torch_version}", self.url, force=True)
|
||||
self.install_package(f"torchvision=={torchvision_version}", self.url, force=True)
|
||||
self.install_package(f"torchaudio=={torchaudio_version}", self.url, force=True)
|
||||
|
||||
if freeze_torch:
|
||||
self.freezed_packages.extend([
|
||||
f"torch=={torch_version}",
|
||||
f"torchvision=={torchvision_version}",
|
||||
f"torchaudio=={torchaudio_version}"
|
||||
])
|
||||
|
||||
|
||||
class ComfyUIInstance(GenericPyTorchInstance):
|
||||
"""Специализированный класс для ComfyUI"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
path: str,
|
||||
api: str,
|
||||
torch_version: str,
|
||||
torchvision_version: str,
|
||||
torchaudio_version: str,
|
||||
env: str = "venv"
|
||||
):
|
||||
super().__init__(
|
||||
path, api, torch_version, torchvision_version, torchaudio_version, env
|
||||
)
|
||||
self.app_dir = self.path / 'app'
|
||||
257
deprecated/program/Engine/Resolver.py
Normal file
257
deprecated/program/Engine/Resolver.py
Normal file
@@ -0,0 +1,257 @@
|
||||
import re
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import List, Dict, Tuple, Optional, Union
|
||||
from dataclasses import dataclass
|
||||
from collections import defaultdict
|
||||
|
||||
@dataclass
|
||||
class ErrorDesc:
|
||||
"""Контейнер для информации об ошибках разрешения зависимостей"""
|
||||
package: str
|
||||
conflict_package: str
|
||||
error_message: str
|
||||
|
||||
class Resolver:
|
||||
"""Статический класс для разрешения зависимостей пакетов"""
|
||||
|
||||
@staticmethod
|
||||
def _parse_package_name(package_spec: str) -> str:
|
||||
"""Извлекает чистое имя пакета из спецификации"""
|
||||
# Удаляем версии, условия и экстра-параметры
|
||||
name = re.split(r'[<>=!~\[\]]', package_spec)[0].strip()
|
||||
return name
|
||||
|
||||
@staticmethod
|
||||
def _load_packages_from_file(file_path: Path) -> List[str]:
|
||||
"""Загружает список пакетов из файла"""
|
||||
if not file_path.exists():
|
||||
return []
|
||||
|
||||
with open(file_path, 'r') as f:
|
||||
lines = [line.strip() for line in f.readlines() if line.strip() and not line.startswith('#')]
|
||||
|
||||
return lines
|
||||
|
||||
@staticmethod
|
||||
def _load_packages_from_dir(dir_path: Path, extension: str) -> Dict[str, List[str]]:
|
||||
"""Загружает пакеты из всех файлов в директории с указанным расширением"""
|
||||
packages_dict = {}
|
||||
|
||||
if not dir_path.exists():
|
||||
return packages_dict
|
||||
|
||||
for file_path in dir_path.glob(f"*.{extension}"):
|
||||
key = file_path.stem
|
||||
packages_dict[key] = Resolver._load_packages_from_file(file_path)
|
||||
|
||||
return packages_dict
|
||||
|
||||
@staticmethod
|
||||
def _remove_freezed_packages(packages_list: List[str], freezed_packages: List[str]) -> List[str]:
|
||||
"""Удаляет замороженные пакеты из списка"""
|
||||
freezed_names = [Resolver._parse_package_name(pkg) for pkg in freezed_packages]
|
||||
return [
|
||||
pkg for pkg in packages_list
|
||||
if Resolver._parse_package_name(pkg) not in freezed_names
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def _run_pip_dry_run(packages: List[str]) -> Tuple[bool, str, Optional[str]]:
|
||||
"""Запускает pip install --dry-run и возвращает результат"""
|
||||
if not packages:
|
||||
return True, "", None
|
||||
|
||||
command = ["python", "-m", "pip", "install", "--dry-run"] + packages
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
command,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=30
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
return True, result.stdout, None
|
||||
else:
|
||||
return False, result.stdout, result.stderr
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
return False, "", "Timeout exceeded"
|
||||
except Exception as e:
|
||||
return False, "", str(e)
|
||||
|
||||
@staticmethod
|
||||
def _find_conflicting_packages(error_output: str, all_packages: List[str]) -> Tuple[Optional[str], Optional[str]]:
|
||||
"""Анализирует вывод pip для поиска конфликтующих пакетов"""
|
||||
# Паттерны для поиска конфликтующих пакетов в выводе pip
|
||||
patterns = [
|
||||
r"ERROR: Cannot install (.+?) because (.+?) conflicts with (.+?)",
|
||||
r"ERROR: (.+?) is incompatible with (.+?)",
|
||||
r"Conflict: (.+?) conflicts with (.+?)",
|
||||
]
|
||||
|
||||
for pattern in patterns:
|
||||
match = re.search(pattern, error_output)
|
||||
if match:
|
||||
groups = match.groups()
|
||||
for i in range(len(groups)):
|
||||
for package in all_packages:
|
||||
if groups[i] in package:
|
||||
if i + 1 < len(groups):
|
||||
for conflict_package in all_packages:
|
||||
if groups[i + 1] in conflict_package:
|
||||
return package, conflict_package
|
||||
break
|
||||
|
||||
return None, None
|
||||
|
||||
@staticmethod
|
||||
def check_integrity(
|
||||
requested_packages: List[str],
|
||||
manual_requirements_path: Path,
|
||||
requirements_dir: Path,
|
||||
freezed_packages_path: Path,
|
||||
required: bool = True,
|
||||
interactive: bool = False
|
||||
) -> Union[List[str], Tuple[List[str], List[ErrorDesc]]]:
|
||||
"""
|
||||
Проверяет целостность и разрешает конфликты зависимостей
|
||||
|
||||
Returns:
|
||||
Union[List[str], Tuple[List[str], List[ErrorDesc]]]:
|
||||
Список пакетов или кортеж (список пакетов, список ошибок)
|
||||
"""
|
||||
# ЭТАП A: Загрузка
|
||||
freezed_packages = Resolver._load_packages_from_file(freezed_packages_path)
|
||||
|
||||
# Загружаем manual requirements
|
||||
manual_packages = Resolver._load_packages_from_file(manual_requirements_path)
|
||||
manual_packages = Resolver._remove_freezed_packages(manual_packages, freezed_packages)
|
||||
|
||||
# Загружаем req и opt файлы
|
||||
req_dict = Resolver._load_packages_from_dir(requirements_dir, "req")
|
||||
opt_dict = Resolver._load_packages_from_dir(requirements_dir, "opt")
|
||||
|
||||
# Удаляем замороженные пакеты из всех словарей
|
||||
for key in list(req_dict.keys()):
|
||||
req_dict[key] = Resolver._remove_freezed_packages(req_dict[key], freezed_packages)
|
||||
|
||||
for key in list(opt_dict.keys()):
|
||||
opt_dict[key] = Resolver._remove_freezed_packages(opt_dict[key], freezed_packages)
|
||||
|
||||
# Формируем общие списки
|
||||
all_req = manual_packages + [pkg for packages in req_dict.values() for pkg in packages]
|
||||
all_opt = [pkg for packages in opt_dict.values() for pkg in packages]
|
||||
|
||||
# Удаляем замороженные пакеты из запрошенных
|
||||
requested_packages = Resolver._remove_freezed_packages(requested_packages, freezed_packages)
|
||||
|
||||
# ЭТАП Б: Обработка ошибок
|
||||
all_packages = all_req + all_opt + requested_packages
|
||||
errors = []
|
||||
|
||||
while True:
|
||||
success, stdout, stderr = Resolver._run_pip_dry_run(all_packages)
|
||||
|
||||
if success:
|
||||
break
|
||||
|
||||
if not stderr:
|
||||
raise RuntimeError("Pip failed but no error output available")
|
||||
|
||||
# Ищем конфликтующие пакеты
|
||||
conflict_package, conflict_with = Resolver._find_conflicting_packages(stderr, all_packages)
|
||||
|
||||
if not conflict_package or not conflict_with:
|
||||
# Если не удалось определить конфликт, используем эвристику
|
||||
if required:
|
||||
if all_opt:
|
||||
# Удаляем первый опциональный пакет
|
||||
removed_package = all_opt.pop(0)
|
||||
all_packages.remove(removed_package)
|
||||
errors.append(ErrorDesc(
|
||||
package=removed_package,
|
||||
conflict_package="unknown",
|
||||
error_message=stderr
|
||||
))
|
||||
continue
|
||||
else:
|
||||
if requested_packages:
|
||||
# Удаляем первый запрошенный пакет
|
||||
removed_package = requested_packages.pop(0)
|
||||
all_packages.remove(removed_package)
|
||||
errors.append(ErrorDesc(
|
||||
package=removed_package,
|
||||
conflict_package="unknown",
|
||||
error_message=stderr
|
||||
))
|
||||
continue
|
||||
|
||||
# Если нечего удалять, выбрасываем исключение
|
||||
raise RuntimeError(f"Unresolvable conflict: {stderr}")
|
||||
|
||||
# Определяем, в каком списке находится конфликтующий пакет
|
||||
conflict_in_opt = any(conflict_package in packages for packages in opt_dict.values())
|
||||
conflict_in_req = any(conflict_package in packages for packages in req_dict.values()) or conflict_package in manual_packages
|
||||
|
||||
# Определяем, в каком списке находится пакет, с которым конфликт
|
||||
conflict_with_in_opt = any(conflict_with in packages for packages in opt_dict.values())
|
||||
conflict_with_in_req = any(conflict_with in packages for packages in req_dict.values()) or conflict_with in manual_packages
|
||||
|
||||
# Принимаем решение об удалении пакета
|
||||
if conflict_in_opt and required:
|
||||
# Удаляем из опциональных
|
||||
for key in list(opt_dict.keys()):
|
||||
if conflict_package in opt_dict[key]:
|
||||
opt_dict[key].remove(conflict_package)
|
||||
all_packages.remove(conflict_package)
|
||||
break
|
||||
else:
|
||||
# Удаляем из запрошенных
|
||||
if conflict_package in requested_packages:
|
||||
requested_packages.remove(conflict_package)
|
||||
all_packages.remove(conflict_package)
|
||||
else:
|
||||
# Если конфликтующий пакет не в запрошенных, удаляем его из всех списков
|
||||
for key in list(req_dict.keys()):
|
||||
if conflict_package in req_dict[key]:
|
||||
req_dict[key].remove(conflict_package)
|
||||
all_packages.remove(conflict_package)
|
||||
break
|
||||
for key in list(opt_dict.keys()):
|
||||
if conflict_package in opt_dict[key]:
|
||||
opt_dict[key].remove(conflict_package)
|
||||
all_packages.remove(conflict_package)
|
||||
break
|
||||
|
||||
# Добавляем информацию об ошибке
|
||||
errors.append(ErrorDesc(
|
||||
package=conflict_package,
|
||||
conflict_package=conflict_with,
|
||||
error_message=stderr
|
||||
))
|
||||
|
||||
# ЭТАП В: Подведение итогов
|
||||
if interactive and errors:
|
||||
print("Обнаружены конфликты зависимостей:")
|
||||
for error in errors:
|
||||
print(f"- {error.package} конфликтует с {error.conflict_package}")
|
||||
print(f" Ошибка: {error.error_message[:100]}...")
|
||||
|
||||
response = input("Продолжить установку с разрешенными конфликтами? (y/n): ")
|
||||
if response.lower() != 'y':
|
||||
raise RuntimeError("Установка отменена пользователем")
|
||||
|
||||
if not required:
|
||||
return requested_packages
|
||||
else:
|
||||
# Проверяем, изменился ли список запрошенных пакетов
|
||||
original_requested = set(requested_packages)
|
||||
current_requested = set(requested_packages)
|
||||
|
||||
if original_requested != current_requested:
|
||||
raise RuntimeError("Неразрешимый конфликт: пришлось удалить запрошенные пакеты")
|
||||
|
||||
return requested_packages
|
||||
41
deprecated/program/UI/NumericMenu.py
Normal file
41
deprecated/program/UI/NumericMenu.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from ..Web.pytorch import *
|
||||
from ..Instance import *
|
||||
|
||||
|
||||
class NumericMenu:
|
||||
|
||||
@staticmethod
|
||||
def print_menu(items: list[str], prompt: str = "Choise: ") -> int:
|
||||
for i in range(len(items)): print(f'{i + 1}. {items[i]}')
|
||||
return int(input(prompt))
|
||||
|
||||
@classmethod
|
||||
def menu_create_instance_generic_pytorch_application(cls):
|
||||
api = input("Type your api version code. Ex. cu121 for cuda 12.1: ")
|
||||
t = pytorch.versions_torch(api)
|
||||
choise = cls.print_menu(t, "torch version: ")
|
||||
t = t[choise - 1]
|
||||
tv = pytorch.versions_torchvision(api)
|
||||
choise = cls.print_menu(tv, "torchvision version: ")
|
||||
tv = tv[choise - 1]
|
||||
ta = pytorch.versions_torchaudio(api)
|
||||
choise = cls.print_menu(ta, "torchaudio version: ")
|
||||
ta = ta[choise - 1]
|
||||
print("Pytorch version:", t, "torchvision:", tv, "torchaudio:", ta)
|
||||
path = input("Instance path: ")
|
||||
instance = GenericPytorchInstance(path, api, t, tv, ta)
|
||||
|
||||
@classmethod
|
||||
def menu_create_instance(cls):
|
||||
choise = cls.print_menu(['Generic PyTorch Application', 'ComfyUI', 'Stable Diffusion Forge'])
|
||||
if choise == 1: cls.menu_create_instance_generic_pytorch_application()
|
||||
|
||||
|
||||
|
||||
|
||||
@classmethod
|
||||
def start(cls):
|
||||
choise = cls.print_menu(['Create instance'])
|
||||
if choise == 1:
|
||||
cls.menu_create_instance()
|
||||
|
||||
BIN
deprecated/program/__pycache__/Instance.cpython-313.pyc
Normal file
BIN
deprecated/program/__pycache__/Instance.cpython-313.pyc
Normal file
Binary file not shown.
Reference in New Issue
Block a user