|
| 1 | +#!/bin/bash |
| 2 | + |
| 3 | +# По умолчанию строгий режим выключен |
| 4 | +# set -euo pipefail |
| 5 | + |
| 6 | +trap 'echo "Ошибка на строке $LINENO"' ERR |
| 7 | + |
| 8 | +DEFAULT_VERSION="3.14.3" |
| 9 | +VERSION="${1:-$DEFAULT_VERSION}" |
| 10 | +DIR="${2:-$HOME/src/python-${VERSION}}" |
| 11 | + |
| 12 | +# Для venv: |
| 13 | +PY_MAJOR_MINOR="${VERSION%.*}" # 3.14.x -> 3.14 |
| 14 | +PY_BIN="python${PY_MAJOR_MINOR}" # python3.14 |
| 15 | +VENV_DIR="${HOME}/venv-python-${PY_MAJOR_MINOR}" |
| 16 | + |
| 17 | +# Список полезных тулзов: name;pip_package(ы);краткое_описание |
| 18 | +DEV_TOOLS=( |
| 19 | +"IPython;ipython;Интерактивная оболочка IPython" |
| 20 | +"Black;black;Форматтер кода" |
| 21 | +"Mypy;mypy;Статическая типизация" |
| 22 | +"Pylint;pylint;Линтер кода" |
| 23 | +"Pytest;pytest;Тестовый фреймворк" |
| 24 | +"HTTPX;httpx[http2,cli];HTTP клиент (sync/async, HTTP/2, CLI)" |
| 25 | +"Pydantic;pydantic[email];Валидация данных, EmailStr и схемы" |
| 26 | +"Locust;locust;Нагрузочное тестирование HTTP/гRPC" |
| 27 | +"Aiogram;aiogram;Телеграм-боты (async)" |
| 28 | +"Gunicorn;gunicorn;WSGI/ASGI сервер" |
| 29 | +"Flask;flask;Минималистичный веб-фреймворк" |
| 30 | +"FastAPI;fastapi;ASGI веб-фреймворк" |
| 31 | +"SQLAlchemy;SQLAlchemy;ORM и работа с БД" |
| 32 | +"python-dotenv;python-dotenv;Загрузка переменных окружения из .env" |
| 33 | +"PyYAML;PyYAML;Парсер/эмиттер YAML" |
| 34 | +"Cryptography;cryptography;Криптографическая библиотека" |
| 35 | +"Nuitka;nuitka;Компилятор Python → бинарники (Nuitka)" |
| 36 | +"Kivy;kivy;GUI/мобильные приложения (Kivy)" |
| 37 | +) |
| 38 | + |
| 39 | +# Группа data/ML-инструментов |
| 40 | +DATA_TOOLS=( |
| 41 | +"Pandas;pandas;Табличные данные и анализ" |
| 42 | +"NumPy;numpy;Базовые массивы и математика" |
| 43 | +"Matplotlib;matplotlib;Построение графиков" |
| 44 | +"Faker;Faker;Генерация фейковых данных" |
| 45 | +"PyTorch;torch;Библиотека для глубокого обучения" |
| 46 | +"Seaborn;seaborn;Статистические визуализации поверх Matplotlib" |
| 47 | +"scikit-learn;scikit-learn;Классический ML (sklearn)" |
| 48 | +) |
| 49 | + |
| 50 | +# Группа сред разработки |
| 51 | +IDES=( |
| 52 | +"JupyterLab;jupyterlab;Веб-интерфейс для ноутбуков" |
| 53 | +"Spyder;spyder;IDE в стиле MATLAB" |
| 54 | +) |
| 55 | + |
| 56 | +confirm() { |
| 57 | + local prompt="$1" |
| 58 | + read -r -p "$prompt [y/N] " answer |
| 59 | + case "$answer" in |
| 60 | + [yY]|[yY][eE][sS]) return 0 ;; |
| 61 | + *) return 1 ;; |
| 62 | + esac |
| 63 | +} |
| 64 | + |
| 65 | +confirm_yes_default() { |
| 66 | + local prompt="$1" |
| 67 | + read -r -p "$prompt [Y/n] " answer |
| 68 | + case "$answer" in |
| 69 | + [nN]|[nN][oO]) return 1 ;; # явное нет |
| 70 | + *) return 0 ;; # Enter, y, yes → да |
| 71 | + esac |
| 72 | +} |
| 73 | + |
| 74 | +create_venv() { |
| 75 | + local py_bin="$1" |
| 76 | + local venv_dir="$2" |
| 77 | + |
| 78 | + echo ">>> Создаём venv в ${venv_dir} с помощью ${py_bin}" |
| 79 | + "$py_bin" -m venv "$venv_dir" |
| 80 | + # shellcheck disable=SC1090 |
| 81 | + source "${venv_dir}/bin/activate" |
| 82 | + echo ">>> Обновляем pip" |
| 83 | + pip install --upgrade pip |
| 84 | +} |
| 85 | + |
| 86 | +download_dependencies() { |
| 87 | + echo "ПОДТЯГИВАЕМ НЕОБХОДИМЫЕ БИБЛИОТЕКИ ДЛЯ СБОРКИ ИЗ ИСХОДНИКОВ" |
| 88 | + |
| 89 | + sudo apt update |
| 90 | + sudo apt install -y build-essential libreadline-dev libncursesw5-dev \ |
| 91 | + libssl-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev \ |
| 92 | + libbz2-dev libffi-dev zlib1g-dev |
| 93 | +} |
| 94 | + |
| 95 | +install_python_from_source() { |
| 96 | + local version="$1" |
| 97 | + local dir="$2" |
| 98 | + |
| 99 | + download_dependencies |
| 100 | + |
| 101 | + echo ">>> Готовим каталог: ${dir}" |
| 102 | + mkdir -p "$dir" |
| 103 | + cd "$dir" |
| 104 | + |
| 105 | + local tarball="Python-${version}.tgz" |
| 106 | + local src_dir="Python-${version}" |
| 107 | + |
| 108 | + if [[ ! -f "$tarball" ]]; then |
| 109 | + echo ">>> Загружаем Python ${version} через wget" |
| 110 | + if command -v wget >/dev/null 2>&1; then |
| 111 | + wget "https://www.python.org/ftp/python/${version}/${tarball}" |
| 112 | + elif command -v curl >/dev/null 2>&1; then |
| 113 | + curl -LO "https://www.python.org/ftp/python/${version}/${tarball}" |
| 114 | + else |
| 115 | + echo "Нужен wget или curl для скачивания Python." |
| 116 | + exit 1 |
| 117 | + fi |
| 118 | + else |
| 119 | + echo ">>> Архив уже есть: ${tarball}" |
| 120 | + fi |
| 121 | + |
| 122 | + if [[ -d "$src_dir" ]]; then |
| 123 | + echo ">>> Каталог ${src_dir} уже существует, переиспользуем" |
| 124 | + else |
| 125 | + echo ">>> Распаковка архива в ${dir}" |
| 126 | + tar -xzf "$tarball" |
| 127 | + fi |
| 128 | + |
| 129 | + cd "$src_dir" |
| 130 | + |
| 131 | + echo ">>> Конфигурируем сборку" |
| 132 | + ./configure --enable-optimizations |
| 133 | + |
| 134 | + echo ">>> Сборка (make -j\"$(nproc)\")" |
| 135 | + make -j"$(nproc)" |
| 136 | + |
| 137 | + echo ">>> Установка через make altinstall (не трогаем системный python)" |
| 138 | + sudo make altinstall |
| 139 | + |
| 140 | + echo ">>> Готово. Проверка версии:" |
| 141 | + command -v "python${version%.*}" || true |
| 142 | + "python${version%.*}" --version || true |
| 143 | +} |
| 144 | + |
| 145 | +install_qt_designer_for_pyqt5() { |
| 146 | + echo ">>> Устанавливаем Qt Designer (Qt5) из пакетов дистрибутива" |
| 147 | + sudo apt update |
| 148 | + sudo apt install -y qttools5-dev-tools # содержит designer-qt5 |
| 149 | +} |
| 150 | + |
| 151 | +install_qt_designer_for_pyside6() { |
| 152 | + echo ">>> Устанавливаем Qt Designer (Qt6) из пакетов дистрибутива" |
| 153 | + sudo apt update |
| 154 | + sudo apt install -y designer-qt6 || { |
| 155 | + echo "designer-qt6 недоступен в репозитории — пропускаем." |
| 156 | + } |
| 157 | +} |
| 158 | + |
| 159 | +choose_qt_stack_and_install() { |
| 160 | + echo |
| 161 | + echo "=== Выбор Qt-стека (GUI) ===" |
| 162 | + echo "1) PyQt5 + Qt Designer (Qt5)" |
| 163 | + echo "2) PySide6 + Qt Designer (Qt6, если доступен)" |
| 164 | + echo "3) Ничего не ставить" |
| 165 | + read -r -p "Выберите вариант [1/2/3] (по умолчанию 3): " qt_choice |
| 166 | + |
| 167 | + case "$qt_choice" in |
| 168 | + 1) |
| 169 | + if confirm "Установить PyQt5? (Qt bindings для Python, PyQt5)"; then |
| 170 | + echo ">>> Устанавливаем PyQt5 в venv" |
| 171 | + pip install PyQt5 |
| 172 | + if confirm "Установить Qt Designer (Qt5, пакет qttools5-dev-tools)?"; then |
| 173 | + install_qt_designer_for_pyqt5 |
| 174 | + else |
| 175 | + echo "Пропускаем Qt Designer (Qt5)." |
| 176 | + fi |
| 177 | + else |
| 178 | + echo "Пропускаем PyQt5 и Qt Designer (Qt5)." |
| 179 | + fi |
| 180 | + ;; |
| 181 | + 2) |
| 182 | + if confirm "Установить PySide6? (Qt bindings для Python, PySide6)"; then |
| 183 | + echo ">>> Устанавливаем PySide6 в venv" |
| 184 | + pip install PySide6 |
| 185 | + if confirm "Установить Qt Designer для PySide6 (designer-qt6/pyside6-designer)?"; then |
| 186 | + install_qt_designer_for_pyside6 |
| 187 | + echo "В venv будет доступен инструмент pyside6-designer." |
| 188 | + else |
| 189 | + echo "Пропускаем Qt Designer для PySide6." |
| 190 | + fi |
| 191 | + else |
| 192 | + echo "Пропускаем PySide6 и Qt Designer (Qt6)." |
| 193 | + fi |
| 194 | + ;; |
| 195 | + *) |
| 196 | + echo "Qt-стек не будет установлен." |
| 197 | + ;; |
| 198 | + esac |
| 199 | +} |
| 200 | + |
| 201 | +install_tool_in_venv() { |
| 202 | + local name="$1" |
| 203 | + local pip_pkgs="$2" |
| 204 | + local desc="$3" |
| 205 | + |
| 206 | + if confirm "Установить ${name}? (${desc})"; then |
| 207 | + echo ">>> Устанавливаем ${name}: pip install ${pip_pkgs}" |
| 208 | + pip install ${pip_pkgs} |
| 209 | + else |
| 210 | + echo "Пропускаем ${name}." |
| 211 | + fi |
| 212 | +} |
| 213 | + |
| 214 | +install_tools_in_venv() { |
| 215 | + echo |
| 216 | + echo "=== ДОПОЛНИТЕЛЬНЫЕ ИНСТРУМЕНТЫ (ставим в venv ${VENV_DIR}) ===" |
| 217 | + echo |
| 218 | + |
| 219 | + # DEV-набор |
| 220 | + if confirm "Установить весь dev-набор (линтеры, тесты, веб, httpx, pydantic и т.д.) без дополнительных вопросов?"; then |
| 221 | + for item in "${DEV_TOOLS[@]}"; do |
| 222 | + IFS=';' read -r name pkgs desc <<< "$item" |
| 223 | + echo ">>> [DEV AUTO] ${name}: pip install ${pkgs}" |
| 224 | + pip install ${pkgs} |
| 225 | + done |
| 226 | + else |
| 227 | + echo ">>> Поштучная установка dev-инструментов:" |
| 228 | + for item in "${DEV_TOOLS[@]}"; do |
| 229 | + IFS=';' read -r name pkgs desc <<< "$item" |
| 230 | + install_tool_in_venv "$name" "$pkgs" "$desc" |
| 231 | + done |
| 232 | + fi |
| 233 | + |
| 234 | + echo |
| 235 | + # DATA/ML-набор |
| 236 | + if confirm "Установить весь data/ML-набор (pandas, numpy, matplotlib, torch и т.д.) без дополнительных вопросов?"; then |
| 237 | + for item in "${DATA_TOOLS[@]}"; do |
| 238 | + IFS=';' read -r name pkgs desc <<< "$item" |
| 239 | + echo ">>> [DATA AUTO] ${name}: pip install ${pkgs}" |
| 240 | + pip install ${pkgs} |
| 241 | + done |
| 242 | + else |
| 243 | + echo ">>> Поштучная установка data/ML-инструментов:" |
| 244 | + for item in "${DATA_TOOLS[@]}"; do |
| 245 | + IFS=';' read -r name pkgs desc <<< "$item" |
| 246 | + install_tool_in_venv "$name" "$pkgs" "$desc" |
| 247 | + done |
| 248 | + fi |
| 249 | + |
| 250 | + # DEV-набор |
| 251 | + if confirm "Установить IDE (Spyder, JupyterLab) без дополнительных вопросов?"; then |
| 252 | + for item in "${IDES[@]}"; do |
| 253 | + IFS=';' read -r name pkgs desc <<< "$item" |
| 254 | + echo ">>> [DEV AUTO] ${name}: pip install ${pkgs}" |
| 255 | + pip install ${pkgs} |
| 256 | + done |
| 257 | + else |
| 258 | + echo ">>> Поштучная установка IDEs:" |
| 259 | + for item in "${IDES[@]}"; do |
| 260 | + IFS=';' read -r name pkgs desc <<< "$item" |
| 261 | + install_tool_in_venv "$name" "$pkgs" "$desc" |
| 262 | + done |
| 263 | + fi |
| 264 | + |
| 265 | + echo |
| 266 | +} |
| 267 | + |
| 268 | +echo "УСТАНОВЩИК PYTHON ${VERSION} ИЗ ИСХОДНИКОВ" |
| 269 | +echo "ПРОВЕРЕНО НА Linux Mint 21.3 x64" |
| 270 | + |
| 271 | +echo "УСТАНОВЛЕННЫЕ ВЕРСИИ PYTHON В СИСТЕМЕ:" |
| 272 | +compgen -c | grep -E '^python3(\.[0-9]+)?$' | sort -u |
| 273 | + |
| 274 | +echo "******************************" |
| 275 | + |
| 276 | +echo "Вы выбрали установку Python версии: ${VERSION}" |
| 277 | +echo "Каталог для исходников: ${DIR}" |
| 278 | +echo "Ожидаемый бинарь: ${PY_BIN}" |
| 279 | +echo "Venv будет создан в: ${VENV_DIR}" |
| 280 | +echo |
| 281 | + |
| 282 | +PYTHON_INSTALLED=0 |
| 283 | + |
| 284 | +if confirm "ВЫ ДЕЙСТВИТЕЛЬНО ХОТИТЕ УСТАНОВИТЬ PYTHON ${VERSION}?"; then |
| 285 | + install_python_from_source "$VERSION" "$DIR" |
| 286 | + PYTHON_INSTALLED=1 |
| 287 | +else |
| 288 | + echo ">>> Установка Python пропущена по решению пользователя." |
| 289 | +fi |
| 290 | + |
| 291 | +# --- Блок работы с venv и доп. инструментами --- |
| 292 | + |
| 293 | +# Если Python не ставили в этом запуске — проверим, есть ли он |
| 294 | +if [[ "$PYTHON_INSTALLED" -eq 0 ]]; then |
| 295 | + if ! command -v "$PY_BIN" >/dev/null 2>&1; then |
| 296 | + echo ">>> ${PY_BIN} не найден в PATH, не сможем создать venv под него." |
| 297 | + echo "Дополнительные инструменты будут пропущены." |
| 298 | + exit 0 |
| 299 | + fi |
| 300 | +fi |
| 301 | + |
| 302 | +if ! confirm_yes_default "Создать и использовать venv (${VENV_DIR}) под ${PY_BIN} для доп. инструментов?"; then |
| 303 | + echo ">>> Пользователь отказался от venv, доп. инструменты ставить не будем." |
| 304 | + exit 0 |
| 305 | +fi |
| 306 | + |
| 307 | +if [[ -d "$VENV_DIR" ]]; then |
| 308 | + echo ">>> Venv ${VENV_DIR} уже существует." |
| 309 | + if confirm_yes_default "Использовать существующий venv и показать установленные пакеты?"; then |
| 310 | + # shellcheck disable=SC1090 |
| 311 | + source "${VENV_DIR}/bin/activate" |
| 312 | + echo ">>> Используем существующее окружение." |
| 313 | + echo ">>> Установленные пакеты (pip list):" |
| 314 | + pip list |
| 315 | + else |
| 316 | + echo ">>> Пересоздаём venv ${VENV_DIR}" |
| 317 | + rm -rf "${VENV_DIR}" |
| 318 | + create_venv "$PY_BIN" "$VENV_DIR" # внутри сделает source + upgrade pip |
| 319 | + fi |
| 320 | +else |
| 321 | + create_venv "$PY_BIN" "$VENV_DIR" # тоже делает source + upgrade pip |
| 322 | +fi |
| 323 | + |
| 324 | +echo |
| 325 | +choose_qt_stack_and_install |
| 326 | + |
| 327 | +echo |
| 328 | +echo "=== ДОПОЛНИТЕЛЬНЫЕ ИНСТРУМЕНТЫ (ставим в venv ${VENV_DIR}) ===" |
| 329 | +echo |
| 330 | + |
| 331 | +install_tools_in_venv |
| 332 | + |
| 333 | +echo |
| 334 | +echo "Готово." |
| 335 | +echo "Чтобы работать в этом окружении:" |
| 336 | +echo " source ${VENV_DIR}/bin/activate" |
| 337 | +echo "После активации доступны выбранные Qt-инструменты и остальные тулзы." |
0 commit comments