Files
Hotel-Booking/Backend/venv/lib/python3.12/site-packages/safety/tool/uv/main.py
Iliyan Angelov 62c1fe5951 updates
2025-12-01 06:50:10 +02:00

240 lines
7.5 KiB
Python

import logging
import os
from pathlib import Path
import shutil
import sys
from typing import Any, Dict, Optional
import tomlkit
import typer
from rich.console import Console
from safety.console import main_console
from safety.tool.auth import build_index_url
from safety.tool.constants import (
PYPI_ORGANIZATION_REPOSITORY_URL,
PYPI_PUBLIC_REPOSITORY_URL,
PYPI_PROJECT_REPOSITORY_URL,
)
from safety.utils.pyapp_utils import get_path
if sys.version_info >= (3, 11):
import tomllib
else:
import tomli as tomllib
logger = logging.getLogger(__name__)
def backup_file(path: Path) -> None:
"""
Create backup of file if it exists
"""
if path.exists():
backup_path = path.with_name(f"{path.name}.backup")
shutil.copy2(path, backup_path)
class Uv:
@classmethod
def is_installed(cls) -> bool:
"""
Checks if the UV program is installed
Returns:
True if UV is installed on system, or false otherwise
"""
return shutil.which("uv", path=get_path()) is not None
@classmethod
def is_uv_project_file(cls, file: Path) -> bool:
try:
cfg = tomllib.loads(file.read_text())
return (
cfg.get("tool", {}).get("uv") is not None
or (file.parent / "uv.lock").exists()
)
except (IOError, ValueError):
return False
@classmethod
def configure_pyproject(
cls,
file: Path,
org_slug: Optional[str],
project_id: Optional[str] = None,
console: Console = main_console,
) -> Optional[Path]:
"""
Configures index url for specified pyproject.toml file.
Args:
file (Path): Path to pyproject.toml file.
org_slug (Optional[str]): Organization slug.
project_id (Optional[str]): Project ID.
console (Console): Console instance.
"""
if not cls.is_installed():
logger.error("UV is not installed.")
return None
repository_url = (
PYPI_PROJECT_REPOSITORY_URL.format(org_slug, project_id)
if project_id and org_slug
else (
PYPI_ORGANIZATION_REPOSITORY_URL.format(org_slug)
if org_slug
else PYPI_PUBLIC_REPOSITORY_URL
)
)
try:
content = file.read_text()
doc: Dict[str, Any] = tomlkit.loads(content)
if "tool" not in doc:
doc["tool"] = tomlkit.table()
if "uv" not in doc["tool"]: # type: ignore
doc["tool"]["uv"] = tomlkit.table() # type: ignore
if "index" not in doc["tool"]["uv"]: # type: ignore
doc["tool"]["uv"]["index"] = tomlkit.aot() # type: ignore
index_container = doc["tool"]["uv"] # type: ignore
cls.filter_out_safety_index(index_container)
safety_index = {
"name": "safety",
"url": repository_url,
# In UV default:
# True = lowest priority
# False = highest priority
"default": False,
}
non_safety_indexes = (
doc.get("tool", {}).get("uv", {}).get("index", tomlkit.aot())
)
# Add safety index as first priority
index_container["index"] = tomlkit.aot() # type: ignore
index_container["index"].append(safety_index) # type: ignore
index_container["index"].extend(non_safety_indexes) # type: ignore
# Write back to file
file.write_text(tomlkit.dumps(doc))
return file
except (IOError, ValueError, Exception) as e:
logger.error(f"Failed to configure {file} file: {e}")
return None
@classmethod
def get_user_config_path(cls) -> Path:
"""
Returns the path to the user config file for UV.
This logic is based on the uv documentation:
https://docs.astral.sh/uv/configuration/files/
"uv will also discover user-level configuration at
~/.config/uv/uv.toml (or $XDG_CONFIG_HOME/uv/uv.toml) on macOS and Linux,
or %APPDATA%\\uv\\uv.toml on Windows; ..."
Returns:
Path: The path to the user config file.
"""
if sys.platform == "win32":
return Path(os.environ.get("APPDATA", ""), "uv", "uv.toml")
else:
xdg_config_home = os.environ.get("XDG_CONFIG_HOME")
if xdg_config_home:
return Path(xdg_config_home, "uv", "uv.toml")
else:
return Path(Path.home(), ".config", "uv", "uv.toml")
@classmethod
def filter_out_safety_index(cls, index_container: Any):
if "index" not in index_container:
return
indexes = list(index_container["index"])
index_container["index"] = tomlkit.aot()
for index in indexes:
index_url = index.get("url", "")
if ".safetycli.com" in index_url:
continue
index_container["index"].append(index)
@classmethod
def configure_system(
cls, org_slug: Optional[str], console: Console = main_console
) -> Optional[Path]:
"""
Configures UV system to use to Safety index url.
"""
try:
repository_url = (
PYPI_ORGANIZATION_REPOSITORY_URL.format(org_slug)
if org_slug
else PYPI_PUBLIC_REPOSITORY_URL
)
user_config_path = cls.get_user_config_path()
if not user_config_path.exists():
user_config_path.parent.mkdir(parents=True, exist_ok=True)
content = ""
else:
backup_file(user_config_path)
content = user_config_path.read_text()
doc = tomlkit.loads(content)
if "index" not in doc:
doc["index"] = tomlkit.aot()
cls.filter_out_safety_index(index_container=doc)
safety_index = tomlkit.aot()
safety_index.append(
{
"name": "safety",
"url": repository_url,
# In UV default:
# True = lowest priority
# False = highest priority
"default": False,
}
)
non_safety_indexes = doc.get("index", tomlkit.aot())
# Add safety index as first priority
doc["index"] = tomlkit.aot()
doc.append("index", safety_index)
doc.append("index", non_safety_indexes)
user_config_path.write_text(tomlkit.dumps(doc))
return user_config_path
except Exception as e:
logger.error(f"Failed to configure UV system: {e}")
return None
@classmethod
def reset_system(cls, console: Console = main_console):
try:
user_config_path = cls.get_user_config_path()
if user_config_path.exists():
backup_file(user_config_path)
content = user_config_path.read_text()
doc = tomlkit.loads(content)
cls.filter_out_safety_index(index_container=doc)
user_config_path.write_text(tomlkit.dumps(doc))
except Exception as e:
msg = "Failed to reset UV global settings"
logger.error(f"{msg}: {e}")
@classmethod
def build_index_url(cls, ctx: typer.Context, index_url: Optional[str]) -> str:
return build_index_url(ctx, index_url, "pypi")