2020年3月

在 Linux、Mac 或 Windows 上为旧版 Python 安装 pip。

Python 是一种功能强大、流行广泛的编程语言,在常规编程、数据科学等很多方面它都有丰富的软件包可供使用。但这些软件包通常都不会在 Python 安装时自动附带,而是需要由用户自行下载、安装和管理。所有的这些软件包(包括库和框架)都存放在一个名叫 PyPI(也就是 Python 软件包索引 Python Package Index )的中央存储库当中,而 pip(也就是 首选安装程序 Preferred Installer Program )则是管理这个中央存储库的工具。

在安装 pip 之后,管理 PyPI 的软件包就变得很方便了。大部分的软件包都可以通过在终端或者命令行界面执行 python -m pip install <软件包名> 这样的命令来完成安装。

较新版本的 Python 3(3.4 或以上)和 Python 2(2.7.9 或以上)都已经预装了 pip,旧版本的 Python 没有自带 pip,但可以另外安装。

在这篇文章中,我将会介绍 pip 在 Linux、Mac 和 Windows 系统上的安装过程。想要了解更多详细信息,可以参考 pip.pypa 的文档。

首先需要安装 Python

首先你的系统中需要安装好 Python,否则 pip 安装器无法理解任何相关的命令。你可以在命令行界面、Bash 或终端执行 python 命令确认系统中是否已经安装 Python,如果系统无法识别 python 命令,请先下载 Python 并安装。安装完成后,你就可以看到一些引导你安装 pip 的提示语了。

在 Linux 上安装 pip

在不同发行版的 Linux 上,安装 pip 的命令也有所不同。

在 Fedora、RHEL 或 CentOS 上,执行以下命令安装:

$ sudo dnf install python3

在 Debian 或 Ubuntu 上,使用 apt 包管理工具安装:

$ sudo apt install python3-pip

其它的发行版可能还会有不同的包管理工具,比如 Arch Linux 使用的是 pacman

$ sudo pacman -S python-pip

执行 pip --version 命令就可以确认 pip 是否已经正确安装。

在 Linux 系统上安装 pip 就是这样简单,你可以直接跳到“使用 pip”部分继续阅读。

在 Mac 上安装 pip

MacOS 已经预装了 Python,但 Python 版本一般是比较旧的。如果你要使用 Python 的话,建议另外安装 Python 3

在 Mac 上可以使用 homebrew 安装 Python 3:

$ brew update && brew upgrade python

如果你安装的是以上提到的新版本 Python 3,它会自带 pip 工具。你可以使用以下命令验证:

$ python3 -m pip --version

如果你使用的是 Mac,安装过程也到此结束,可以从“使用 pip”部分继续阅读。

在 Windows 上安装 pip

以下 pip 安装过程是针对 Windows 8 和 Windows 10 的。下面文章中的截图是 Windows 10 环境下的截图,但对 Windows 8 同样适用。

首先确认 Python 已经安装完成

如果你想在 Windows 系统上像 Linux 一样使用包管理工具,Chocolatey 是一个不错的选择,它可以让 Python 的调用和更新变得更加方便。Chocolatey 在 PowerShell 中就可以运行,只需要简单的命令,Python 就可以顺利安装。

PS> choco install python

接下来就可以使用 pip 安装所需的软件包了。

使用 pip

在 Linux、BSD、Windows 和 Mac 上,pip 都是以同样的方式使用的。

例如安装某个库:

python3 -m pip install foo --user

卸载某个已安装的库:

python3 -m pip uninstall foo

按照名称查找软件包:

python3 -m pip search foo

pip 更新到一个新版本:

$ sudo pip install --upgrade pip

需要注意的是,在 Windows 系统中不需要在前面加上 sudo 命令,这是因为 Windows 通过独有的方式管理用户权限,对应的做法是创建一个执行策略例外

python -m pip install --upgrade pip

希望本文介绍的的方法能对你有所帮助,欢迎在评论区分享你的经验。


via: https://opensource.com/article/20/3/pip-linux-mac-windows

作者:Vijay Singh Khatri 选题:lujun9972 译者:HankChow 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

有时,由于某些应用依赖性,你可能会意外更新不想更新的软件包。这在全系统更新或自动包升级时经常会发生。如果发生这种情况,可能会破坏应用的功能。这会造成严重的问题,你需要花费大量时间来解决问题。

