在 setuputils 包中获取入口点脚本文件位置? [英] Get entry point script file location in setuputils package?

查看:38
本文介绍了在 setuputils 包中获取入口点脚本文件位置?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

So I have an entry point defined in my setup.py [console_scripts] section. The command is properly installed and works fine, but I need a way to programatically find out the path to the script (e.g. on windows it'll be something like C:/my/virtual/env/scripts/my_console_script.exe). I need this so I can pass that script path as an argument to other commands, regardless of where the package is installed. Setuputils provides the pkg_resources, but that doesn't seem to expose any way of actually getting at the raw installed paths, only loadable objects.

为了使用例简单,这里是设置.

To make the use case plain here's the setup.

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

I have a plugin-driven application that communicates with various local services. One of these plug-ins ties into the alerting interface of an NMS package. The only way this alerting package can get alerts out to an arbitrary handler is to call a script - the path to execute (the console_scripts entry point in this case) is register as a complete path - that's the path I need to get.

推荐答案

好吧,你可以传递一个 --install-option="--install-scripts=/usr/local/bin" 形式的选项pip 并自己设置路径.但我理解为什么您可能不想在跨平台项目中对其进行硬编码.

Well, you could pass an option of the form --install-option="--install-scripts=/usr/local/bin" to pip and just set the path yourself. But I understand why you might not want to hardcode that in a cross-platform project.

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

So, we just need to find out what directory setuptools is actually using. Unfortunately, the path is determined in the middle of a whole bunch of actual setup code, rather than in a separate function we can just call.

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

So the next step is to write a custom install command that observes and saves the path. (For that matter, you could also set self.install_scripts to a directory of your choice in this custom installer class, thereby keeping that piece of config in one place (the package) rather than in the package and in a command line arg to setup...)

示例:

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 代码中获取.

  • Some people don't like any form of code generation. In this case it's more like configuration, though putting it in a .py in the package makes it easy to get at from Python code anywhere.

从表面上看,这有点像黑客.但是,setuptools 专门设计用于允许自定义命令类.这里没有什么是真正访问任何私有的,除了可能使用 distribution 对象传递的值,这只是为了避免 global.

On the surface it looks like a bit of a hack. However, setuptools is specifically designed to allow custom command classes. Nothing here is really accessing anything private, except maybe the value passing using the distribution object which is just to avoid a global.

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

Only gets the directory, not the executable names. You should know the names though, based on your entry point configuration. Getting the names would require calling the parsing methods for your entry point spec and generating more code.

如果你例如,它需要一些错误修正.无需安装即可构建.sdist 是安全的.

It's going to need some bugfixing if you e.g. build without installing. sdist is safe though.

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

develop is possible but needs another command class. install isn't called and build_py is only called if you set use_2to3. A custom develop command can get the path as in the install example, but as self.script_dir. From there you have to decide if you're comfortable writing a file to your source directory, which is in self.egg_path -- as it will get picked up by install later, though it should be the same file (and the build command will overwrite the file anyway)

附录

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

This little flash of insight may be more elegant as it does not require saving the path anywhere, but still gets the actual path setuptools uses, though of it assumes no configuration has changed since the install.

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

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

Just realized/remembered that pip creates an installed-files.txt in the egg-info, which is a list of paths relative to the package root of all the files including the scripts. pkg_resources.get_distribution('mypackage').get_metadata('installed-files.txt') will get this. It's only a pip thing though, not created by setup.py install. You'd need to go through the lines looking for your script name and then get the absolute path based on your package's directory.

这篇关于在 setuputils 包中获取入口点脚本文件位置?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