Spaces:
Sleeping
Sleeping
File size: 7,402 Bytes
e55536e |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
"""
Gradio app: 入力されたpip依存関係(requirements.txtスタイル or パッケージリスト)を受け取り、
指定したディレクトリに`pip install --target`でパッケージ(依存関係含む)をインストールし、
その内容をZIPにまとめてダウンロードできるようにします。
使い方:
- requirements テキストボックスにパッケージを1行ごとに書くか、requirements.txtの中身を貼る
- "依存関係を含める" のチェックを入れると pip が依存も自動でインストールします(デフォルト: True)
- "実行" を押すと処理が行われ、ZIPファイルとログが出力されます
注意事項:
- 任意のパッケージのインストールは任意のコード実行を伴うため、悪意あるパッケージをインストールすると
サーバーに危害が及ぶ可能性があります。公開サーバーでの運用は十分注意してください。
- ネイティブ拡張(C拡張)を含むパッケージはビルドが必要で、ビルド環境が整っていないと失敗します。
- 大きなパッケージや多数の依存関係は時間がかかる場合があります。
このファイルは1つのPythonスクリプトとして実行できます (Python 3.8+ 推奨)。
前提: gradio がインストールされていること。
例:
python gradio_pip_installer_zipper.py
"""
import gradio as gr
import tempfile
import shutil
import subprocess
import sys
import os
import zipfile
from pathlib import Path
import time
def install_and_zip(requirements_text: str, include_deps: bool = True, zip_name_prefix: str = "packages"):
"""requirements_text: requirements.txt の中身またはパッケージを改行区切りで受け取る。
include_deps: True のとき pip は依存も解決してインストールする。
戻り値: (zip_path, log_text)
"""
start_ts = time.time()
log_lines = []
if not requirements_text or not requirements_text.strip():
return None, "エラー: requirements の入力が空です。"
# 一時ディレクトリを作成
work_dir = Path(tempfile.mkdtemp(prefix="pip_zipper_"))
target_dir = work_dir / "site-packages"
target_dir.mkdir(parents=True, exist_ok=True)
req_file = work_dir / "requirements.txt"
# クリーンな requirements.txt を作る(空行とコメントを除去)
pkgs = []
for line in requirements_text.splitlines():
s = line.split("#", 1)[0].strip()
if s:
pkgs.append(s)
if not pkgs:
shutil.rmtree(work_dir, ignore_errors=True)
return None, "エラー: 有効なパッケージ指定が見つかりませんでした。"
req_file.write_text("\n".join(pkgs), encoding="utf-8")
log_lines.append(f"作業ディレクトリ: {work_dir}")
log_lines.append(f"インストール先ディレクトリ: {target_dir}")
log_lines.append(f"requirements:\n" + "\n".join(pkgs))
# pip コマンドの構築
pip_cmd = [sys.executable, "-m", "pip", "install", "-r", str(req_file), "--target", str(target_dir)]
if not include_deps:
pip_cmd.append("--no-deps")
# 環境によっては pip が古くて失敗することがあるため最小限のアップグレードを試みる(任意)
try:
log_lines.append("pip のバージョン確認...")
out = subprocess.check_output([sys.executable, "-m", "pip", "--version"], stderr=subprocess.STDOUT, text=True)
log_lines.append(out.strip())
except subprocess.CalledProcessError as e:
log_lines.append("pip バージョン確認に失敗しました: " + str(e))
# 実行
log_lines.append("\n=== pip install を実行します ===")
log_lines.append("実行コマンド: " + " ".join(pip_cmd))
try:
proc = subprocess.run(pip_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, cwd=str(work_dir), timeout=60*30)
log_lines.append(proc.stdout)
if proc.returncode != 0:
log_lines.append(f"pip install は終了コード {proc.returncode} で失敗しました。")
# ZIP は作らず一時ディレクトリを残してログを返す
zip_path = None
total_time = time.time() - start_ts
log_lines.append(f"経過時間: {total_time:.1f}s")
return zip_path, "\n".join(log_lines)
except subprocess.TimeoutExpired:
log_lines.append("pip install がタイムアウトしました。")
shutil.rmtree(work_dir, ignore_errors=True)
return None, "\n".join(log_lines)
except Exception as e:
log_lines.append("予期せぬエラー: " + str(e))
shutil.rmtree(work_dir, ignore_errors=True)
return None, "\n".join(log_lines)
# 成功したら ZIP を作る
zip_filename = f"{zip_name_prefix}_{int(time.time())}.zip"
zip_path = work_dir / zip_filename
log_lines.append("\n=== ZIP 生成 ===")
try:
with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED) as zf:
# target_dir 以下をすべて追加
for root, dirs, files in os.walk(target_dir):
for file in files:
fullpath = Path(root) / file
# ZIP 内のパスは target_dir の相対パスにする
relpath = fullpath.relative_to(work_dir)
zf.write(fullpath, arcname=str(relpath))
log_lines.append(f"ZIP 作成完了: {zip_path} (サイズ: {zip_path.stat().st_size} bytes)")
except Exception as e:
log_lines.append("ZIP 作成中にエラー: " + str(e))
shutil.rmtree(work_dir, ignore_errors=True)
return None, "\n".join(log_lines)
total_time = time.time() - start_ts
log_lines.append(f"経過時間: {total_time:.1f}s")
# 注意: 一時ディレクトリは残しておく(ユーザーがZIPをダウンロードできるように)
# 運用サーバーであれば定期クリーンアップを別途実装してください。
return str(zip_path), "\n".join(log_lines)
# Gradio UI
with gr.Blocks() as demo:
gr.Markdown("""# Pip 依存関係インストール → ZIP 生成
下に requirements.txt の中身、または1行ごとのパッケージ名を貼り付けてください。
""")
with gr.Row():
req_input = gr.Textbox(label="requirements またはパッケージ一覧 (1行に1件)", lines=10, placeholder="例:\nrequests==2.31.0\nnumpy>=1.25\n# コメントは無視されます")
with gr.Row():
include_deps = gr.Checkbox(value=True, label="依存関係を含める (通常はオン) ")
prefix = gr.Textbox(value="packages", label="生成ZIPのプレフィックス (任意)")
run_btn = gr.Button("実行")
output_file = gr.File(label="ダウンロード ZIP")
log_box = gr.Textbox(label="実行ログ", lines=15)
def _run(req_text, incl_deps, pref):
zip_path, log = install_and_zip(req_text, include_deps=incl_deps, zip_name_prefix=(pref or "packages"))
if zip_path:
return zip_path, log
else:
return None, log
run_btn.click(_run, inputs=[req_input, include_deps, prefix], outputs=[output_file, log_box])
if __name__ == "__main__":
demo.launch()
|