如何避免这种情况?如何从 apt-get 更新中排除软件包?

如果你要从 Yum Update 中排除特定软件包,请参考这篇。

是的,可以在 Debian 和 Ubuntu 系统上使用以下三种方法来完成。

我们将分别详细展示。

方法 1:如何使用 apt-mark 命令排除 Debian/Ubuntu 系统上的软件包更新

apt-mark 用于将软件包标记/取消标记为自动安装。

hold 选项用于将软件包标记为保留,以防止软件包被自动安装、升级或删除。

unhold 选项用于取消先前面的设置,以允许重复执行所有操作。

运行以下命令以使用 apt-mark 命令保留指定的软件包。

$ sudo apt-mark hold nano
nano set on hold.

保留软件包后,请运行以下 apt-mark 命令查看它们。

$ sudo apt-mark showhold
nano

这表明在执行完整的系统更新时,不会升级 nano 包。

$ sudo apt update

Reading package lists… Done
Building dependency tree
Reading state information… Done
Calculating upgrade… Done
The following packages have been kept back:
  nano
0 upgraded, 0 newly installed, 0 to remove and 1 not upgraded.

运行以下命令,使用 apt-mark 命令取消保留 nano 包。

$ sudo apt-mark unhold nano
Canceled hold on nano.

方法 2:如何使用 dpkg 命令在 Debian/Ubuntu 系统上排除软件包更新

dpkg 命令是一个 CLI 工具,用于安装、构建、删除和管理 Debian 软件包。dpkg 的主要且更用户友好的前端是 aptitude

运行以下命令使用 dpkg 命令阻止给定的软件包。

语法:

$ echo "package_name hold" | sudo dpkg --set-selections

运行以下 dpkg 命令以保留 apache2 包。

$ echo "apache2 hold" | sudo dpkg --set-selections

保留软件包后,请运行以下命令查看它们。

$ sudo dpkg --get-selections | grep "hold"
apache2                        hold

它会显示在执行完整的系统更新时,不会升级 apache2包。

$ sudo apt update

Reading package lists… Done
Building dependency tree
Reading state information… Done
Calculating upgrade… Done
The following packages have been kept back:
  apache2
0 upgraded, 0 newly installed, 0 to remove and 1 not upgraded.

运行以下命令,使用 dpkg 命令取消对指定软件包的保留。

语法:

$ echo "package_name install" | sudo dpkg --set-selections

运行以下命令,使用 dpkg 命令取消保留 apache2 包。

$ echo "apache2 install" | sudo dpkg --set-selections

方法 3:如何使用 aptitude 命令排除 Debian/Ubuntu 系统上的软件包更新

aptitude 命令是 Debian 及其衍生版本的基于文​​本的软件包管理界面。

它允许用户查看软件包列表并执行软件包管理任务,例如安装、升级和删除软件包。它可以从可视界面或命令行执行操作。

运行以下命令,使用 aptitude 命令保留指定的软件包。

$ sudo aptitude hold python3

保留某些软件包后,请运行以下命令查看它们。

$ sudo dpkg --get-selections | grep "hold"
或者
$ sudo apt-mark showhold

python3

这表明在执行完整的系统更新时,不会升级 python3 软件包。

$ sudo apt update

Reading package lists… Done
Building dependency tree
Reading state information… Done
Calculating upgrade… Done
The following packages have been kept back:
  python3
0 upgraded, 0 newly installed, 0 to remove and 1 not upgraded.

使用 aptitude 命令运行以下命令以解除对 python3 软件包的保留。

$ sudo aptitude unhold python3

via: https://www.2daygeek.com/debian-ubuntu-exclude-hold-prevent-packages-from-apt-get-upgrade/

作者:Magesh Maruthamuthu 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

任意一个开源电子书应用都能使你在大屏设备上的阅读体验得到提升。

 title=

我通常使用手机或 Kobo 阅读器阅读电子书,总是没有在大屏设备上阅读书籍那么爽。很多人经常在自己的笔记本或桌面电脑上阅读电子书,如果你有这种需求(或者你认为以后会遇到这种情况),我想向你介绍三种 Linux 桌面下的电子书阅读器。

