如何正确使用Sublime Text插件的第三方依赖关系



我正在尝试为Sublime Text 3编写一个插件。

我必须在代码中使用几个第三方程序包。我已经通过手动将包复制到/home/user/.config/sublime-text-3/Packages/User/中使代码工作,然后我使用相对导入来获得所需的代码。如何将插件分发给最终用户?告诉他们将所需的依赖项复制到适当的位置肯定不是办法。第三方模块应如何与Sublime Text插件正确使用?我在网上找不到任何文档;我看到的只是将模块放入文件夹的建议。

Sublime使用自己的嵌入式Python解释器(目前为Python 3.3.6,尽管下一个版本也将支持Python 3.8),因此它将完全忽略您系统上可能安装或未安装的任何版本的Python,以及为该版本安装的任何库。

因此,如果要使用外部模块(以下简称dependencies),则需要做额外的工作。有多种方法可以实现这一点,每种方法都有自己的利弊。

以下列出了实现这一目标的各种方法;所有这些都需要对Python中的模块是如何工作的有一点了解,才能了解发生了什么。总的来说,除了涉及的路径之外,没有什么太"升华文本"的机制在起作用。

注意:截至本答案时,以下内容是准确的。然而,包控制有计划改变它的工作方式,即将到来的依赖关系可能会改变这方面的某些方面。

这与即将推出的Sublime版本有关,该版本支持当前Package Control机制不支持的多个版本的Python(以及支持它们的方式)。

目前尚不清楚这一变化是否会带来一种新的方式来指定依赖项,或者是否只有依赖项安装的内部工作方式会发生变化。然而,现有机制可能仍然存在,而不仅仅是为了向后兼容性。

从Sublime插件访问Pythondependency的所有途径都包括将其代码放在Python解释器要查找的地方。这类似于标准Python的做法,除了检查的位置包含在Sublime用于存储配置的区域(称为Data目录)中,并且Python不是独立的Python解释器,而是在插件主机中运行。

将库填充到Lib文件夹中

自3.0版本(版本3143)起,Sublime将在数据目录中创建一个名为Lib的文件夹,并在其中创建一个基于Python版本名称的目录。如果您使用Preferences > Browse Packages并上升到一个文件夹级别,您将看到Lib,其中有一个名为python3.3的文件夹(或者如果您使用的是较新的版本,则为python33python38)。

默认情况下,这些目录直接位于Pythonsys.path上,因此任何插件都可以立即使用这些目录中的任何内容,就像普通Python库(或任何内置库)一样。您可以将这些文件夹视为类似于标准Python中的site-packages文件夹。

因此,任何可以安装标准Python库的方法都可以使用,只要结果是文件最终位于该文件夹中即可。例如,您可以通过pip安装库,然后从site-packages手动将文件复制到该位置,从源手动安装,等等。

