这里主要记录在日常使用 Python 时,遇到的一些零碎问题。不易写成独立文章,所以统一记录于此。
1. PIP 工具的最推荐使用方式
需要修改服务框架的默认文件上传大小已适配不同的需求
- 问题描述: 知名
Python
开发者Ned Batchelder
在推上发了下面这个截图,意思就是:推荐使用在命令行中使用模块的方式安装依赖包。这时才知道,官方文档中已经很早就改用这种方式了。- 使用
pip
安装了第三方库,但调用时却显示无法导入 - 一般情况下出现上述问题,就是因为:使用的
python
和pip
并不是一套
- 使用
- 解决方法: 那问题俩了,上面两种方式到底有什么不同呢?使用
python -m pip
这样的方式到底有什么好处呢?我们都知道,只要包里面定义了main
函数就可以使用在命令行导入模块的方式执行某些行为(没有什么高深莫测的技巧)。- 比如使用
python -m venv venv
创建虚拟环境目录 - 比如使用
python -m flask run
启动Flask
应用 - 比如使用
python -m http.server
启动一个简单的HTTP
服务器
- 比如使用
- 无论如何都应该使用
python -m pip
的方式安装第三方库,并且每个项目都应该使用自己的虚拟开发环境(比如pyenv
等工具),非必要不要安装包到全局的python
环境中,才是合理的工程实践。- [1] 如果因为某些原因导致使用的
python
和pip
并不是一套,这时候使用pip install
安装的话,就会出现开头遇到的问题,有可能对应的包安装到了全局环境中去了。但是使用python -m pip install
的话,就无所谓是否已经激活了虚拟环境,因为其会确保了想要安装的包会和当前解释器是一个环境。 - [2] 还有一个问题,就是当我们使用
pip/pip3 install
安装的时候,系统是可能会存在多个版本,到底其对应的是哪个版本呢?并没有显示的制定出来,且不好区分,所以python -m pip install
是一个比较省心的选择。
- [1] 如果因为某些原因导致使用的
# 参考链接地址
# 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"
2. 关于 pip 安装的相关报错
收集有关 pip 安装和使用的相关报错和处理方式 - 参考地址
- [1] 创建虚拟环境
- 可以通过
pyenv
来创建对应版本 - 然后通过
virtualenv
命令进行创建
- 可以通过
> pyenv local github
> virtualenv -p $(which python3.6) test3.6
- [2] 关于正是信任关系
- 就是站点的
https
证书过期了 - 可以通过更新整数解决,或者直接加信任
- 如果我们使用自建的一些服务,在配置
SSL
证书之后,使用pip
安装包的话,就是提示一大堆告警信息,很影响输出和显示。
- 就是站点的
# 报错信息提示证书过期
SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired (_ssl.c:1131)'))
# 显示链接服务器失败
Failed to establish a new connection: [Errno 111] Connection refused')': /simple/app/
# 直接加信任
> python -m pip install --upgrade pip --trusted-host app.escapelife.site
# 全局禁用
$ pip install gensim config --global http.sslVerify false
# 安装时忽略
$ pip install --trusted-host pypi.org <package_name>
# 配置文件忽略(pip.conf)
[global]
trusted-host = pypi.python.org pypi.org
# 配置文件忽略(requirements)
--index-url https://xxx:xxx@xxx:xxx/simple --trusted-host xxx
- [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]
# 可以通过对网卡进行限速
# Pypi服务端报错
raise OSError(errno.EMFILE, "inotify instance limit reached")
# 文件末尾添加以下参数
# 表示每个instatnces可监控的最大目录数量,如果监控的文件数目巨大,需要根据情况,适当增加此值的大小
$ sudo vim /etc/sysctl.conf
fs.inotify.max_user_watches = 524288 # 每个实例最多监控目录数
3. Python 单元测试依赖问题
因为 grpc 依赖 protobuf 包,但是指定了版本后并不会强制更新 protobuf 版本!
- 问题描述: 项目自动化执行
Unit
测试的时候,提示如下所示的错误。
- 解决方法: 搜了下,看到 StackOverFlow 上面有人之前提问题,也有对应解决方法。
# 是因为protobuf版本太低导致
➜ pip3 install --upgrade protobuf
4. 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
- 所以,知道上述知识之后,可以通过如下配置临时修改环境,使其能够生成
core dump
文件。
# 临时修改
$ 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*
5. 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上构建支持框架的CPython
➜ env PYTHON_CONFIGURE_OPTS="--enable-framework" pyenv install 3.5.0
# 如何使用 - 即可共享构建CPython
$ env PYTHON_CONFIGURE_OPTS="--enable-shared" pyenv install 3.5.0
6. 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
7. Python 服务上传文件异常
需要修改服务框架的默认文件上传大小已适配不同的需求
- 问题描述: 发现服务上传文档的时候发生了异常,排除了下确实是文档大小超过了
Nginx
的限制了。但是当调整完毕之后,发现还是有同样的问题。
- 解决方法: 后来发现是
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()
8. 苹果上无法访问 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
- 解决方法: 简单来说,新版本的
macOS
上localhost 5000
端口被一个叫AirPlay Receiver
的服务占用了。而Flask
内置服务器默认就运行在5000
端口,所以会造成端口冲突。- 最简单的解决方法是关掉这个服务
- 更改
Flask
开发服务器默认的端口
# 中文
系统设置 > 分享 > 隔空播放接收器 > 取消勾选
# 英文
System Preferences -> Sharing -> AirPlay Receiver -> Close
# 更改Flask开发服务器默认的端口
$ flask run --port 8000
# 使用环境变量修改默认的端口
$ export FLASK_RUN_PORT=8000
9. 找到 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
也会安装包到第三个路径。所以要记住,发行版上自带的 Python
和 pip
都是特制的,你用从官网和 PyPI
上下载的去替换是会出问题。
10. Python 代码编译成 so 问题
Python 代码编译成 so 文件之后,就不用直接调用函数执行某些操作(比如传参、直接启动等)!
- 问题描述: 因为某些问题,导致原本是
pyc
文件的代码需要转成so
文件(部分代码重构成为第三方模块来提供使用),所以之前需要启动服务的命令无法使用。
# 服务启动命令无法执行
> python3 -m app.run_server --port 9000
supervisor: child process was not spawned
- 解决方法: 因为编译(
so
)的原因,导致不能再这样使用了,而是要通过模块的使用方式(需要包一层py
)。
# 启动方式
> python3 -c "import app.run_server as p; p.main()" --port 9000
# app/run_server.py
def main():
pass
if __name__ == '__main__':
main()