Bookworm(书虫)

Bookworm 意在成为一个“简洁、专注的电子书阅读器”。事实证明它也确实做到了。Bookworm 内置一系列基础功能,有人抱怨它太过简单,缺少功能性(虽然这词显然有点指意不明)。Bookworm 起到了应有的作用而没有无关的花哨功能。

该应用的界面整洁干净。

 title=

你可通过以下方式查看书籍:

  • 空格键前往下一页
  • 上下方向键按行移动
  • 左右方向键跳转至前后章节

你也可以通过添加注释或书签跳转至特定页面。

 title=

Bookworm 没有太多设置选项。你能调整书籍字体大小及间距,开启双页模式或夜间模式。在应用中添加新文件夹后,Bookworm 会自动扫描文件夹中的电子书。

 title=

Bookworm 支持常用的电子书格式:EPUB、PDF、MOBI、FB2,你还能用它阅读 CBR 和 CBZ 格式的数字版漫画。我只在 Bookworm 上测试了前三种格式,PDF 倒是能打开,但加载速度很慢,格式也很难看。

Foliate

单从功能上来说,Foliate 确实比 Bookworm 强上一些。Foliate 不仅功能更强,它还有更多设置选项。这个功能多样、简单干净的应用满足了所有要求。

 title=

在 Foliate 中可以通过空格、方向键、向上或向下翻页键浏览电子书,这里没什么特别的。

你还可以在书籍中添加注释、查找以及翻译字句、查询字词含义。如果你电脑上安装了智能语音应用, Foliate 还能将文本朗读出来。

在 Foliate 中的注释文本

Foliate 与 Bookworm 相比有更多自定义选项。你能在应用中修改字体类型及大小、行间距、电子书页边距,也可以调高或降低屏幕亮度,从应用内置的四个主题中选择一个等。

 title=

你可以在 Foliate 上阅读 EPUB、MOBI、AZW、AZW3 格式的电子书。如果你不了解,我可以提前告诉你,后三种格式是亚马逊 Kindle 阅读器上架的书籍的闭源格式。

Calibre 电子书阅读器

这个电子书阅读器Calibre 电子书管理工具的一个组件,就像它的前代一样,电子书阅读器这部分并不是软件整体最棒的部分。

 title=

不过别被上面的话吓到,它也的确是个桌面电子书阅读器。

在 Calibre 电子书阅读器中,你可以通过方向键、向上/向下翻页键以及空格浏览书籍,还能在在线字典中查找词语含义,添加书签。这一阅读器没有注释功能,但内置的搜索引擎却很好用,你还能将书籍保存为 PDF 格式(虽然我不太明白你为什么要这么做)。

不过参数设置这里确是它出彩的地方,它的可选设置比 Bookworm 和 Foliate 加起来都多,从字体到文本布局,页面分割方式你都能改。除此之外,你还能自定义应用按键设置,将你喜欢的词典网站添加进来,方便阅读查找。

 title=

Calibre 电子书阅读器一个很有用的功能,就是把自行设置的 CSS 配置文件效果应用到电子书上。赘言一句,CSS 是一种调整网页格式的方法(这类文件就是许多电子书的一个组成部分)。如果你是使用 CSS 的高手,你可以将自己的 CSS 文件复制粘贴到配置窗口的 “User stylesheet” 部分,这就是个性化的究极办法。

据开发者描述,这一电子书阅读器“能支持所有主流电子书格式”。如果你想知道具体支持格式是什么,这是列表链接。我测试了列表中的一小部分格式,没有发现问题。

留在最后的话

不管你只是单纯想找个电子书阅读器,还是有个更多复杂功能的应用,上文提到的三个选择都很不错,而且它们都能提升你在大屏设备上的阅读体验。


via: https://opensource.com/article/20/2/linux-ebook-readers

作者:Scott Nesbitt 选题:lujun9972 译者:wenwensnow 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

我们的小程序们

发轫于三年前 Bestony 开发了我们 Linux 中国的第一款小程序“运维密码”之后,我们陆陆续续的又开发了一些微信小程序,给大家提供一些或有用、或有趣的小功能。

这里我做个整理:

运维密码

这是一个 OTP 口令管理器,用于提供双因子认证功能。基本功能完善,不过存在一些小 bug,功能上也许久没有更新了。目前还在计划中的 2.0 版本还遥遥无期……