Lib/python3.3/
|-- librarya
|   `-- file1.py
|-- libraryb
|   `-- file2.py
`-- singlefile.py

此处适用版本限制;您想要使用的dependency必须支持Sublime正在使用的Python版本,否则它将无法工作。这对于具有本地组件(例如.dll.so.dylib)的Python库来说尤其重要,因为它们可能需要手动编译代码。

这种方法不是自动的;您需要这样做才能在本地使用您的软件包,任何想要使用您的程序包的人也需要这样做。由于Sublime目前使用的是较旧版本的Python,因此获取正确版本的库也可能存在问题。

将来,Package Control将在此位置安装dependencies(在3.0版本之前的运行过程中,将专门为此添加文件夹),但截至我撰写此答案时,目前情况并非如此。

直接在您自己的包中提供您的依赖项

默认情况下,Packages文件夹也在sys.path上;Sublime就是这样查找和加载包的。物理Packages文件夹以及包含sublime-package文件内容的"虚拟"软件包文件夹都是如此。

例如,可以通过以下方式访问提供exec命令的类:

from Default.exec import ExecCommand

即使exec.py文件实际存储在Sublime文本安装文件夹中的Default.sublime-package中,并且不存在于Packages文件夹中,这也会起作用。

因此,您可以直接在自己的包中vendor任何您需要的dependencies。这里可以是User包或您正在创建的任何其他包。

需要注意的是,Sublime会将包顶级中的任何Python文件视为插件,并尝试将其作为插件加载。因此,重要的是,如果你走这条路,你可以在包中创建一个子文件夹,并将库放在那里。

MyPackage/
|-- alibrary
|   `-- code.py
`-- my_plugin.py

使用这种结构,您可以直接访问模块:

import MyPackage.alibrary 
from MyPackage.alibrary import someSymbol

并不是所有的Python模块都可以在不进行修改的情况下直接使用这种方法;可能需要对dependency进行一些代码更改,以便允许库的不同部分查看其自身的其他部分,例如,如果它不使用相对import来获取同级文件。许可证限制也可能会阻碍这一点,具体取决于您使用的库。

另一方面,这会直接将您正在使用的库的版本锁定为您测试的版本,从而确保您不会在以后遇到任何不必要的意外。

使用此方法,您为分发包所做的任何操作都将自动分发包中包含的vendored库。因此,如果你通过Package Control进行分发,你不需要做任何特别的事情,它就会起作用™.

修改sys.path以指向自定义位置

Sublime中嵌入的Python仍然是标准Python,因此如果需要,您可以手动操作sys.path,该CCD_36描述了要在哪些文件夹中查找包,以便它除了在Sublime自动设置的标准位置之外,还可以在您选择的位置查找包。

这通常不是一个好主意,因为如果做得不对,事情会很快变成梨形。它还需要您首先在自己的某个地方手动安装库,在这种情况下,您最好使用上面概述的Lib文件夹,它已经在sys.path上了。

我认为这种方法是一种高级解决方案,在开发过程中可以用于测试目的,但在其他方面不是面向用户的。如果您计划通过package Control分发包,那么对包的审查可能会通过请求使用另一种方法来阻止对sys.path的操作。

使用Package Control的依赖项系统(并且依赖项存在)

包控件包含一个依赖关系机制,该机制使用前面两种方法的组合来提供自动安装依赖关系的方法。还有一个可用依赖项的列表,尽管该列表可能不完整。

如果您感兴趣使用的dependency已经可用,那么您可以继续使用。有两种不同的方法可以声明您需要对包进行一个或多个依赖。

注意:包控制当前不支持依赖项的依赖项;如果一个依赖项需要同时安装另一个库,则需要自己明确提及这两个库。

第一步是将dependencies密钥添加到包控制通道文件中的包条目中。这是在将程序包添加到程序包控制时要采取的步骤,这不在本答案的范围内。

当您开发包时(或者如果您决定在完成后不想通过包控制分发包),您可以在包的根目录中添加一个dependencies.json文件(dependencies.json文件示例可以说明这一点)。

完成此操作后,您可以从命令Palette中选择Package Control: Satisfy Dependencies,让Package Control为您下载并安装依赖项(如果需要)。

如果您的软件包是由软件包控制分发和安装的,则此步骤是自动执行的;否则,您需要告诉用户在安装软件包后执行此步骤。

使用包控件的依赖项系统(但依赖项不存在)

PackageControl用于安装dependencies的方法,如问题顶部所述,可能在不久的将来发生更改。这可能会影响此处的说明。就设置而言,总体机制可能保持不变,只是安装位置发生了变化,但目前仍有待观察。

PackageControl通过vendoring的特殊组合以及对sys.path的操作来安装依赖项,以允许查找内容。为了做到这一点,需要以特定的方式布置依赖关系,并提供一些额外的元数据。

在构建时,包含依赖项的包的布局将具有类似于以下的结构:

Packages/my_dependency/
├── .sublime-dependency
└── prefix
└── my_dependency
└── file.py

Package Control将dependency作为Package安装,由于Sublime将包根目录中的每个Python文件都视为插件,因此依赖关系的代码不会保留在包的顶层。如上所述,依赖关系的实际内容存储在上面标记为prefix的文件夹内(稍后将详细介绍)。

安装依赖项后,Package Control会将一个条目添加到其特殊的0_package_control_loader程序包中,该程序包会将prefix文件夹添加到sys.path中,从而使import语句可以正常使用其中的所有内容。这就是为什么库的名称存在固有的重复(本例中为my_dependency)。

关于prefix文件夹,它实际上并没有这样命名,而是有一个特殊的名称,它决定了Sublime Text版本、平台和体系结构的组合(例如,对于包含二进制文件的库很重要)。

prefix文件夹的名称实际上遵循形式{st_version}_{os}_{arch}{st_version}_{os}{st_version}all{st_version}可以是st2st3{os}可以是CCD-64、linuxosx{arch}可以是x32x64

因此,您可以说您的依赖项仅支持st3st3_linuxst3_windows_x64或其任意组合。对于具有本机代码的东西,您可以通过具有多个文件夹来指定几个不同的版本,尽管通常在dependency包含纯Python代码时使用all,无论Sublime版本、操作系统或体系结构如何,这些代码都可以工作。

在这个例子中,如果我们假设prefix文件夹被命名为all,因为my_dependency是纯Python,那么安装这个依赖项的结果将是Packages/my_dependency/all将被添加到sys.path中,这意味着如果您import my_dependency,您将从该文件夹中获得代码。

在开发过程中(或者如果您不想通过Package Control分发依赖项),您可以在包的根目录中创建一个.sublime-dependency文件,如上所示。这应该是一个文本文件,其中只有一行包含2位数字(例如0150)。这控制了每个已安装的依赖项将以何种顺序添加到sys.path。如果您的依赖项没有其他依赖项,您通常会选择一个较低的数字,如果有,则选择一个较高的值(以便在这些依赖项之后注入)。

一旦在Packages文件夹中以正确的格式布置了初始依赖项,就可以使用命令调色板中的命令Package Control: Install Local Dependency,然后选择依赖项的名称。

这导致Package Control"安装"依赖项(即更新0_package_control_loader程序包)以使依赖项处于活动状态。此步骤通常由Package Control在首次安装依赖项时自动执行,因此,如果您也手动分发依赖项,则需要提供执行此步骤的说明。

最新更新