【问题标题】:Cannot exec() Python script from within entry_points script installed via pip and setuptools无法从通过 pip 和 setuptools 安装的 entry_points 脚本中执行 Python 脚本
【发布时间】:2021-12-18 04:36:06
【问题描述】:

我按照these 的说明创建了一个setup.py 文件,该文件将安装一个Python“可执行”脚本。这是我的项目结构:

pkgexec/
  setup.py
  pkgexec/
    __init__.py
    __main__.py
    core.py

按照说明,__main__.pymain() 方法是setup.py 中的入口点:

from setuptools import setup, find_packages

setup(
    name="pkgexec",
    version="0.2.0",
    packages=find_packages(),
    entry_points={ "console_scripts": ["pkgexec = pkgexec.__main__:main"]},
)

我通过运行 pip install -e .pkgexec/ 目录中安装了该软件包。

到目前为止,一切都按预期进行。

没有的工作是通过这个“可执行”入口点执行 Python 脚本。你看,这个包的全部目的是运行 Python 脚本,从包中导入一堆东西,例如script.py 使用 pkgexec 包中的功能,并通过 pkgexec“可执行文件”“运行”:

pkgexec script.py -v arg1 arg2

这里是__main__.py的简化版:

import argparse
import sys

from pkgexec import some_stuff

def main(args=None):
    if args is None:
        args = sys.argv[1:]

    parser = argparse.ArgumentParser()
    parser.add_argument('script', help='script to run via pkgexec')
    parser.add_argument(...)
    cli_args = parser.parse_args()

    print(f'{__name__}: Running script {cli_args.script}')
    exec(open(cli_args.script).read(), globals(), globals())  # <-- ???
    print(f'{__name__}: Done')


if __name__ == '__main__':
    sys.exit(main())

问题:exec(open(cli_args.script).read()) 没有任何反应(尝试使用和不使用, globals(), globals())。该脚本未执行。我在这里做错了什么?

我不喜欢的解决方法:

  • 如果我不“安装”入口点,我可以通过exec() 运行脚本。不是一种选择。
  • 如果我通过importlib 导入脚本,我可以运行它。但这对于应该编写 main() 方法的软件包用户来说过于严格了。

【问题讨论】:

    标签: python pip exec setuptools entry-point


    【解决方案1】:

    调用exec() 时,脚本的全局变量发生了一些事情。我不知道标准库中的runpy 有什么不同,但它确实有效。就我而言,解决方案是将exec() 调用替换为对runpy.run_path() 的调用。

    这里是修改后的__main__.py脚本用于比较:

    import argparse
    import os
    import runpy
    import sys
    
    from pkgexec import some_stuff
    
    def main(args=None):
        if args is None:
            args = sys.argv[1:]
    
        parser = argparse.ArgumentParser()
        parser.add_argument('script', help='script to run via pkgexec')
        parser.add_argument(...)
        cli_args = parser.parse_args()
    
        print(f'{__name__}: Running script {cli_args.script}')
        mod = argparse.Namespace(
            **runpy.run_path(cli_args.script,
                             run_name=os.path.basename(cli_args.script)))
        print(f'{__name__}: Done')
    
    
    if __name__ == '__main__':
        sys.exit(main())
    

    我将文件名作为run_name 参数发送给run_path(这样脚本“知道”它的实际__name__,而不是runpy 设置的默认&lt;run_path&gt;)。

    请注意,此解决方案仅适用于要运行的脚本包含if __name__ == '__main__' 部分(这正是我想要的)。

    更新:同样的效果可以通过 runpy 在后台执行:compile() 先代码,然后 exec() 后代码:

    # mod = argparse.Namespace(
    #     **runpy.run_path(cli_args.script,
    #                      run_name=os.path.basename(cli_args.script)))
    code = compile(open(cli_args.script).read(),
                   os.path.basename(cli_args.script), 
                   'exec')
    exec(code)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-07-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-19
      • 2013-12-15
      • 1970-01-01
      相关资源
      最近更新 更多