相关文章

Linux

这是一个 Linux 命令用法速查的小工具,数据来源于 tldr.sh ,我们在这个小程序中不但提供了命令速查的功能,而且提供了一种众包的翻译机制。目前已经几乎全部翻译完成了,当然,已经翻译的条目也在时不时的改进完善之中。

相关文章:

Linux文章

这是一个以一种比较奇特的方式来搜索、阅读我们发布的全部文章的小程序,我给它的定位是我们的官网小程序。尤其是,目前我们官网已经停止注册,而没有注册是无法在站内搜索文章的,这个小程序可以弥补这个需求窗口。

相关文章:

文章助手

鉴于微信公众号文章中的链接在大部分情况下都不能点击,虽然我们提供了比较好的解决方案来解决访问链接的需求(通过原文链接提供站内链接、图片等素材的访问),但是很多人还是直觉上会去点击链接,而又失望。所以我们通过将链接包装到小程序当中提供一种更佳的链接访问的用户体验。

相关文章:

新的服务号

早在 2013 年 3 月,我们就开设了微信公众号,而除了主号“Linux中国”之外,还有若干矩阵号,以服务于不同侧面的读者。不过后面由于精力所限和内容量的问题,我们又关闭了大部分矩阵号。

不过,我们现在又将其中一个服务号重新激活,以做这些小程序(以及将来可能的其它产品服务)的支持,比如说提供消息推送、更新信息等等。

这个号就是“璃霓思”——这个名字也是“Linux”的谐音。

当然,除此之外,我们还有两个公众号,这里一起汇总一下:

璃霓思

这就是我们产品服务号,每个月最多只能推送四次,因此推送频率和时间完全没准,不过,如果感兴趣我们的产品/服务,这里可以及时收到消息,而不会被遗漏。

除了这些小程序们,我们主要的工作还是集中在翻译的文章发布上,这些都可以在下面的公众号上看到。

Linux中国

这是我们的主号,每天更新的内容主要来自于我们的翻译组 LCTT 的贡献所出。内容涉及到开发、运维、安全、开源文化等等,由于翻译组选题广泛,译者偏好不同,因此这个号发布的内容深浅不一,方向各异。

Linux

这是我们的精选号,会对主号发布的内容精选后,延后几天发布出来,如果觉得主号内容比较繁杂,可以只关注这个号,相对来说比较精要。

最后,希望大家多多支持,多多提出建议。

在本教程中,你将学习如何在 Ubuntu 和其它基于 Ubuntu 的发行版上安装最新的 Wireshark。同时你也将学习如何在没有 sudo 的情况下来运行 Wireshark,以及如何设置它来进行数据包嗅探。

Wireshark 是一款自由开源的网络协议分析器,它在全球被广泛使用。

通过使用 Wireshark,你可以实时捕获网络的传入和传出数据包,并将其用于网络故障排除、数据包分析、软件和通信协议开发等。

它适用于所有主流的桌面操作系统,如 Windows、Linux、macOS、BSD 等。

在本教程中,我将指导你在 Ubuntu 和其他基于 Ubuntu 的发行版上安装 Wireshark 。我还将介绍如何设置和配置 Wireshark 来捕获数据包。

在基于 Ubuntu 的 Linux 发行版上安装 Wireshark

Wireshark 适用于所有主流 Linux 发行版。你应该查看官方安装说明。因为在本教程中,我将着重在仅基于 Ubuntu 的发行版上安装最新版本的 Wireshark 。

Wireshark 可以在 Ubuntu 的 Universe 存储库中找到。你可以启用 universe 存储库,然后按如下方式安装:

sudo add-apt-repository universe
sudo apt install wireshark

这种方法的一个小问题是,你可能并不总是得到最新版本的 Wireshark 。

例如,在 Ubuntu 18.04 中,如果你使用 apt 命令检查 Wireshark 的可用版本,可用版本会显示是 2.6 。

abhishek@nuc:~$ apt show wireshark
Package: wireshark
Version: 2.6.10-1~ubuntu18.04.0
Priority: optional
Section: universe/net
Origin: Ubuntu
Maintainer: Balint Reczey <[email protected]>

