【问题标题】:how to load virtualenv using environmental module file (tcl script)?如何使用环境模块文件(tcl 脚本)加载 virtualenv?
【发布时间】:2014-04-05 18:40:52
【问题描述】:

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

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

谢谢,

编辑:

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

【问题讨论】:

    标签: python tcl virtualenv modulefile


    【解决方案1】:

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

    其次,modulesswapping 模块时有一些可怕的逻辑。如果你想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

    【讨论】:

      【解决方案2】:

      Modules 系统非常奇怪,因为它真正 所做的是创建一组由调用 shell 评估的指令。这意味着正常的 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"
      }
      

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

      【讨论】:

        【解决方案3】:

        基于 Donal Fellows 的回答和可以使用的文档:

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

        分号是必不可少的。

        【讨论】:

          【解决方案4】:

          您没有非常清楚地解释您要做什么,但是鉴于您在标题中提到了一个 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
          }
          

          【讨论】:

          • 非常感谢您的回复。对不起,我没有很好地解释这个问题。我现在更新了我的问题以包含更多信息。如果您能提供帮助,请查看它。另外,我不知道如何在模块文件中测试上述脚本。再次感谢您的努力,非常感谢!
          • 如果你在一个新的模块文件中使用除了主过程之外的所有过程,然后安排你的模块代码调用 load_virtualenv 那么它应该配置你需要的环境。您可能希望更改 update_env 过程以调用 setenv 而不是更新 env 数组,但显然这应该可以正常工作。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-11-18
          • 2016-10-03
          • 1970-01-01
          相关资源
          最近更新 更多