2014-04-05 38 views
6

我想寫一個模塊文件的程序,創建一個python virtualenv。爲了啓動virtualenv,它需要首先運行/programs/program-env/bin/activate。我如何在modulefile中做到這一點? 任何幫助將不勝感激。如何使用環境模塊文件(tcl腳本)加載virtualenv?

注:我試着把上面的行放在文件中,它不起作用。

感謝,

編輯:

我寫一個modulefile加載程序,只能在virtualenv運行。通常,這些模塊文件將設置變量名稱和/或將bin目錄添加到路徑。由於上述軟件包有所不同,我不知道如何繼續。示例模塊文件可以在here找到。

回答

0

你沒有十分清楚地解釋你正在做什麼,但考慮到你在標題中提到了一個tcl腳本,我將假設你正在編寫一個Tcl腳本,需要加載virtualenv環境來使用virtualenv來操縱python腳本組態。激活腳本是最終設置當前環境的bash腳本。您不能簡單地將這些內容發送到Tcl,因爲Tcl不是Bourne shell。但是,您可以創建一個shell子進程並讀取其環境,並在激活腳本獲取後將其與環境進行比較。如果您的tcl腳本將差異應用於其自己的環境,那麼生成的Tcl過程將在獲取激活腳本之後等同於bash shell。

這裏是一個例子。如果您將其作爲tclsh scriptname bin/activate運行,它將打印現在包含激活腳本中的其他設置的環境。在我的linux盒子測試中,添加了一個VIRTUAL_ENV變量,並修改了PS1和PATH。

#!/usr/bin/env tclsh 
# Load a virtualenv script in a subshell and apply the environment 
# changes to the current process environment. 

proc read_env {chan varname} { 
    upvar #0 $varname E 
    set len [gets $chan line] 
    if {$len < 0} { 
     fileevent $chan readable {} 
     set ::completed 1 
    } else { 
     set pos [string first = $line] 
     set key [string range $line 0 [expr {$pos - 1}]] 
     set val [string range $line [expr {$pos + 1}] end] 
     set E($key) $val 
    } 
} 

proc read_shell_env {varname cmd} { 
    set shell [open |[list /bin/bash] "r+"] 
    fconfigure $shell -buffering line -encoding utf-8 -blocking 0 
    fileevent $shell readable [list read_env $shell $varname] 
    puts $shell $cmd 
    flush $shell 
    vwait ::completed 
    close $shell 
    return 
} 

proc update_env {key val} { 
    global env 
    set env($key) $val 
} 

proc load_virtualenv {filename} { 
    array set ::envA {} 
    array set ::envB {} 
    read_shell_env ::envA "printenv; exit 0" 
    read_shell_env ::envB "source \"$filename\"; printenv; exit 0" 

    set keys [lsort [array names ::envA]] 
    foreach k [lsort [array names ::envB]] { 
     if {[info exists ::envA($k)]} { 
      if {$::envA($k) ne $::envB($k)} { 
       update_env $k $::envB($k) 
      } 
     } else { 
      update_env $k $::envB($k) 
     } 
    } 
    unset ::envA 
    unset ::envB 
    return 
} 

proc main {filename} { 
    global env 
    load_virtualenv $filename 
    foreach key [lsort [array names env]] { 
     puts "$key=$env($key)" 
    } 
    return 0 
} 

if {!$tcl_interactive} { 
    set r [catch [linsert $argv 0 main] err] 
    if {$r} {puts stderr $err} 
    exit $r 
} 
+0

非常感謝您的回覆。對不起,我沒有很好地解釋這個問題。我現在更新了我的問題以包含更多信息。如果你能幫忙,請看看它。另外,我不知道如何在模塊文件中測試上述腳本。再次感謝您的努力,我非常感謝! – arnstrm

+1

如果您使用本示例中的所有過程(除了新模塊文件中的主過程),請安排您的模塊代碼以調用load_virtualenv,然後它應該配置所需的環境。您可能需要更改update_env proc來調用setenv,而不是更新env數組,但顯然這應該按原樣運行。 – patthoyts

5

的模塊系統是非常奇怪的,因爲它是真的是創建一組是由主叫殼評估指令。這意味着正常的Tcl處事方式往往不太正確;它是調用者誰需要運行/programs/program-env/bin/activate,而不是Tcl腳本。

首先要嘗試的是:

system "/programs/program-env/bin/activate" 

然而,在the FAQ字裏行間看,我看,你可能會需要做這個(帶防護):

if {[module-info mode] == "load"} { 
    puts stdout "/programs/program-env/bin/activate" 
} 

我不知道如何反轉操作(這是模塊的一部分)。

4

基於多納爾研究員答案,它是可以做到的文檔:

if { [ module-info mode load ] } { 
    puts stdout "/programs/program-env/bin/activate;" 
} elseif { [ module-info mode remove ] } { 
    puts stdout "deactivate;" 
} 

分號是必不可少的。

6

這裏有一個稍微更完整的答案,構建由多納爾和betapatch的答案,它允許你兩個模塊裏面做類似的事情之間進行切換:

if { [module-info mode load] || [module-info mode switch2] } { 
    puts stdout "source /programs/program-env/bin/activate;" 
} elseif { [module-info mode remove] && ![module-info mode switch3] } { 
    puts stdout "deactivate;" 
} 

首先,你需要使用source .../activate而不是隻是.../activate

其次,modules有一些可怕的邏輯,當swap ping模塊。如果你想module swap foo bar(刪除foo並在其位置加載bar),它實際上執行以下操作:

foo: switch1 # prep for remove 
foo: remove # actually remove 
bar: switch2 # load new module 
foo: switch3 # cleanup 
foo: remove # happens at the same time as foo switch3 

這意味着,如果foobar使用virtualenvs,第二foo removedeactivatebar都modulefiles。

相關問題