【问题标题】:Global Python packages in Sublime Text plugin developmentSublime Text 插件开发中的全局 Python 包
【发布时间】:2017-07-27 22:31:38
【问题描述】:

1。总结

我没找到,Sublime Text 插件开发者如何使用 Sublime Text 查找全局 Python 包,而不是 Sublime Text 目录的 Python 包。

Sublime Text 使用自己的 Python 环境,而不是机器的 Python 环境。 Developers needs sys.path 用于设置未内置的 Sublime Text Python 包。

是否有任何方法在 Sublime Text 插件中使用全局安装的 Python 包?例如,如果有人告诉我如何更改我的插件,那就太好了——请参阅此问题的 3.2 项。


2。使用 Sublime Text 3 环境的缺点

  1. Sublime Text 3 Build 3126 使用 Python 3.3,但在撰写此问题时发布 Python 3.6 稳定版。 Python 3.6 具有更多功能。
  2. 开发人员需要添加和更新第三方 Python 包,即使它们是为用户安装的。它花费了开发人员的时间。
  3. 对于开发者来说,包的依赖可能会出现问题,请参阅本问题的 6.2 项。

3。示例

1。 Python代码

例如,我编写了 Python 代码 — 将 Поиск Кристиниты 替换为 [**Поиск Кристиниты**](https://github.com/Kristinita/Kristinita.github.io),其中 https://github.com/Kristinita/Kristinita.github.io — DuckDuckGo 查询的第一个链接 Поиск Кристиниты

# -*- coding: utf-8 -*-
import re
import urllib

from bs4 import BeautifulSoup

from w3lib.url import safe_url_string


# ASCII link for solved encoding problems —
# https://stackoverflow.com/a/40654295/5951529
ascii_link = safe_url_string(
    u'http://duckduckgo.com/html/?q=' + 'Поиск Кристиниты',
    encoding="UTF-8")
print(ascii_link)
# SERP DuckDuckGo
serp = urllib.request.urlopen(ascii_link)
# Reading SERP
read_serp = serp.read()
# BeautifulSoup — https://stackoverflow.com/a/11923803/5951529
parsed = BeautifulSoup(read_serp, "lxml")
# Parsed first link
first_link = parsed.findAll(
    'div', {'class': re.compile('links_main*')})[0].a['href']
# Remove DuckDuckGo specific characters —
# https://stackoverflow.com/a/3942100/5951529
remove_duckduckgo_symbols = first_link.replace("/l/?kh=-1&uddg=", "")
# https://stackoverflow.com/a/32451970/5951529
final_link = (urllib.parse.unquote(remove_duckduckgo_symbols))
# Markdown link
markdown_link = '[' + 'Поиск Кристиниты' + ']' + \
    '(' + final_link + ')'

print(markdown_link)

如果我在终端或 SublimeREPL 中运行这个文件,我会得到输出:

[**Поиск Кристиниты**](https://github.com/Kristinita/Kristinita.github.io/)

2。 Sublime Text 插件

现在,基于此代码,我编写了 Sublime Text 插件,用于将 example text 替换为 [**example text**](http://<first link for DuckDuckGo query “example link”>)

import re
import urllib

from bs4 import BeautifulSoup

from w3lib.url import safe_url_string

import sublime_plugin


class KristinitaLuckyLinkCommand(sublime_plugin.TextCommand):

    def run(self, edit):
        # Get selection text
        print('KristinitaLuckyLink called')
        select = self.view.sel()
        selection_region = select[0]
        selection_text = self.view.substr(selection_region)
        print(selection_text)

        # ASCII link for solved encoding problems —
        # https://stackoverflow.com/a/40654295/5951529
        ascii_link = safe_url_string(
            u'http://duckduckgo.com/html/?q=' + (selection_text),
            encoding="UTF-8")
        print(ascii_link)
        # SERP DuckDuckGo
        serp = urllib.request.urlopen(ascii_link)
        # Reading SERP
        read_serp = serp.read()
        # BeautifulSoup — https://stackoverflow.com/a/11923803/5951529
        parsed = BeautifulSoup(read_serp, "lxml")
        # Parsed first link
        first_link = parsed.findAll(
            'div', {'class': re.compile('links_main*')})[0].a['href']
        # Remove DuckDuckGo specific characters —
        # https://stackoverflow.com/a/3942100/5951529
        remove_duckduckgo_symbols = first_link.replace("/l/?kh=-1&uddg=", "")
        # Final link — https://stackoverflow.com/a/32451970/5951529
        final_link = (urllib.parse.unquote(remove_duckduckgo_symbols))
        markdown_link = '[' + selection_text + ']' + \
            '(' + final_link + ')'
        print(markdown_link)

        # Replace selected text to Markdown link
        self.view.replace(
            edit, selection_region, markdown_link)

4。预期行为

如果用户已安装 Python 并安装包

  • pip install beautifulsoup4
  • pip install lxml
  • pip install w3lib

我希望我的 2.2 项中的插件成功地为用户工作。


5。实际行为

如果我保存我的插件,我会得到堆栈跟踪:

Traceback (most recent call last):
  File "D:\Sublime Text Build 3126 x64 For Debug\sublime_plugin.py", line 109, in reload_plugin
    m = importlib.import_module(modulename)
  File "./python3.3/importlib/__init__.py", line 90, in import_module
  File "<frozen importlib._bootstrap>", line 1584, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1565, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1532, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 584, in _check_name_wrapper
  File "<frozen importlib._bootstrap>", line 1022, in load_module
  File "<frozen importlib._bootstrap>", line 1003, in load_module
  File "<frozen importlib._bootstrap>", line 560, in module_for_loader_wrapper
  File "<frozen importlib._bootstrap>", line 868, in _load_module
  File "<frozen importlib._bootstrap>", line 313, in _call_with_frames_removed
  File "D:\Sublime Text Build 3126 x64 For Debug\Data\Packages\Grace Splitter\kristi.py", line 4, in <module>
    from bs4 import BeautifulSoup
ImportError: No module named 'bs4'

6。没有帮助

1。使用计算机的全局 Python 环境

我不知道,我该怎么做。我可以找到的问题示例:

2。使用 Sublime Text 环境

我安装

我将我的w3lib 目录从C:\Python36\Lib\site-packages 复制到Sublime Text 的Data\Packages 目录。

我在 Sublime Text 3 控制台中运行:

>>> window.run_command("kristinita_lucky_link")

我得到堆栈跟踪:

Traceback (most recent call last):
  File "D:\Sublime Text 3 x64\sublime_plugin.py", line 818, in run_
    return self.run(edit)
  File "D:\Sublime Text 3 x64\Data\Packages\KristinitaLuckyLink\KristinitaLuckyLink.py", line 32, in run
    parsed = BeautifulSoup(read_serp, "lxml")
  File "D:\Sublime Text 3 x64\Data\Packages\bs4\__init__.py", line 165, in __init__
    % ",".join(features))
bs4.FeatureNotFound: Couldn't find a tree builder with the features you requested: lxml. Do you need to install a parser library?

没找到,怎么设置lxml

3。在 2 个文件中使用变量

例如我在同一目录中有KristinitaLuckyLink.pyKrisDuckDuckGo.py 文件。

我的KristinitaLuckyLink.py 文件:

import re
import requests
import sublime_plugin
import subprocess
import sys
sys.path.append(
    'D:\Sublime Text 3 x64\Data\Packages\KristinitaLuckyLink\KrisDuckDuckGo.py')

from KrisDuckDuckGo import final_link
from bs4 import BeautifulSoup


class KristinitaLuckyLinkCommand(sublime_plugin.TextCommand):

    def run(self, edit):
        # Get selection text
        print('KristinitaLuckyLink called')
        select = self.view.sel()
        selection_region = select[0]
        selection_text = self.view.substr(selection_region)
        print(selection_text)

        # Get terminal output — https://stackoverflow.com/a/4760517/5951529
        # Paths is correct
        result = subprocess.run(["C:\Python36\python.exe", "D:\Sublime Text 3 x64\Data\Packages\KristinitaLuckyLink\krisduckduckgo.py"],
                                stdout=subprocess.PIPE)
        final_link = result.stdout.decode('utf-8')
        print(final_link)

        # Markdown link
        markdown_link = '[' + selection_text + ']' + \
            '(' + final_link + ')'
        print(markdown_link)

        # Replace selected text to Markdown link
        self.view.replace(
            edit, selection_region, markdown_link)

我的KrisDuckDuckGo.py 文件:

import urllib

import sys
sys.path.append(
    'D:\Sublime Text 3 x64\Data\Packages\KristinitaLuckyLink\KristinitaLuckyLink.py')

from w3lib.url import safe_url_string

from KristinitaLuckyLink import selection_text

from bs4 import BeautifulSoup


# ASCII link for solved encoding problems —
# https://stackoverflow.com/a/40654295/5951529
ascii_link = safe_url_string(
    u'http://duckduckgo.com/html/?q=' + (selection_text),
    encoding="UTF-8")
print(ascii_link)
# SERP DuckDuckGo
serp = urllib.request.urlopen(ascii_link)
# Reading SERP
read_serp = serp.read()
# BeautifulSoup — https://stackoverflow.com/a/11923803/5951529
parsed = BeautifulSoup(read_serp, "lxml")
# Parsed first link
first_link = parsed.findAll(
    'div', {'class': re.compile('links_main*')})[0].a['href']
# Remove DuckDuckGo specific characters —
# https://stackoverflow.com/a/3942100/5951529
remove_duckduckgo_symbols = first_link.replace("/l/?kh=-1&uddg=", "")
# Final link — https://stackoverflow.com/a/32451970/5951529
final_link = (urllib.parse.unquote(remove_duckduckgo_symbols))
print(final_link)

我选择任何文本 → 我在 Sublime Text 控制台中打印:

window.run_command("kristinita_lucky_link")

我在 Sublime Text 控制台中没有得到输出。


7。环境

操作系统和版本:
Windows 10 企业版 LTSB 64 位 EN
Sublime Text:
构建 3126
Python:
3.6.0

【问题讨论】:

    标签: python python-3.x sublimetext3 sublimetext sublime-text-plugin


    【解决方案1】:

    错误:

    bs4.FeatureNotFound: Couldn't find a tree builder with the features you requested: lxml. Do you need to install a parser library?
    

    很可能是因为 Sublime Text 发现 lxml 是作为全局 Python 环境的一部分安装的——它是为与 ST 使用的不同版本的 Python 编译的——所以它无法加载它。

    我们希望 ST 找到您在问题中链接到的 sublime-lxml 依赖项。 “为什么找错了?”,你可能会问。很难确定,但我认为OdatNurd's answer gives us some clues - ST 出于某种原因看到的sys.path 包括您所有的全局 Python 包。默认情况下,ST 应该只使用它的可执行文件所在的文件夹和 ST 数据目录中的 Packages 文件夹。例如,在我的系统上,在 ST 的控制台中执行 import sys; sys.path 会给我:

    [
     'C:\\Program Files\\Sublime Text 3',
     'C:\\Program Files\\Sublime Text 3/python3.3.zip',
     'C:\\Users\\Keith\\AppData\\Roaming\\Sublime Text 3\\Packages',
     'C:\\Users\\Keith\\AppData\\Roaming\\SUBLIM~1\\Packages\\lxml\\ST3_WI~2'
    ]
    

    即没有site-packages 文件夹。

    因此,解决方案是:

    一个。您可以尝试卸载系统范围的lxml 包,以便ST 只能找到sublime-lxml 依赖包,但实际上这只是一个临时措施。最好是: 湾。调整 ST 正在使用的路径环境变量(项目的顺序)。 (即删除对site-packages 的所有引用,或者至少将它们移动到 ST 文件夹之后。)

    选项 B 不应影响任何其他 ST 插件,因为它们也会遇到同样的问题。我怀疑其他一些包改变了 ST 使用的路径,但如果不搜索它们,可能不容易找出哪个包。当使用构建系统时,用于这些的路径与插件用于加载其模块的路径完全不同,因此构建系统也应该不受此“修复”的影响。

    【讨论】:

      【解决方案2】:

      如果你想在你的计算机上使用 Python 版本,它不是 Sublime 提供的,你可以使用以下 Sublime 命令:

      window.run_command("exec", {"shell_cmd" : '&lt;your command here&gt;'})

      此命令允许您在 Sublime 之外的计算机上执行应用程序。 只需将&lt;your command here&gt; 替换为您要执行的shell 命令即可。 输出将在 sublime 的输出面板(终端)中返回。该输出面板与 Sublime 控制台不同。

      您可以将要使用的本地 Python 的路径放在那里,并使用脚本(需要额外的 Python 模块)作为参数。 例如:

      window.run_command("exec", {"shell_cmd" : 'C:\Python36\python.exe D:\Sublime Text 3 x64\Data\Packages\KristinitaLuckyLink\LuckyLink\LuckyLink.py'})

      你需要将你的包分成两个文件。一个描述你的包和 Sublime 内部所需命令的文件。另一个文件(它应该在你的包目录的子目录中——所以它不会被 Sublime 当作插件)包含你的代码的其余部分。 第二个文件是导入未包含在 Sublime 内部 Python 中的 Python 模块的地方。 此外,您需要找到一种方法将要使用的选择或字符串/url 传输到第二个脚本。

      您可以在我的 Sublime 包PlotGraph 中查看我是如何做到的。

      【讨论】:

        【解决方案3】:

        Python 通过sys.path 从它知道的位置加载模块,并且在 Sublime Text 3 中,sys.path 仅指向几个位置(例如,Sublime 可执行文件附带的软件包的位置,用户安装的软件包的位置当前用户等)。

        为了从 Sublime 文本插件中访问全局安装的 Python 模块,您需要在插件中将其位置显式添加到 sys.path

        假设一台 Linux 机器的 python 包安装在 /usr/local/lib/python3.4/site-packages 中(将其修改为 Windows 机器的适当路径),示例如下:

        import sublime
        import sublime_plugin
        import sys
        
        site_path="/usr/local/lib/python3.4/site-packages"
        if site_path not in sys.path:
            sys.path.append(site_path)
        
        from bs4 import BeautifulSoup
        
        class ExampleCommand(sublime_plugin.TextCommand):
            def run(self, edit):
                parser = BeautifulSoup('<a href="#">Link Text</a>', "html.parser")
                a = parser.find("a")
                text = a.contents[0]
                self.view.insert(edit, 0, text)
        

        这会专门添加到sys.path 的路径(如果它不存在),从而允许导入和使用模块。

        Sublime 包含自己独特的 Python 版本,专门用于沙箱本身,因此它完全不依赖于任何版本的 python 或安装在用于运行它的计算机上的任何 python 包,这就是它不依赖的原因默认情况下不会为您执行此操作。

        【讨论】:

        • OdatNurd,您是否尝试运行第 3.2 节中的真实示例?我尝试像您的示例一样使用 site_path,但我得到了与 6.2 部分相同的错误 — bs4.FeatureNotFound: Couldn't find a tree builder with the features you requested: lxml. Do you need to install a parser library? 谢谢。
        • 我检查了我发布的代码是否有效,并从 Sublime Text 插件中与全局安装的 python 库对话,从而证明该技术有效。不过,我对beautifulsoup 的了解还不够,不知道它为什么会出现这个错误。
        • I checked that the code I posted works — 它对我不起作用(是的,我设置了 site_path = "C:\Python36\Lib\site-packages)。我进入了 Sublime Text 控制台AttributeError: 'NoneType' object has no attribute 'content'。谢谢。
        • 如果没有完整的错误上下文,不确定会发生什么。
        【解决方案4】:

        是的,插件可以与用户全局安装的 Python 模块一起使用。您没有义务向您的 Sublime Text 插件引入模块。

        问题

        1。 Python 3.3 兼容性

        2018 年初

            您在插件中使用的所有全局模块都必须与 Python 3.3 兼容。

        1.1。论证

        1.1.1。 Sublime Text sys.path 顺序

        例如,我添加到Default.sublime-package 存档文件0000.pyDefault.sublime-package 的模块是 loaded first 由 Sublime Text 启动。

        0000.py内容:

        import sys
        
        sys.path.append('C:\\Python36')
        sys.path.append('C:\\Python36\\python36.zip')
        sys.path.append('C:\\Python36\\DLLs')
        sys.path.append('C:\\Python36\\lib')
        sys.path.append('C:\\Python36\\lib\\site-packages')
        

        路径——我的全局sys.path

        >>> import sys; sys.path
        ['', 'C:\\Python36', 'C:\\Python36\\python36.zip', 'C:\\Python36\\DLLs', 'C:\\Python36\\lib', 'C:\\Python36\\lib\\site-packages']
        

        我重新启动 Sublime Text → 我在控制台中看到:

        DPI scale: 1
        startup, version: 3143 windows x64 channel: stable
        executable: /D/Sublime Text Build 3143 x64 For Debug/sublime_text.exe
        working dir: /D/Kristinita
        packages path: /D/Sublime Text Build 3143 x64 For Debug/Data/Packages
        state path: /D/Sublime Text Build 3143 x64 For Debug/Data/Local
        zip path: /D/Sublime Text Build 3143 x64 For Debug/Packages
        zip path: /D/Sublime Text Build 3143 x64 For Debug/Data/Installed Packages
        ignored_packages: ["Anaconda", "Vintage"]
        pre session restore time: 0.458819
        startup time: 0.493818
        first paint time: 0.506818
        reloading plugin Default.0000
        reloading plugin Default.auto_indent_tag
        reloading plugin Default.block
        # And so on
        
        >>> import sys; sys.path
        ['D:\\Sublime Text Build 3143 x64 For Debug', 'D:\\Sublime Text Build 3143 x64 For Debug\\python3.3.zip', 'D:\\Sublime Text Build 3143 x64 For Debug\\Data\\Lib\\python3.3', 'D:\\Sublime Text Build 3143 x64 For Debug\\Data\\Packages', 'C:\\Python36', 'C:\\Python36\\python36.zip', 'C:\\Python36\\DLLs', 'C:\\Python36\\lib', 'C:\\Python36\\lib\\site-packages']
        

        来自内部环境的路径在前面,即全局环境中的路径。不知道怎么改。

        1.1.2。 Python 3.6 模块

        例如,我想在我的插件中使用Python Google Search API PyPI 模块。它与 Python 3.6 兼容,但 not compatible with Python 3.3.

        我在上面的小节中注入文件0000.py → 我创建插件SashaGSearch.py,它的内容:

        import sublime_plugin
        
        from gsearch.googlesearch import search
        
        
        class GoogleSearchCommand(sublime_plugin.TextCommand):
        
            def run(self, edit):
        
                results = search('kristinitaluckylife', num_results=1)
                r = results[0][1]
                print(r)
        

        我重启Sublime Text → 我得到same traceback,好像0000.py没有被实现。

        1.2。 2018年初的情况

        查看 Sublime Text 论坛讨论:

        我希望,在不久的将来,在内部 Sublime Text 环境中,Python 3.3 将被替换为 3.6 或下一个 3.7 版本。


        2。环境变量

        如果您知道,可以不要添加新的环境变量,请回复this question

        2.1。插件代码

        您需要在您的 PC 环境变量中创建,其中 value — site-packages 文件夹的路径。比如我把它命名为PYTHONPACKAGES

        您需要添加到您的代码行,如OdatNurd answer:

        # PYTHONPACKAGES path:
        # https://stackoverflow.com/a/4907053/5951529
        # Disable duplicate paths:
        # https://stackoverflow.com/a/42656754/5951529
        site_packages = (os.environ['PYTHONPACKAGES'])
        if site_packages not in sys.path:
            sys.path.append(site_packages)
        

        2.2.激活说明

        您插件的用户还必须在操作系统中添加环境变量PYTHONPACKAGES。在您的包裹描述中,您需要添加说明,如何操作。


        3。重启

        对于开发过程:

            如果您更改site-packages文件夹或子文件夹中的文件,您可能需要重新启动Sublime Text。

        3.1。示例

        从 pip 安装 examplesashamodule → 启动 Sublime Text → 将 examplesashamodule 导入插件 → 从 site-packages 文件夹中删除 examplesashamodule → 插件将工作,因为 examplesashamodule 仍在 site-packages文件夹。 AutomaticPackageReloader 包在这种情况下没有帮助。

        您重新启动 Sublime Text → 您在 Sublime Text 控制台中获得回溯,即ImportError: No module named 'examplesashamodule'

        显然,Sublime Text 在会话中缓存来自外部模块的数据。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-05-12
          • 1970-01-01
          • 2013-11-16
          • 2013-02-17
          • 1970-01-01
          • 1970-01-01
          • 2012-12-11
          • 1970-01-01
          相关资源
          最近更新 更多