然而, Wireshark 3.2 稳定版已经在几个月前发布了。当然,新版本拥有新的特性。

那么在这种情况下,你应该怎么办呢?谢天谢地, Wireshark 开发者提供了一种官方 PPA 方式,你可以使用它在 Ubuntu 和其它基于 Ubuntu 的发行版上安装最新稳定版本的 Wireshark.

我希望你熟悉 PPA。如果没有,请阅读我们关于 PPA 的优秀指南,以便完全理解它

打开终端并逐个使用以下命令:

sudo add-apt-repository ppa:wireshark-dev/stable
sudo apt update
sudo apt install wireshark

即使安装了旧版本的 Wireshark ,它也将更新为新版本。

安装时,系统将询问你是否允许非超级用户捕获数据包。选择“Yes”允许,选择“No”限制非超级用户捕获数据包,最后完成安装。

不使用 sudo 运行 Wireshark

如果在上一次安装中选择了“No”,则以 root 用户身份运行以下命令:

sudo dpkg-reconfigure wireshark-common

然后按 tab 键并使用回车键选择“No”:

由于你允许非超级用户捕获数据包,因此你必须将该用户添加到 wireshark 组。使用 usermod 命令将自己添加到 wireshark 组。

sudo usermod -aG wireshark $(whoami)

最后,重启你的 Ubuntu 系统对你的系统进行必要的修改。

冷知识

Wireshark 于 1998 年首次发布,最初被称为 Ethereal 。2006 年,由于商标问题,开发商不得不将其名称改为 Wireshark 。

启动 Wireshark

你可以从应用程序启动器或者命令行启动 Wireshark 应用。

如果从命令行启动,只需要在你的控制台中输入 wireshark

wireshark

要是从图形化界面启动,需要在搜索栏搜索 Wireshark 应用,并按回车键。

现在,让我们来玩玩 Wireshark 吧。

使用 Wireshark 捕获数据包

当你启动 Wireshark 的时候,你会看到一个接口列表,你可以使用它来捕获这个接口接收和发送的数据包。

你可以使用 Wireshark 监视许多类型的接口,例如,有线、外部设备等。根据你的偏好,你可以从下图中的标记区域选择在欢迎屏幕中显示特定类型的接口。

Select interface

例如,我只列出了有线网络接口。

接下来,要开始捕获数据包,你必须选择接口(在我的示例中是 ens33 ),然后单击“Start capturing packets”图标,如下图所示。

Start capturing packets with Wireshark

你还可以同时从多个接口捕获数据包。只需按住 CTRL 按钮,同时单击要捕获的接口,然后点击“Start capturing packets”图标,如下图所示。

接下来,我尝试在终端中使用 ping google.com 命令,如你所见,捕获了许多数据包。

Captured packets

现在你可以选择任何数据包来检查该特定数据包。在点击一个特定的包之后,你可以看到与它相关的 TCP/IP 协议的不同层的信息。

Packet info

你还可以在底部看到该特定数据包的原始数据,如下图所示。

Check RAW data in the captured packets

这就是为什么端到端加密很重要。

假设你正在登录一个不使用 HTTPS 的网站。与你在同一网络上的任何人都可以嗅探数据包,并在原始数据中看到用户名和密码。

这就是为什么现在大多数聊天应用程序使用端到端加密,而大多数网站使用 https (而不是 http)。

在 Wireshark 中停止数据包捕获

你可以点击如图所示的红色图标停止捕获数据包。

Stop packet capture in Wireshark

将捕获的数据包保存成文件

你可以单击下图中标记的图标,将捕获的数据包保存到文件中以备将来使用。

Save captured packets by Wireshark

注意:输出可以导出为 XML、PostScript、CSV 或纯文本。

接下来,选择一个目标文件夹,键入文件名并单击“Save”。

然后选择文件并单击“Open”。

现在你可以随时打开和分析保存的数据包。要打开文件,请按 \+o,或从 Wireshark 转到 “File > Open”。

捕获的数据包将从文件中加载。

结语

Wireshark 支持许多不同的通信协议。有许多选项和功能,使你能够以独特的方式捕获和分析网络数据包。你可以从 Wireshark 的官方文档了解更多关于 Wireshark 的信息。

我希望这个教程能帮助你在 Ubuntu 上安装 Wireshark 。请让我知道你的问题和建议。


