无法从通过 pip 和 setuptools 安装的 entry_points 脚本中执行 exec() Python 脚本
Cannot exec() Python script from within entry_points script installed via pip and setuptools
我按照 these 的说明创建了一个 setup.py
文件,该文件将安装 Python “可执行”脚本。这是我的项目结构:
pkgexec/
setup.py
pkgexec/
__init__.py
__main__.py
core.py
根据说明,__main__.py
的 main()
方法是 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"]},
)
我通过 运行ning 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()
方法的软件包用户来说限制太多。
调用 exec()
时,脚本的全局变量发生 某些事情。我无法弄清楚标准库中的 runpy
有什么不同,但它确实有效。我的解决方案是用 runpy.run_path()
.
调用替换 exec()
调用
这里是修改后的__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__
而不是默认的 <run_path>
集通过 runpy
).
请注意,此解决方案仅适用于 运行 的脚本 而不是 包含 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)
我按照 these 的说明创建了一个 setup.py
文件,该文件将安装 Python “可执行”脚本。这是我的项目结构:
pkgexec/
setup.py
pkgexec/
__init__.py
__main__.py
core.py
根据说明,__main__.py
的 main()
方法是 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"]},
)
我通过 运行ning 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()
方法的软件包用户来说限制太多。
调用 exec()
时,脚本的全局变量发生 某些事情。我无法弄清楚标准库中的 runpy
有什么不同,但它确实有效。我的解决方案是用 runpy.run_path()
.
exec()
调用
这里是修改后的__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__
而不是默认的 <run_path>
集通过 runpy
).
请注意,此解决方案仅适用于 运行 的脚本 而不是 包含 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)