Python常见零碎问题汇总


这里主要记录在日常使用 Python 时,遇到的一些零碎问题。不易写成独立文章,所以统一记录于此。

Python常见零碎问题汇总


1. Python 单元测试依赖问题

因为 grpc 依赖 protobuf 包,但是指定了版本后并不会强制更新 protobuf 版本!

  • 问题描述: 项目自动化执行 Unit 测试的时候,提示如下所示的错误。

Python常见零碎问题汇总 - 单元测试依赖问题

  • 解决方法: 搜了下,看到 StackOverFlow 上面有人之前提问题,也有对应解决方法。
# 是因为protobuf版本太低导致
➜ pip3 install --upgrade protobuf

2. Python dump 文件无法生成

Python doesn’t dump core files!

  • 问题描述:收到反馈,说是程序异常之后,提示生成了 core dump 文件,但是整个系统都没有找到对应文件。
# 查了下确实没有
find ./ -name "code.*" -type f
  • 解决方法: 搜了下,看到下面这个两个有用的链接,提及了可能出现的四个原因。首先,我们需要了解 core 文件是什么。某些信号的操作是导致进程终止并生成核心转储文件(core),包含终止时进程内存的映像,主要用于程序崩溃时调试找原因用的。

  • unlimited

    • 使用 ulimit -c 确认系统是否能够生成 core 文件
    • 若为 0,说明禁用了 core 文件生成(根据需要配置大小;以 KB 为单位)
    • 若为 unlimited,说明对生成 core 文件的大小没有限制
  • suid_dumpable

    • 在默认情况下系统不会为 seteuid()/setegid() 进程生成 core dump 文件的
    • 为了能够让这些进程生成 core dump 文件,需要将 suid_dumpable 文件的内容改为 1
  • core_uses_pid

    • 设置 pid 作为扩展名
    • 0: 不添加 pid 作为扩展名;生成的文件名称为 core;新的 core 会覆盖已存在的 core
    • 1: 可以将进程的 pid 作为作为扩展名;生成的 core 文件格式为 core.xxx;其中 xxx 即为 pid
  • core_pattern

    • 设置 core 文件保存位置和格式
# unlimited
$ ulimit -c
unlimited

# suid_dumpable
$ cat /proc/sys/fs/suid_dumpable
2

# core_uses_pid
$ cat /proc/sys/kernel/core_uses_pid
0

# core_pattern
$ cat /proc/sys/kernel/core_pattern
|/usr/share/apport/apport %p %s %c %d %P %E
# 临时修改
$ sudo ulimit -c unlimited
$ echo "1" | sudo tee /proc/sys/fs/suid_dumpable
$ echo "1" | sudo tee /proc/sys/kernel/core_uses_pid
$ echo "/home/vagrant/core-%e-%p-%t" | sudo tee /proc/sys/kernel/core_pattern
# 永久修改
$ echo "ulimit -c unlimited" | sudo /etc/profile
$ echo "kernel.suid_dumpable = 1" | sudo /etc/sysctl.conf
$ echo "kernel.core_uses_pid = 1" | sudo /etc/sysctl.conf
$ echo "kernel.core_pattern = /home/vagrant/core-%e-%p-%t" | sudo /etc/sysctl.conf
$ sudo sysctl -p
# 测试方式
$ set -x
$ sleep 120 &
$ kill -abrt $!; ls core*

3. Python 创建虚拟环境异常

使用 Pyenv 工具创建出来的 Python 版本在执行和安装存在问题!

  • 问题描述: 使用 Pyenv 安装对应 Python 版本,并通过其创建虚拟环境的时候,发现对应的环境执行某些操作存在异常,比如 pip 安装第三方库报错、执行某些 inv 命令提示缺少依赖问题,等等。
# 报错示例一
This program needs access to the screen. Please run with a
Framework build of python, and only when you are logged in
on the main display of your Mac.

