【问题标题】:Get entry point script file location in setuputils package?在 setuputils 包中获取入口点脚本文件位置?
【发布时间】:2014-09-23 19:43:56
【问题描述】:

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

编辑:为了使用例更清晰,这里是设置。

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

【问题讨论】:

  • 你能根据知道站点包包或其他目录的真实路径找到脚本的路径吗?如果不是,您是否控制脚本的安装程序?也只是我们在谈论的Windows吗?脚本所在的目录是 PATH 变量还是其他环境变量指向的?
  • 我无法从 setuptools 获取安装路径。二进制路径可能不在系统路径上,因为典型的安装将是一个 virtualenv。需要 Windows 支持,但代码也将在 *nix 上运行。
  • 你提到了.exe;对于 Windows,您使用的是 py2exe 吗?
  • 当你在这里说“动态”时,它听起来更像是“魔术”,除非它是在路径中定义的,或者在脚本的同一个工作目录中你必须做很多猜工作。您是否考虑过使用通过管道传输到 setup.py 脚本中的变量?

标签: python setuptools


【解决方案1】:

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

或者从distutils.spawn拨打find_executable:

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

【讨论】:

  • 不是一个选项,因为脚本存在于库文件夹之外,并且无论如何入口点脚本不是我需要加载位置的位置。
【解决方案2】:

您可以通过以下方式获取 site_packages 目录的路径:

from distutils.sysconfig import get_python_lib
get_python_lib()

如果您的脚本位于相对于该路径的某个位置,无论是在站点包中还是在一个目录中,您都可以将相对路径与站点包路径连接起来,如下所示:

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

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

import django
django.__file__

/usr/lib/python2.6/site-packages/django/init.pyc

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

以上建议是否正确?

【讨论】:

  • 不是一个选项,因为脚本存在于库文件夹之外,并且无论如何入口点脚本都不是我需要加载位置的地方。 Setuptools 脚本是特殊的启动包装器,不能直接导入。
  • @TylerEaves 你到底在哪里试图获得你需要的路径。我认为您需要更详细地更新您的问题,以便人们真正帮助您。
【解决方案3】:

好吧,您可以将--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 代码中获取。

  • 从表面上看,它看起来有点像 hack。但是,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

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

【讨论】:

  • 好的,我觉得这很接近,所以我暂时将其标记为接受。有什么方法可以使这项工作也与“开发”一起使用?
  • 您的附录是我在环境路径中安装二进制文件所需的解决方案,适用于install 和develop 命令(因为install_scripts 只能作为命令的成员使用install 场景中的类)。
猜你喜欢
  • 1970-01-01
  • 2014-09-07
  • 1970-01-01
  • 2019-10-28
  • 1970-01-01
  • 1970-01-01
  • 2011-04-09
  • 1970-01-01
相关资源
最近更新 更多