获取setuputils包中的入口点脚本文件位置?

时间:2014-07-31 18:11:43

标签: python setuptools

所以我在setup.py [console_scripts]部分中定义了一个入口点。该命令已正确安装并正常工作,但我需要一种方法以编程方式找到脚本的路径(例如,在Windows上它会像C:/my/virtual/env/scripts/my_console_script.exe) 。我需要这个,所以我可以将该脚本路径作为参数传递给其他命令,无论软件包安装在何处。 Setuputils提供了pkg_resources,但似乎没有公开任何实际获取原始安装路径的方法,只有可加载对象。

编辑:要使用例明白,请设置。

我有一个插件驱动的应用程序,可以与各种本地服务进行通信。其中一个插件与NMS包的警报界面相关联。此警报包可以向任意处理程序发出警报的唯一方法是调用脚本 - 执行的路径(在本例中为console_scripts入口点)注册为完整路径 - 这是我需要的路径得到。

3 个答案:

答案 0 :(得分:6)

好吧,您可以将--install-option="--install-scripts=/usr/local/bin"形式的选项传递给pip,然后自己设置路径。但我明白为什么你可能不想在跨平台项目中硬编码。

因此,我们只需要找出setuptools 实际使用的目录。不幸的是,路径是在一大堆实际设置代码的中间确定的,而不是我们可以调用的单独函数。

因此,下一步是编写一个观察并保存路径的自定义安装命令。 (就此而言,您也可以 self.install_scripts设置为此自定义安装程序类中您选择的目录,从而将该部分配置保存在一个位置(包)而不是包和在命令行arg设置...)

示例:

from setuptools import setup
from setuptools.command.install import install
from distutils.command.build_py import build_py
import os

class InstallReadsScriptDir(install):
    def run(self):
        self.distribution._x_script_dir = self.install_scripts
        install.run(self)

class BuildConfiguresScriptDir(build_py):
    def run(self):
        build_py.run(self)
        if self.dry_run:
            return
        script_dir = self.distribution._x_script_dir  # todo: check exists
        target = os.path.join(self.build_lib, 'mypackage', 'scriptdir.py')
        with open(target, 'w') as outfile:
            outfile.write('path = """{}"""'.format(script_dir))

setup(
    name="MyPackage",
    version='0.0.1',
    packages = ['mypackage'],
    entry_points = {
        'console_scripts': [
            'footest = mypackage:main',
        ],
    },
    cmdclass= {
        'install': InstallReadsScriptDir,
        'build_py': BuildConfiguresScriptDir,
    },
)

可能的反对意见:

  • 有些人不喜欢任何形式的代码生成。在这种情况下,它更像是配置,虽然将它放在包中的.py中可以很容易地从任何地方的Python代码中获取。

  • 从表面上看,它看起来有点像黑客。但是,setuptools专门用于允许自定义命令类。这里没有任何内容真正访问任何私有内容,除非使用distribution对象传递的值只是为了避免global

  • 只获取目录,而不是可执行文件名。您应该根据入口点配置知道名称。获取名称需要调用入口点规范的解析方法并生成更多代码。

  • 如果你需要一些错误修正没有安装就构建。 sdist是安全的。

  • develop是可能的,但需要另一个命令类。未调用install,只有在设置build_py时才会调用use_2to3。自定义develop命令可以获取install示例中的路径,但是self.script_dir。从那里你必须决定你是否愿意将文件写入源目录,这是在self.egg_path - 因为它将在install之后被提取,尽管它应该是同一个文件(并且构建命令无论如何都将覆盖文件)

<强>附录

这个小小的洞察力可能更优雅,因为它不需要在任何地方保存路径,但仍然获得实际路径setuptools使用,但它假设自安装以来没有任何配置发生变化。

from setuptools import Distribution
from setuptools.command.install import install

class OnlyGetScriptPath(install):
    def run(self):
        # does not call install.run() by design
        self.distribution.install_scripts = self.install_scripts

def get_setuptools_script_dir():
    dist = Distribution({'cmdclass': {'install': OnlyGetScriptPath}})
    dist.dry_run = True  # not sure if necessary, but to be safe
    dist.parse_config_files()
    command = dist.get_command_obj('install')
    command.ensure_finalized()
    command.run()
    return dist.install_scripts

附录2

刚刚意识到/记得pipinstalled-files.txt中创建了一个egg-info,这是一个相对于包括脚本在内的所有文件的包根的路径列表。 pkg_resources.get_distribution('mypackage').get_metadata('installed-files.txt')会得到这个。它只是一个pip的东西,不是由setup.py install创建的。您需要遍历查找脚本名称的行,然后根据程序包的目录获取绝对路径。

答案 1 :(得分:2)

您是否在入口点脚本中尝试过`os.path.abspath(__ file__)'?它将返回你的入口点绝对路径。

或者从find_executable致电distutils.spawn

import distutils.spawn distutils.spawn.find_executable('executable')

答案 2 :(得分:2)

您可以按如下方式获取site_packages目录的路径:

from distutils.sysconfig import get_python_lib
get_python_lib()

如果您的脚本位于相对于该路径的某个位置,无论是在site-packages还是在目录中,您都可以使用site-packages路径加入相对路径,如下所示:

import os
scriptPath = os.path.realpath(os.path.join(get_python_lib(), '../my/relative/path/script.exe'))

您可以尝试的另一个技巧是导入关联的模块,然后查看其文件参数。然后,如果您知道该文件的相对路径是什么,则可以使用与上述示例相同的方式查找脚本。在命令行界面上试试..

import django
django.__file__

/usr/lib/python2.6/site-packages/django/的初始化 pyc文件

modulePath = os.path.split(django.__file__)[0]
scriptPath = os.path.realpath(os.path.join(modulePath, '../my/relative/path/script.exe'))

上述建议是否正确?