如何使用可以访问 R 脚本的 pip 安装包

How to install package with pip that has access to an R script

问题

我需要创建一个 python 包来访问 R 脚本并使其可通过 pip 安装

设置:

我的包结构如下

foo/  
  setup.py  
  foo/
    foo.R

setup.py的内容是:

from setuptools import setup, find_packages

setup(
    name='foo',
    packages=find_packages(),
    scripts=['foo/foo.R'],
    zip_safe=False,
)

foo.R的内容是:

#!/usr/bin/env Rscript

R.version

我正在安装这个包:
pip install -e .

当我查看安装的 R 脚本时,它不再是 R 脚本,因此当它被调用时,它被 运行 作为 python,因此失败

$ cat $(which foo.R)查看文件内容

我期待的是:

#!/usr/bin/env Rscript

R.version

我得到的:

#!/Users/jc33/miniconda3/bin/python
# EASY-INSTALL-DEV-SCRIPT: 'foo==0.0.0','foo.R'
__requires__ = 'foo==0.0.0'
__import__('pkg_resources').require('foo==0.0.0')
__file__ = '/Users/jc33/Desktop/foo/foo/foo.R'
exec(compile(open(__file__).read(), __file__, 'exec'))

附加范围

这是一个非常人为的示例,其创建的唯一目的是寻找解决方案,而不是争论使用 setuptools 定位 R 脚本的优点。有关更多信息,我正在创建一个更复杂的 python 包,类似于 this,它需要在某些时候调用 R,这是用 subprocess.run 完成的,但是我相信那是不可能的这个问题的范围

Python=='3.5.2'
setuptools=='27.2.0'
pip=='10.0.1

这是 "editable" 安装的正常行为。来自 setuptools docs:

In addition, the develop command creates wrapper scripts in the target script directory that will run your in-development scripts after ensuring that all your install_requires packages are available on sys.path.

pip install -epython setup.py develop 并不完全相同,但旨在非常接近。我相信 implementationpython setup.py develop.

的薄包装

scripts 适用于 Python 脚本。如果你想安装东西 那根本不是 Python 代码,您需要将其视为数据。在 setuptools 叫做 package_data:

setup(
    name='foo',
    packages=find_packages(),
    package_data={
        'foo': ['foo.R'],
    },
    zip_safe=False,
)

该文件将安装为 foo/foo.R,您可以从 foo/ 中的模块中找到它,假设它是 foo/foo.pyfoo.py 中的第一件事是找到它的目录,然后调用 foo.R:

import os, subprocess
foo_dir = os.path.dirname(__file__)
subprocess.check_call(['r', os.path.join(foo_dir, 'foo.R')])

PS。请注意,代码应该位于 foo/ 目录中的模块中。对于不同的目录,它必须以不同的方式使用 __file__ 进行操作。

发生这种情况是因为您的 R 脚本与 Python 完全不同。

如果您要放置一些导致语法错误的 R 代码,例如前导 .$foo[[0]] 的任何用法,您会发现 setuptools(但实际上distutils+easy_install) 不生成 develop 包装器。

可以找到控制脚本包装器的源代码here. It tests with is_python_script which calls is_python。这反过来只是编译文件。成功编译 returns True 并导致 easy_install 生成包装脚本,不成功编译 returns False 并且文件只是逐字复制(这就是你想要的)。