via: https://itsfoss.com/install-wireshark-ubuntu/

作者:Community 选题:lujun9972 译者:qianmigntian 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

你对如何让调试器变得更快产生过兴趣吗?本文将分享我们在为 Python 构建调试器时得到的一些经验。

整段故事讲的是我们在 Rookout 公司的团队为 Python 调试器开发不中断断点的经历,以及开发过程中得到的经验。我将在本月于旧金山举办的 PyBay 2019 上介绍有关 Python 调试过程的更多细节,但现在就让我们立刻开始这段故事。

Python 调试器的心脏:sys.set\_trace

在诸多可选的 Python 调试器中,使用最广泛的三个是:

  • pdb,它是 Python 标准库的一部分
  • PyDev,它是内嵌在 Eclipse 和 Pycharm 等 IDE 中的调试器
  • ipdb,它是 IPython 的调试器

Python 调试器的选择虽多,但它们几乎都基于同一个函数:sys.settrace。 值得一提的是, sys.settrace 可能也是 Python 标准库中最复杂的函数。

 title=

简单来讲,settrace 的作用是为解释器注册一个跟踪函数,它在下列四种情形发生时被调用:

  • 函数调用
  • 语句执行
  • 函数返回
  • 异常抛出

一个简单的跟踪函数看上去大概是这样:

def simple_tracer(frame, event, arg):
  co = frame.f_code
  func_name = co.co_name
  line_no = frame.f_lineno
  print("{e} {f} {l}".format(
e=event, f=func_name, l=line_no))
  return simple_tracer

在分析函数时我们首先关注的是参数和返回值,该跟踪函数的参数分别是:

  • frame,当前堆栈帧,它是包含当前函数执行时解释器里完整状态的对象
  • event,事件,它是一个值可能为 calllinereturnexception 的字符串
  • arg,参数,它的取值基于 event 的类型,是一个可选项

该跟踪函数的返回值是它自身,这是由于解释器需要持续跟踪两类跟踪函数:

  • 全局跟踪函数(每线程):该跟踪函数由当前线程调用 sys.settrace 来设置,并在解释器创建一个新的堆栈帧时被调用(即代码中发生函数调用时)。虽然没有现成的方式来为不同的线程设置跟踪函数,但你可以调用 threading.settrace 来为所有新创建的 threading 模块线程设置跟踪函数。
  • 局部跟踪函数(每一帧):解释器将该跟踪函数的值设置为全局跟踪函数创建帧时的返回值。同样也没有现成的方法能够在帧被创建时自动设置局部跟踪函数。

该机制的目的是让调试器对被跟踪的帧有更精确的把握,以减少对性能的影响。

简单三步构建调试器 (我们最初的设想)

仅仅依靠上文提到的内容,用自制的跟踪函数来构建一个真正的调试器似乎有些不切实际。幸运的是,Python 的标准调试器 pdb 是基于 Bdb 构建的,后者是 Python 标准库中专门用于构建调试器的基类。

基于 Bdb 的简易断点调试器看上去是这样的:

import bdb
import inspect

class Debugger(bdb.Bdb):
  def __init__(self):
      Bdb.__init__(self)
      self.breakpoints = dict()
      self.set_trace()

def set_breakpoint(self, filename, lineno, method):
  self.set_break(filename, lineno)
  try :
      self.breakpoints[(filename, lineno)].add(method)
  except KeyError:
      self.breakpoints[(filename, lineno)] = [method]

def user_line(self, frame):
  if not self.break_here(frame):
      return

  # Get filename and lineno from frame
  (filename, lineno, _, _, _) = inspect.getframeinfo(frame)

  methods = self.breakpoints[(filename, lineno)]
  for method in methods:
      method(frame)

这个调试器类的全部构成是:

  1. 继承 Bdb,定义一个简单的构造函数来初始化基类,并开始跟踪。
  2. 添加 set_breakpoint 方法,它使用 Bdb 来设置断点,并跟踪这些断点。
  3. 重载 Bdb 在当前用户行调用的 user_line 方法,该方法一定被一个断点调用,之后获取该断点的源位置,并调用已注册的断点。

这个简易的 Bdb 调试器效率如何呢?

