如何创建可pip安装且能访问R脚本的Python包
我明白你遇到的麻烦了——当你用scripts参数把R脚本加入Python包时,setuptools会自动把它转换成Python包装脚本,导致原本的R代码被当作Python执行而报错。这是因为scripts参数是专门为Python可执行脚本设计的,它会自动添加依赖检查和Python shebang,完全不适合处理R脚本。
下面是两种可行的解决方案,根据你的实际需求选择:
方案1:将R脚本作为包资源,在Python中调用
这种方法适合你需要在Python代码中通过subprocess调用R脚本的场景(就像q2-dada2那样)。
步骤1:修改setup.py
把原来的scripts参数替换成package_data,让setuptools把R脚本当作包的静态资源保存:
from setuptools import setup, find_packages setup( name='foo', packages=find_packages(), # 声明foo包下的foo.R是需要包含的资源文件 package_data={ 'foo': ['foo.R'], }, zip_safe=False, )
步骤2:在Python中获取R脚本路径并调用
在你的Python代码(比如foo/__init__.py)中,用pkg_resources获取R脚本的绝对路径,再通过subprocess.run调用:
import pkg_resources import subprocess def run_foo_r(): # 获取包内foo.R的绝对路径 r_script_path = pkg_resources.resource_filename('foo', 'foo.R') # 调用R脚本,这里可以根据需求调整参数 result = subprocess.run( ['Rscript', r_script_path], capture_output=True, text=True ) # 打印输出或处理结果 print(result.stdout) if result.stderr: print("错误信息:", result.stderr)
方案2:添加命令行入口(直接运行R脚本)
如果你希望用户能直接通过命令行运行这个R脚本,可以写一个Python包装脚本作为入口点,避免setuptools修改原R脚本。
步骤1:添加Python CLI包装脚本
在foo目录下新建cli.py:
import pkg_resources import subprocess import sys def main(): # 获取R脚本路径 r_script_path = pkg_resources.resource_filename('foo', 'foo.R') # 传递命令行参数给R脚本 subprocess.run(['Rscript', r_script_path] + sys.argv[1:]) if __name__ == '__main__': main()
步骤2:修改setup.py添加entry_point
在setup中添加entry_points参数,注册命令行入口:
from setuptools import setup, find_packages setup( name='foo', packages=find_packages(), package_data={ 'foo': ['foo.R'], }, zip_safe=False, # 注册命令行脚本foo-r,指向cli.py的main函数 entry_points={ 'console_scripts': [ 'foo-r = foo.cli:main', ], }, )
安装后,用户直接在终端输入foo-r就能运行你的R脚本了,原R脚本的内容会完整保留。
为什么原来的方法不行?
再强调一下:scripts参数的设计目的是处理Python脚本,setuptools会自动给每个脚本添加Python shebang和依赖验证代码(就是你看到的那些__requires__和exec代码),所以你的R脚本被错误地包装成了Python脚本,运行时自然会报错。
内容的提问来源于stack exchange,提问作者johnchase