# 报错示例二
$ pip install grpcio==1.37
error: legacy-install-failure
× Encountered error while trying to install package.
╰─> grpcio
note: This is an issue with the package mentioned above, not pip.
hint: See above for output from the failure.

# 报错示例三
$ pip install weditor
Collecting weditor
Using cached weditor-0.6.5.tar.gz (2.2 MB)
Preparing metadata (setup.py) ... error
error: subprocess-exited-with-error
× python setup.py egg_info did not run successfully.
│ exit code: 1
╰─> [31 lines of outp
  • 解决方法: 使用了很多方法都没有解决对应问题,最好在该工具的 wiki 里面找到了对应的解决方法。
    • 首先确定是否在对应操作系统中,已经安装了对应依赖库了
    • 然后在创建版本的使用需要加上框架支持参数构建才能可以
    • 最好就是大胆放心的使用和编写代码了(构建带有共享库的 CPython)
# 如何在OS X上构建支持框架的CPythonenv PYTHON_CONFIGURE_OPTS="--enable-framework" pyenv install 3.5.0

# 如何使用 - 即可共享构建CPython
$ env PYTHON_CONFIGURE_OPTS="--enable-shared" pyenv install 3.5.0

4. Python 较安全的文件传输

虽然已经基本不怎么提交代码了,但是简单好用!

  • 问题描述: 我们因为某些原因,需要共享一些文件或者目录给其他人员临时使用,虽然 Linux 自带了 Python2/3 可以临时启动一个简单的服务,完成上述需求。但是,其并不太安全,没有任何限制。
# Python2
$ python -m SimpleHTTPServer 80

# Python3
$ python -m http.server 9000 --bind 127.0.0.1 --directory /tmp/
  • 解决方法: Updog 是一个 Python 自带的 HTTPServer 的一个替代选择,其允许通过 HTTP/S 上传和下载,还可以支持简单的用户认证。
Argument Description
-d DIRECTORY, –directory DIRECTORY Root directory [Default=.]
-p PORT, –port PORT Port to serve [Default=9090]
–password PASSWORD Use a password to access the page. (No username)
–ssl Enable transport encryption via SSL
–version Show version
-h, –help Show help
$ pip3 install updog
$ updog -d /data/app/core -p 9001 --password 12345678

Python常见零碎问题汇总 - 较安全的文件传输


5. Python 服务上传文件异常

需要修改服务框架的默认文件上传大小已适配不同的需求

  • 问题描述: 发现服务上传文档的时候发生了异常,排除了下确实是文档大小超过了 Nginx 的限制了。但是当调整完毕之后,发现还是有同样的问题。

Python常见零碎问题汇总 - 服务上传文件异常

  • 解决方法: 后来发现是 Nginx 之后的后续服务 block 掉了,需要排除全链路所有涉及的服务,进行统一的调整来解决这个问题。
# 调整Nginx上传配置(要都一致才可以)
http {
    client_max_body_size    100M;

    server {
        ...
    }
}
# 调整Gunicorn配置(如果有的话)
app_params = {
    "max_buffer_size": 200 * 1024 * 1024, # 100MB
}

# 调整Tronado配置(要都一致才可以)
class Application(tornado.web.Application):
    ...

ws_app = Application()
server = tornado.httpserver.HTTPServer(ws_app, max_buffer_size=100*1024*1024) # 100M
server.bind(options.port, reuse_port=True)
server.start()

6. PIP 工具的最推荐使用方式

需要修改服务框架的默认文件上传大小已适配不同的需求

  • 问题描述: 知名 Python 开发者 Ned Batchelder 在推上发了下面这个截图,意思就是:推荐使用在命令行中使用模块的方式安装依赖包。这时才知道,官方文档中已经很早就改用这种方式了。
    • 使用 pip 安装了第三方库,但调用时却显示无法导入
    • 一般情况下出现上述问题,就是因为:使用的 pythonpip 并不是一套

Python常见零碎问题汇总 - 单元测试依赖问题

  • 解决方法: 那问题俩了,上面两种方式到底有什么不同呢?使用 python -m pip 这样的方式到底有什么好处呢?我们都知道,只要包里面定义了 main 函数就可以使用在命令行导入模块的方式执行某些行为(没有什么高深莫测的技巧)。
    • 比如使用 python -m venv venv 创建虚拟环境目录
    • 比如使用 python -m flask run 启动 Flask 应用
    • 比如使用 python -m http.server 启动一个简单的 HTTP 服务器
  • 无论如何都应该使用 python -m pip 的方式安装第三方库,并且每个项目都应该使用自己的虚拟开发环境(比如 pyenv 等工具),非必要不要安装包到全局的 python 环境中,才是合理的工程实践。
    • [1] 如果因为某些原因导致使用的 pythonpip 并不是一套,这时候使用 pip install 安装的话,就会出现开头遇到的问题,有可能对应的包安装到了全局环境中去了。但是使用 python -m pip install 的话,就无所谓是否已经激活了虚拟环境,因为其会确保了想要安装的包会和当前解释器是一个环境。
    • [2] 还有一个问题,就是当我们使用 pip/pip3 install 安装的时候,系统是可能会存在多个版本,到底其对应的是哪个版本呢?并没有显示的制定出来,且不好区分,所以 python -m pip install 是一个比较省心的选择。
# 参考链接地址
# https://www.dongwm.com/post/you-should-use-python-m-pip/

# 发现存在多个版本
$ pip<tab><tab>
pip        pip2        pip2.7        pip2.7.14
pip3       pip3.7      pip3.9.1      pip3.9.3

# 创建别名
$ vim ~/.zshrc
alias pip="python -m pip"

7. 苹果上无法访问 Flask 程序

参考李辉老师的 《如果你在 macOS 上无法访问 Flask 程序》

  • 问题描述: 如果你用 macOS 开发 Flask 时无法正常访问程序,或是使用 ngrok 等内网穿透工具时映射的公网地址无法访问,大概率是因为 macOS 新版本(Monterey)的变动导致。
# 错误信息 - 访问
Access to 127.0.0.1 was denied.
You don not have authorization to view this page.
HTTP ERROR 403

# 错误信息 - 执行
OSError: [Errno 48] Address already in use
  • 解决方法: 简单来说,新版本的 macOSlocalhost 5000 端口被一个叫 AirPlay Receiver 的服务占用了。而 Flask 内置服务器默认就运行在 5000 端口,所以会造成端口冲突。
    • 最简单的解决方法是关掉这个服务
    • 更改 Flask 开发服务器默认的端口
# 中文
系统设置 > 分享 > 隔空播放接收器 > 取消勾选

# 英文
System Preferences -> Sharing -> AirPlay Receiver -> Close
# 更改Flask开发服务器默认的端口
$ flask run --port 8000

# 使用环境变量修改默认的端口
$ export FLASK_RUN_PORT=8000

8. 找到 Python 的安装路径

参考 Frost 老师的 《系统上捉摸不定的 Python 路径》

Python 中有一个叫做 Install Schema 的概念,可以简单理解为 安装蓝图 ,意思就是提供了一个路径的集合,告诉 Python 包的安装器,什么文件应该放到哪个路径下。

  • 获取 Install Scheme 的函数
# 输入
import sysconfig
install_paths = sysconfig.get_paths()
print(install_paths['purelib'])

# 输出
/usr/lib/python3.8/site-packages
  • 正常情况下,Python 库是放到
# Posix
$path_prefix/lib/pythonX.Y/site-packages

# Windows
$path_prefix/Lib/site-packages

# path_prefix
# 那么 /usr 是路径前缀
$ which python3.8
/usr/bin/python3.8
  • 获取 Python3.8 的安装文件
# 列出所有的mysql安装包
$ dpkg --list | grep python3.8

# Ubuntu查看系统包的安装文件
$ dpkg -L python3.8

基本所有的 Linux 发行版都会自己打包 Python 库,他们不信任 PyPI,所有用到的 Python 库都拿源码下来,自己打包。那问题来了,那么多 Python 包,那么多 Python 版本,怎么打,怎么安装?

他们发现了一点:纯 Python 库是比较兼容的,不需要每个 Python 版本都打一个包,只需要整个 Python3 打一个包就可以了,所以有 python3-pip, python3-requests 这种命名方式的包。安装的时候,也把它们放在一个地方,所有 Python3 版本都可以用。

  • Debian/Ubuntu 事实并非如此
    • /usr/lib/python3/dist-packages
      • apt 安装的纯 Python
    • /usr/lib/python3.9/dist-packages/
      • apt 安装的带扩展的 Python
    • /usr/local/lib/python3.9/dist-packages/
      • pip3 安装的 Python

这属于自己的设计,那么就需要改安装库和导入库的逻辑。Debian 维护了一系列的补丁来干这件事,改完之后,sys.path 会包含上面三个路径,site-packages 的路径从中去除了,而 pip3 也会安装包到第三个路径。所以要记住,发行版上自带的 Pythonpip 都是特制的,你用从官网和 PyPI 上下载的去替换是会出问题。


9. 关于 pip 安装的相关报错

收集有关 pip 安装和使用的相关报错和处理方式 - 参考地址

  • [1] 创建虚拟环境
    • 可以通过 pyenv 来创建对应版本
    • 然后通过 virtualenv 命令进行创建
> pyenv local github
> virtualenv -p $(which python3.6) test3.6
  • [2] 关于正是信任关系
    • 就是站点的 https 证书过期了
    • 可以通过更新整数解决,或者直接加信任
# 报错信息提示证书过期
SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired (_ssl.c:1131)'))

# 直接加信任
> python -m pip install --upgrade pip --trusted-host app.escapelife.site
  • [3] 循环搜索依赖包
    • 由于依赖库版本的不一致
# --use-deprecated=legacy-resolver
$ pip3 install -r ./requirements.txt --upgrade --use-deprecated=legacy-resolver
  • [4] 本地安装包版本太老了
    • 删除提示中冲突的 ~ 开头的文件目录即可
# 报错信息
# 用distutils配置文件配置安装方案已被废弃
WARNING: Ignoring invalid distribution -ip (/usr/local/lib/python3.9/site-packages)

# 找到带~波浪线的临时文件夹,然后删除
$ cd /usr/local/lib/python3.9/site-packages && rm \~ip \~ip-21.0.1.dist-info
  • [5] 服务器带宽速度限制
    • 私有部署内部镜像源,如果突然集中大量使用的话,可能因为带宽的问题导致使用端异常
# 客户端会出现很多超时的重试操作
WARNING: ... connection broken by 'ReadTimeoutError("HTTPSConnectionPool(host='py.escapelife.site', port=1000): Read timed out. (read timeout=15)")': /simple/app/

# 可以看下服务器对应的流量是否超过了带宽
Total DISK READ :      213.41 M/s | Total DISK WRITE :      113.62 M/s
Actual DISK READ:      100.89 M/s | Actual DISK WRITE:      213.99 M/s
  TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO>    COMMAND
  977 be/4 root       82.89 K/s    0.00 B/s  0.00 % 10.78 % [txg_sync]

# 可以通过对网卡进行限速

```bash
# Pypi服务端报错
raise OSError(errno.EMFILE, "inotify instance limit reached")

# 文件末尾添加以下参数
# 表示每个instatnces可监控的最大目录数量,如果监控的文件数目巨大,需要根据情况,适当增加此值的大小
$ sudo vim /etc/sysctl.conf
fs.inotify.max_user_watches = 524288  # 每个实例最多监控目录数

文章作者: Escape
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Escape !
  目录