Rookout 的目标是在生产级性能的使用场景下提供接近普通调试器的使用体验。那么,让我们来看看先前构建出来的简易调试器表现的如何。

为了衡量调试器的整体性能开销,我们使用如下两个简单的函数来进行测试,它们分别在不同的情景下执行了 1600 万次。请注意,在所有情景下断点都不会被执行。

def empty_method():
   pass

def simple_method():
   a = 1
   b = 2
   c = 3
   d = 4
   e = 5
   f = 6
   g = 7
   h = 8
   i = 9
   j = 10

在使用调试器的情况下需要大量的时间才能完成测试。糟糕的结果指明了,这个简陋 Bdb 调试器的性能还远不足以在生产环境中使用。

 title=

对调试器进行优化

降低调试器的额外开销主要有三种方法:

  1. 尽可能的限制局部跟踪:由于每一行代码都可能包含大量事件,局部跟踪比全局跟踪的开销要大得多。
  2. 优化 call 事件并尽快将控制权还给解释器:在 call 事件发生时调试器的主要工作是判断是否需要对该事件进行跟踪。
  3. 优化 line 事件并尽快将控制权还给解释器:在 line 事件发生时调试器的主要工作是判断我们在此处是否需要设置一个断点。

于是我们复刻了 Bdb 项目,精简特征、简化代码,针对使用场景进行优化。这些工作虽然得到了一些效果,但仍无法满足我们的需求。因此我们又继续进行了其它的尝试,将代码优化并迁移至 .pyx 使用 Cython 进行编译,可惜结果(如下图所示)依旧不够理想。最终,我们在深入了解 CPython 源码之后意识到,让跟踪过程快到满足生产需求是不可能的。

 title=

放弃 Bdb 转而尝试字节码操作

熬过先前对标准调试方法进行的试验-失败-再试验循环所带来的失望,我们将目光转向另一种选择:字节码操作。

Python 解释器的工作主要分为两个阶段:

  1. 将 Python 源码编译成 Python 字节码:这种(对人类而言)不可读的格式专为执行的效率而优化,它们通常缓存在我们熟知的 .pyc 文件当中。
  2. 遍历 解释器循环中的字节码: 在这一步中解释器会逐条的执行指令。

我们选择的模式是:使用字节码操作来设置没有全局额外开销的不中断断点。这种方式的实现首先需要在内存中的字节码里找到我们感兴趣的部分,然后在该部分的相关机器指令前插入一个函数调用。如此一来,解释器无需任何额外的工作即可实现我们的不中断断点。

这种方法并不依靠魔法来实现,让我们简要地举个例子。

首先定义一个简单的函数:

def multiply(a, b):
   result = a * b
   return result

inspect 模块(其包含了许多实用的单元)的文档里,我们得知可以通过访问 multiply.func_code.co_code 来获取函数的字节码:

'|\x00\x00|\x01\x00\x14}\x02\x00|\x02\x00S'

使用 Python 标准库中的 dis 模块可以翻译这些不可读的字符串。调用 dis.dis(multiply.func_code.co_code) 之后,我们就可以得到:

  4          0 LOAD_FAST               0 (a)
             3 LOAD_FAST               1 (b)
             6 BINARY_MULTIPLY    
             7 STORE_FAST              2 (result)

  5         10 LOAD_FAST               2 (result)
            13 RETURN_VALUE      

与直截了当的解决方案相比,这种方法让我们更靠近发生在调试器背后的事情。可惜 Python 并没有提供在解释器中修改函数字节码的方法。我们可以对函数对象进行重写,不过那样做的效率满足不了大多数实际的调试场景。最后我们不得不采用一种迂回的方式来使用原生拓展才能完成这一任务。

总结

在构建一个新工具时,总会学到许多事情的工作原理。这种刨根问底的过程能够使你的思路跳出桎梏,从而得到意料之外的解决方案。

在 Rookout 团队中构建不中断断点的这段时间里,我学到了许多有关编译器、调试器、服务器框架、并发模型等等领域的知识。如果你希望更深入的了解字节码操作,谷歌的开源项目 cloud-debug-python 为编辑字节码提供了一些工具。


via: https://opensource.com/article/19/8/debug-python

作者:Liran Haimovitch 选题:lujun9972 译者:caiichenr 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出