# -*- coding: utf-8 -*-
"""QVP2_20250325_00.ipynb

Automatically generated by Colab.

Original file is located at
    https://colab.research.google.com/drive/1WGHxTLeHn6AJdS_sI_eyFmA7QgnYNSYN

# QuickViper2
Conda実行環境権限不足に対応するため、

1. conda-packで仮想環境の圧縮書庫を作成
2. 1.でできた圧縮書庫をtarで指定フォルダに解凍
3. conda-unpackで、コピーに伴いプレフィックスに発生する不具合を修正

を行う。



```
2025/03/22 0.1.0 完成
2025/03/22 0.1.1 バグ修正
2025/03/23 0.1.3 DebugHelperとの依存関係修復のため
2025/03/23 0.1.6 バグ修正
2025/03/25 0.1.7 バグ修正
2025/03/25 0.1.8 各種修正と改良。UniqueEnvVar を導入
```

# QuickViper2

## 1. モジュール定義
"""

# @title a. GDrive接続とcondacolab設定1
if __name__ == '__main__':
    get_ipython().system( "pip install an_CondaInitializer" )
    from an_condainitializer import CondaInitializer

    condainitializer = CondaInitializer()

# @title b. QuickViper2 定義
import os
import re
import json
import yaml
import shutil
import subprocess

from pathlib import Path


get_ipython().system( "pip install an_DebugHelper" )
from an_debughelper import DebugHelper
get_ipython().system( "pip install an_EnvManager" )
from an_envmanager import EnvManager
get_ipython().system( "pip install an_UniqueEnvVar" )
from an_uniqueenvvar import UniqueEnvVar


