11

我正在尝试为创建 python 的程序编写模块文件virtualenv。为了启动它virtualenv,它需要先运行/programs/program-env/bin/activate。我该如何做到这一点modulefile?任何帮助将不胜感激。

注意:我尝试将上述行放入文件中,但没有成功。

谢谢,

编辑:

我正在编写一个模块文件来加载一个只能在virtualenv. 通常,这些模块文件将设置变量名称和/或将 bin 目录添加到路径。由于上面的包有些不同,我不知道如何进行。可以在此处找到示例模块文件。

4

4 回答 4

11

这是一个稍微更完整的答案,基于 Donal 和 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.

其次,在ping 模块modules时有一些可怕的逻辑。swap如果您想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 removedeactivate bar.

于 2016-02-25T15:22:20.737 回答
6

Modules 系统非常奇怪,因为它真正做的是创建一组由调用 shell 评估的指令。这意味着正常的 Tcl 做事方式往往不太正确。需要运行的是调用/programs/program-env/bin/activate者,而不是 Tcl 脚本。

首先要尝试的是:

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

但是,在常见问题解答中的两行之间,我发现您可能需要这样做(使用警卫):

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

我不知道如何反转操作(这是模块要点的一部分)。

于 2014-04-06T07:59:33.057 回答
5

根据 Donal Fellows 的回答和可以使用的文档:

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

分号是必不可少的。

于 2015-09-02T11:43:57.713 回答
0

您没有非常清楚地解释您要做什么,但是鉴于您在标题中提到了 tcl 脚本,我将假设您正在编写一个 Tcl 脚本,该脚本需要加载 virtualenv 环境以使用 virtualenv 配置操作 python 脚本。激活脚本是最终设置当前环境的 bash 脚本。由于 Tcl 不是 Bourne shell,因此您不能简单地将这些源代码输入 Tcl。但是,您可以创建一个 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
}
于 2014-04-05T21:18:44.847 回答