【问题标题】:How to hide args to argparse when running script with exec?使用 exec 运行脚本时如何将 args 隐藏到 argparse?
【发布时间】:2020-06-02 11:44:22
【问题描述】:

我有 2 个文件,runner.py 使用子进程或 exec 运行 target.py
它们都有命令行选项。

如果 runner 使用 subprocess 运行目标,则可以:

$ python runner.py
run target.py with subprocess...
target.py: running with dummy = False

如果 runner 使用 exec 运行目标代码(使用 -e 选项):

$ python runner.py -e
run target.py with exec...
usage: runner.py [-h] [-d]
runner.py: error: unrecognized arguments: -e

命令行参数-etarget.py 代码“看到”(它只接受一个--dummy 选项)并引发错误。

使用 exec 运行脚本时如何将 args 隐藏到 argparse?

代码如下:

runner.py

​​>
import subprocess
import argparse


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("-e", "--exec", help="run with exec", action="store_true")
    args = parser.parse_args()

    target_filename = "target.py"

    if args.exec:
        print("run target.py with exec...")
        source_code = open(target_filename).read()
        compiled = compile(source_code, filename=target_filename, mode="exec")
        exec(compiled) # OPTION 1 - error on argparse
        # exec(compiled, {}) # OPTION 2 - target does not go inside "if main"
        # exec(compiled, dict(__name__="__main__")) # OPTION 3 - same error as OPTION 1 
    else:
        print("run target.py with subprocess...")
        subprocess.run(["python3", target_filename])

我尝试使用上面注释的选项隐藏全局变量,但没有运气。
似乎与 argparse 的工作原理有关。

target.py

​​>
import argparse

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("-d", "--dummy", help="a dummy option", action="store_true")
    args = parser.parse_args()

    print(f"target.py: running with dummy = {args.dummy}")

【问题讨论】:

  • 是否有任何理由使用 exec 而不是 subprocess ?有各种情况docsg/locals 应该是什么样子,如果没有提供它们会发生什么(不同的命名空间,...)。
  • 我认为总的来说,探索和分享有关编程中不清楚的方面的问题或答案会很有用。
  • 感谢@MauriceMeyer,我试图了解在这种情况下如何处理 exec 。我想到的第一个可行的原因可能是在运行 python 代码之前对其进行预处理(不创建其他文件)。
  • 如果您想忽略无法识别的参数,可以使用parse_known_args

标签: python python-3.x argparse python-exec


【解决方案1】:

有 argparse conflict_handler 选项,因此可以在目标脚本中写入
argparse.ArgumentParser(conflict_handler='resolve')
resolve 删除了冲突的选项,但是不能很好地处理选项在 runner 和 target 中具有相同名称的类似情况,或者您不能或不想更改目标文件的情况。


这是我找到的解决方案。
在内部 argparse 使用 sys.argv 来检索使用命令行设置的选项。

你可以直接设置sys.argv = [target_filename] 去掉选项,但是更改sys 会带来很多其他问题。

使用unittest.mock.patch (python3.4+) 可以像这样安全地更改sys.argv

from unittest.mock import patch

# [...]    

source_code = open(target_filename).read()
compiled = compile(source_code, filename=target_filename, mode="exec")

# remove command args
with patch('sys.argv', [target_filename]):
    exec(compiled)

所以也可以使用选项运行目标脚本代码:

# run target
with patch('sys.argv', [target_filename]):  
    exec(compiled)

# run target with -d
with patch('sys.argv', [target_filename, "-d"]):
    exec(compiled)

【讨论】:

    猜你喜欢
    • 2021-05-07
    • 2020-01-05
    • 2020-05-11
    • 2021-07-23
    • 2018-06-06
    • 2022-11-28
    • 2020-11-05
    • 2015-01-05
    • 2021-01-26
    相关资源
    最近更新 更多