class QuickViper2:
    def __init__( self, init_path = False, debug_display = False ):
        from google.colab import drive
        drive.mount( '/content/drive' )

        self.debug = DebugHelper( instance_name = "QuickViper2" )
        self.debug.enable_log_to_file_stdout()
        self.debug.enable_log_to_file_stderr()
        self.debug.enable_timestamp()

        # env_file_list に読み込む.envファイルを指定（デフォルトは settings_ven.env）
        self.envmanager = EnvManager(env_files=["settings_ven.env"])
        # 環境変数LD_LIBRARY_PATHのバックアップを取る。

        self.uniqueev = UniqueEnvVar( var_name = "LD_LIBRARY_PATH")

        # 環境変数から各種パスを読み出し
        self.base_folder = self.envmanager.get_env_var("Ven_base_folder")
        self.venv_folder = self.envmanager.get_env_var("Ven_venv_folder")
        self.app_folder = self.envmanager.get_env_var("Ven_app_folder")
        self.venv_config_folder = self.envmanager.get_env_var("Ven_venv_config_folder")
        self.venv_contents_folder = self.envmanager.get_env_var("Ven_venv_contents_folder")
        self.local_venv_folder = self.envmanager.get_env_var("Ven_local_venv_folder")
        self.local_app_folder = self.envmanager.get_env_var("Ven_local_app_folder")
        self.archive_folder = self.envmanager.get_env_var("Ven_archive_folder")
        self.chunked_folder = self.envmanager.get_env_var("Ven_chunked_folder")

        self.bch_path = os.path.join( self.venv_contents_folder )
        self.dst_path = os.path.join( self.local_venv_folder )

        if not debug_display:
            self.debug.disable_debug()
        self.debug.log_step( f"bch_path : { self.bch_path }", success = None )
        self.debug.log_step( f"dst_path : { self.dst_path }", success = None )
        self.debug.enable_debug()

    def set_env_value( self, venv_name, back = 0, debug_display = False ):
        self.venv_name = venv_name
        self.cur_path = Path( self.base_folder )
        self.src_path = Path( self.venv_folder ).joinpath( self.venv_name, self.venv_contents_folder )
        self.arc_path = Path( self.archive_folder ).joinpath( "arcvenv" ).with_suffix( ".tar.gz" )
        self.lcl_path = Path( self.local_venv_folder ).joinpath( self.venv_name, self.venv_contents_folder )
        self.bin_path = self.lcl_path.joinpath( "bin" )
        self.lib_path = self.lcl_path.joinpath( "lib" )
        if not debug_display:
            self.debug.disable_debug()
        self.debug.log_step( f"venv_name : { self.venv_name }", success = None, back = back )
        self.debug.log_step( f"cur_path  : { self.cur_path }", success = None, back = back )
        self.debug.log_step( f"src_path  : { self.src_path }", success = None, back = back )
        self.debug.log_step( f"arc_path  : { self.arc_path }", success = None, back = back )
        self.debug.log_step( f"lcl_path  : { self.lcl_path }", success = None, back = back )
        self.debug.log_step( f"bin_path  : { self.bin_path }", success = None, back = back )
        self.debug.log_step( f"lib_path  : { self.lib_path }", success = None, back = back )
        self.debug.enable_debug()

    def compress_venv( self, venv_name ):
        self.debug.log_step( "compress_venv", success = None )
        self.set_env_value( venv_name, back = 1 )

        # get_ipython().system( "conda install -c conda-forge conda-pack" )
        # conda-pack --prefix . --format tar --output /path/to/myenv.tar
        cd_cmd = f"cd { Path( self.src_path ).joinpath( self.venv_name ) }"
        ex_cmd = f"conda-pack --prefix { self.src_path } --format tar.gz --output { self.arc_path }"
        self.debug.log_step( f"cd_cmd : { cd_cmd }" )
        self.debug.log_step( f"ex_cmd : { ex_cmd }" )
        get_ipython().system( cd_cmd )
        get_ipython().system( ex_cmd )

    def list_archive( self, venv_name ):
        self.debug.log_step( "list_archive", success = None )
        self.set_env_value( venv_name, back = 1 )
        get_ipython().system( f"tar tvf { self.arc_path }" )
        # 実行コマンドをリスト形式で定義
        #cmd = [str(self.bin_path / "python"), "-m", "pip", "--version"]
        cmd = [ "tar", "tvf", str(self.arc_path ) ]
        # コマンドを実行して出力をキャプチャ
        result = subprocess.run(cmd, capture_output=True, text=True)
        self.debug.log_step( f"stdout:\n{ result.stdout }", success = None)
        self.debug.log_step( f"stderr:\n{ result.stdout }", success = None)

    def delete_venv( self, venv_name ):
        self.debug.log_step( "delete_venv", success = None )
        self.set_env_value( venv_name, back = 1 )
        shutil.rmtree( self.lcl_path )

    def extract_venv( self, venv_name ):
        self.debug.log_step( "extract_venv", success = None )
        self.set_env_value( venv_name, back = 1 )
        os.chdir( self.cur_path )
        os.makedirs( self.lcl_path, exist_ok = True )
        cmd = f"tar -xf { self.arc_path } -C {self.lcl_path}"
        get_ipython().system( cmd )

    def conda_unpack( self, venv_name ):
        self.debug.log_step( "conda_unpack", success = None )
        self.set_env_value( venv_name, back = 1 )
        os.chdir( self.lcl_path )
        get_ipython().system( f"./bin/conda-unpack" )
        ### 2025/03/25 どうしてもこの方法では仮想環境をアクティブにできないので、やめにした。
        # Colabでは "source" を使えないため、bash -c 経由で conda の初期化スクリプトを使う
        #            bash -c "source /usr/local/etc/profile.d/conda.sh && conda activate /content/cEnv/venv/kohya_env/contents && python -m pip --version"
        # command = f'bash -c "source /usr/local/etc/profile.d/conda.sh && conda activate { self.lcl_path } && exec bash"'
        # result, stdout, stderr = self.debug.run_command(command, stdout=True, stderr=True)
        # self.debug.log_step( f"result:\n{ result }", success = None)
        # self.debug.log_step( f"stdout:\n{ stdout }", success = None)
        # self.debug.log_step( f"stderr:\n{ stdout }", success = None)
        # if result == 0:
        #    self.debug.log_step(f"環境が有効になりました。", char="🌟")


    def set_executable( self, venv_name ):
        self.debug.log_step( "set_executable", success = None )
        self.set_env_value( venv_name, back = 1 )
        get_ipython().system( f"chmod 755 { self.bin_path / 'python3.10' }" )
        get_ipython().system( f"chmod 755 { self.bin_path / 'python' }" )
        get_ipython().system( f"ls -l { self.bin_path / 'python3.10' }" )
        get_ipython().system( f"ls -l { self.bin_path / 'python' }" )

    def set_ld_library_path( self, venv_name ):
        self.debug.log_step( "set_ld_library_path", success = None )
        """
        指定した new_path を既存のLD_LIBRARY_PATHの先頭に追加します。
        """
        self.set_env_value( venv_name, back = 1 )
        # os.environ["LD_LIBRARY_PATH"] = f"{self.lib_path}:{ self.bkup_env }"
        self.uniqueev.add_value( new_value = str(self.lib_path) )
        self.debug.log_step(f"Updated LD_LIBRARY_PATH: {os.environ['LD_LIBRARY_PATH']}", success = None )

    def unlock( self, venv_name, force_unlock = False ):
        self.debug.log_step( "unlock", success = None )
        self.set_env_value( venv_name, back = 1 )
        if force_unlock and Path( self.lcl_path ).exists():
            shutil.rmtree( self.lcl_path )
        if not Path( self.lcl_path ).exists():
            self.extract_venv( venv_name = venv_name )
            self.conda_unpack( venv_name = venv_name )
            self.set_executable( venv_name = venv_name )
            self.set_ld_library_path( venv_name = venv_name )

    def executer( self, venv_name, cmdlist = [ "pip", "--version" ] ):
        self.debug.log_step( "exe_python", success = None )
        self.set_env_value( venv_name, back = 1 )
        os.chdir( self.bin_path )
        # get_ipython().system( "env | grep LD_LIBRARY_PATH" )

        # 現在の環境変数をコピー
        env = os.environ.copy()
        # シェルを使って "env | grep LD_LIBRARY_PATH" を実行し、出力をキャプチャ
        # result = subprocess.run(["env"], env=env, capture_output=True, text=True)
        result = subprocess.run("env | grep ^LD_LIBRARY_PATH=", shell=True, env=env, capture_output=True, text=True)
        self.debug.log_step( f"result : { result }", success = None )

        # cmd = f"{ self.bin_path / 'python' } -m pip --version"
        # self.debug.log_step( f"cmd : { cmd }", success = None )
        # get_ipython().system( cmd )

        # 実行コマンドをリスト形式で定義
        cmd = [str(self.bin_path / "python"), "-m"]
        ### conda run -p /content/cEnv/venv/kohya_env/contents python -m pip --version
        ### cmd = [ "conda", "run", "-p", str( self.lcl_path ), "python", "-m" ] ※ 2025/03/25 圧縮&解凍した仮想環境では、conda run は使えないことがわかった
        full_cmd = cmd + cmdlist
        # コマンドを実行して出力をキャプチャ
        self.debug.log_step( f"full_cmd : { full_cmd }", success = None )
        result = subprocess.run(full_cmd, env=env, capture_output=True, text=True)
        print( f"result :\n{ result.returncode }" )
        print( f"stdout :\n{ result.stdout     }" )
        print( f"stderr :\n{ result.stderr     }" )
        ### self.debug.run.commandが使えない理由はわからない。これも仮想環境絡みか?
        ### result, stdout, stderr = self.debug.run_command( command = full_cmd, stdout = True, stderr = True )
        ### self.debug.log_step( f"stdout:\n{ stdout }", success = None)
        ### self.debug.log_step( f"stderr:\n{ stdout }", success = None)



if __name__ == "__main__":
    quick_viper2 = QuickViper2()
    # quick_viper2.compress_venv( venv_name = "kohya_env" )
    # quick_viper2.list_archive( venv_name = "kohya_env" )
    quick_viper2.unlock( venv_name = "kohya_env", force_unlock = True )
    quick_viper2.executer( venv_name = "kohya_env" )