# setuptools
[](https://hackmd.io/@RogelioKG/setuptools)
## References
+ 🔗 [**Documentation : setuptools**](https://setuptools.pypa.io/en/latest/userguide/)
+ 🔗 [**HackMD : 由淺入深 Python Packaging**](https://hackmd.io/@celineyeh/SyJSK8AXB#Package-distribution)
+ 🔗 [**HackMD : 如何將寫好的 package 上傳到 pypi 供人安裝使用**](https://hackmd.io/@seanbbear/HJaAYctkw)
+ 🔗 [**Medium: 開發 Python 套件並且上傳至 Pypi by setuptool and twine**](https://medium.com/edward-hong-%E6%8A%80%E8%A1%93%E7%AD%86%E8%A8%98/%E9%96%8B%E7%99%BCpython-%E5%A5%97%E4%BB%B6%E4%B8%A6%E4%B8%94%E4%B8%8A%E5%82%B3%E8%87%B3pypi-by-setuptool-and-twine-4f178e752640)
+ 🔗 [**Cnblogs : Python 包构建教程**](https://www.cnblogs.com/cposture/p/9029023.html)
+ 🔗 [**CSDN : pip 安裝常見坑**](https://blog.csdn.net/qq_41068877/article/details/127457367)
+ 🔗 [**Structuring Your Project**](https://docs.python-guide.org/writing/structure/)
+ 🔗 [**Choose an open source license**](https://choosealicense.com/)
+ 🔗 [**Dependency Resolution Made Simple**](https://borretti.me/article/dependency-resolution-made-simple)
+ 🔗 [**pip : Dependency Resolution**](https://pip.pypa.io/en/latest/topics/dependency-resolution/)
+ 🔗 [**【Python的解憂錦囊】setuptools.errors.PackageDiscoveryError**](https://vocus.cc/article/64f1c81bfd897800012a054b)
+ 🔗 [**【Python的解憂錦囊】python -m build 打包時也能包含被引用的目錄**](https://vocus.cc/article/662998defd89780001968711)
+ 🎞️ [**ArjanCodes: How to Build a Complete Python Package Step-by-Step**](https://youtu.be/5KEObONUkik)
<!-- link -->
[binary-distribution-format]: https://packaging.python.org/en/latest/specifications/binary-distribution-format/
[toml-config]: https://packaging.pythonlang.cn/en/latest/guides/writing-pyproject-toml/
[caching-and-troubleshooting]: https://setuptools.pypa.io/en/latest/userguide/miscellaneous.html#caching-and-troubleshooting
[dependency-resolution]: https://pip.pypa.io/en/stable/topics/dependency-resolution/
## Preface
| 📘 <span class="note">NOTE</span> |
| :--- |
| 你也遇到奇怪的安裝錯誤了嗎?<br />看看 [Common Error](#Common-Error) 能不能解決你遇到的問題! |
| 📘 <span class="note">NOTE</span> |
| :--- |
| 你也正在學 JavaScript npm / pnpm 嗎?<br />搭配這篇[⚡ JS 套件管理大師](https://hackmd.io/@RogelioKG/package_management_js) 一起服用,效果更好! |
|📗 <span class="tip">TIP</span>|
|:---|
|使用 `pip install -e .` (editable) 就可以將自己目前開發的套件,<br />暫時安裝到本地 venv,這樣就可以一邊開發一邊測試!<br />(記得開發完要重新再做一次此操作,才能看到改動的結果)|
## Nouns
### egg
> Python 的一種 <mark>二進制發佈格式 ([binary distribution format][binary-distribution-format])</mark>,現已被 wheel 取代。
### wheel
> Python 的一種 <mark>二進制發佈格式 ([binary distribution format][binary-distribution-format])</mark>,旨在加速套件的安裝過程。\
> 相較於源代碼發佈格式 (如 `.tar.gz`),wheel <ins>不需在安裝時進行編譯</ins>,因而可以節省安裝時間。\
> wheel 檔案的副檔名為 `.whl` (本質上是 `zip` 格式的壓縮檔),是目前 Python 生態系中推薦的發佈格式。
### PyPI
> Python 的<mark>第三方套件集中地</mark>。全稱 Python Package Index。
### TestPyPI
> <mark>PyPI 的測試區</mark>,與 PyPI 是分開的,不用擔心會相互影響。
### build frontend
> 負責<mark>調用構建過程的工具</mark>,通過與 build backend 交互來實際執行構建任務。\
> 前端工具本身不處理具體的構建邏輯,它只是啟動後端的構建。\
> 例如 : `build` / `pip` / `tox`。
### build backend
> 負責<mark>實際執行構建邏輯的工具</mark>,它會處理如何將源代碼轉換成可以安裝和發佈的格式 (`.whl` 和 `.tar.gz`)。\
> 例如 : `setuptools` / `flit` / `poetry` / `hatchling` / `meson-python`。
## Tools
### `pip`
+ <mark>package management 工具</mark>
+ <mark>build frontend 工具</mark>
+ 用於從 PyPI 上下載、安裝套件
### `build`
+ <mark>第三方套件</mark>
+ <mark>build frontend 工具</mark>
+ 支持從 `pyproject.toml` 配置構建
### `distutils`
+ <mark>標準庫套件</mark>
+ 用於構建和發佈 Python 套件
+ 現已被 setuptools 取代
### `setuptools`
+ <mark>第三方套件</mark>
+ <mark>build backend 工具</mark>
+ 用於構建和發佈 Python 套件
+ 擴展了標準的 distutils 模組 (打了個 monkey patch)
+ 提供了更多強大的功能,如支持依賴關係的管理、自動化版本控制和插件系統
### `wheel`
+ <mark>第三方套件</mark>
+ 用於產生 Python 套件的二進制發佈格式 ([binary distribution format][binary-distribution-format]) `.whl`
### `twine`
+ <mark>第三方套件</mark>
+ 用於發佈 Python 套件
+ 支持將多個檔案一次性上傳到 PyPI / TestPyPI
+ 發佈 Python 套件的推薦工具
## Project Directory
```
sound_project/
├── pyproject.toml # configuration (or setup.py or setup.cfg)
| README.md
| LICENSE
└── soundcraft/ # top-level package
├── __init__.py
└── ...
└── tests/ # unit testing
├── __init__.py
└── ...
```
```
soundcraft/ # top-level package
├── __init__.py
├── formats/ # subpackage for file format conversions
│ ├── __init__.py
│ ├── wavread.py
│ ├── wavwrite.py
│ ├── aiffread.py
│ ├── aiffwrite.py
│ ├── auread.py
│ └── auwrite.py
├── effects/ # subpackage for sound effects
│ ├── __init__.py
│ ├── echo.py
│ ├── surround.py
│ └── reverse.py
└── filters/ # subpackage for filters
├── __init__.py
├── equalizer.py
├── vocoder.py
└── karaoke.py
```
## Configure
### 🚨 caution
+ **🚨 <span class="caution">CAUTION</span> : <u>metadata</u>**
+ 這些 config 是用來設定 distribution 的 metadata
+ **🚨 <span class="caution">CAUTION</span> : <u>少用動態 config</u>**
+ 盡量使用 `pyproject.toml` 和 `setup.cfg` 這類的靜態 config,<br>少用 `setup.py` 這類的動態 config (除非必要)
+ **🚨 <span class="caution">CAUTION</span> : <u>套件</u>**
+ 目錄底下一定要有 `__init__.py` 才會被視為套件
### `setup.py`
+ `setup()`
```py
# In setup.py
from setuptools import find_packages, setup
with open("README.md", "r", encoding="utf-8") as f:
long_description = f.read()
setup(
# 套件名稱
# 當你安裝這個套件時,它將會使用這個名稱
name="pdfize",
# 套件版本
# 隨著套件的更新,版本會改變
version="0.3.3",
# 套件簡短描述
description="a simple tools for converting pdf to image.",
# 構建成發佈套件後應該呈現的目錄架構
# 詳見 packages 選項
packages=[...]
# 發佈套件目錄架構映射
# 詳見 package_dir 選項
package_dir={"...": "..."},
# 套件詳細描述
# 通常是從一個 README 檔案中讀取內容
long_description=long_description,
# 詳細描述格式
long_description_content_type="text/markdown",
# 參考網址
# 通常是指向套件的源代碼或官方網站
url="https://github.com/RogelioKG/PDFize",
# 套件作者
author="RogelioKG",
# 套件作者信箱
author_email="...@gmail.com",
# 套件維護者
maintainer="WilliamHuang",
# 套件維護者信箱
maintainer_email="...@gmail.com",
# 授權條款
license="MIT",
# 分類項目
# 方便 PyPI 索引
classifiers=[
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3.11",
"Operating System :: OS Independent",
],
# 套件運行所需的依賴套件
install_requires=[
"altgraph>=0.17.4",
"click>=8.1.7",
"colorama>=0.4.6",
"pillow>=10.3.0",
"PyMuPDF>=1.24.9",
"PyMuPDFb>=1.24.9",
"pywin32-ctypes>=0.2.2",
"tqdm>=4.66.4",
],
# 套件額外的依賴套件
extras_require={
"dev": ["twine>=4.0.2"], # pip install pdfize[dev]
},
# 套件所需的 Python 版本
python_requires=">=3.11",
)
```
+ `packages=`
構建成 distribution 後應該呈現的目錄架構
> [Project Directory](#project-directory) 中,呈現的是「project 的目錄架構」\
> 但這裡指定的是「構建成發佈套件後應該呈現的目錄架構」\
> 假如你希望發佈套件架構應該長這樣:
> ```
> soundcraft/
> ├── __init__.py
> ├── formats/
> │ ├── __init__.py
> │ ├── ...
> ├── effects/
> │ ├── __init__.py
> │ ├── ...
> └── filters/
> ├── __init__.py
> ├── ...
> ```
> 就應該給定
> ```py
> packages=[
> "soundcraft",
> "soundcraft/formats",
> "soundcraft/effects",
> "soundcraft/filters",
> ]
> ```
> 此時若沒有給定 package_dir 選項,它就會直接映射。\
> 也就是說:
> + 發佈套件中的 soundcraft 目錄\
對應的是 project 中的 soundcraft 目錄
> + 發佈套件中的 soundcraft/formats 目錄\
對應的是 project 中的 soundcraft/formats 目錄
> + 發佈套件中的 soundcraft/effects 目錄\
對應的是 project 中的 soundcraft/effects 目錄
> + 發佈套件中的 soundcraft/filters 目錄\
對應的是 project 中的 soundcraft/filters 目錄
+ `package_dir=`
> 假如你希望發佈套件架構應該長這樣:
> ```
> soundcraft/
> ├── __init__.py
> ├── encodings/
> │ ├── __init__.py
> │ ├── ...
> ├── modifiers/
> │ ├── __init__.py
> │ ├── ...
> └── sifters/
> ├── __init__.py
> ├── ...
> ```
> 你可以指定:
> ```py
> packages=[
> "soundcraft",
> "soundcraft/encodings",
> "soundcraft/modifiers",
> "soundcraft/sifters",
> ]
> ```
> 但你的 project 套件目錄架構長得像 [Project Directory](#project-directory),\
> 此時你會希望它們有個映射關係可以對應,\
> 這時候你就可以指定 package_dir 選項。\
> 像這樣:
> ```py
> package_dir={
> "soundcraft/encodings": "soundcraft/formats",
> "soundcraft/modifiers": "soundcraft/effects",
> "soundcraft/sifters": "soundcraft/filters",
> }
> ```
> 也就是說:
> + 發佈套件中的 soundcraft 目錄\
對應的是 project 中的 soundcraft 目錄
> + 發佈套件中的 soundcraft/encodings 目錄\
對應的是 project 中的 soundcraft/formats 目錄
> + 發佈套件中的 soundcraft/modifiers 目錄\
對應的是 project 中的 soundcraft/effects 目錄
> + 發佈套件中的 soundcraft/sifters 目錄\
對應的是 project 中的 soundcraft/filters 目錄
+ `find_packages()`
在 project 中自動搜索套件
> 套件 (package) 和子套件 (subpackage) 間以 `.` 分隔,可使用 `*` 作為 wildcard。\
> 比如 [Project Directory](#project-directory) 中,\
> 若要忽略 `soundcraft/effects/`,要給定 `exclude=["soundcraft.effects"]`。\
> 若要忽略 `tests/` (單元測試),要給定 `exclude=["tests", "tests.*"]`。
+ `where=` : 從哪個目錄搜索套件 (預設為 `'.'`,即與 `setup.py` 同層目錄)
+ `include=` : 包含哪些套件 (預設為 `('*',)`,即包含所有套件)
+ `exclude=` : 排除哪些套件 (預設為 `()`,即不排除任何套件)
### [`pyproject.toml`][toml-config]
| ☢️ <span class="warning">WARNING</span> |
| :--- |
| 超級警告!!!如果你使用 setuptools 作為 build backend,<br>這裡搜索套件的匹配字串和 `find_packages()` 有小差異。|
| <mark>最後使用 `*` 時,前面不要加 `.`</mark> |
| ☢️ <span class="warning">WARNING</span> |
| :--- |
| 當你在套件中包含 `py.typed` 時,這表示該套件的 type hints 可以被靜態類型檢查工具 (mypy) 正確地識別和使用。 |
| 如果套件沒有包含此檔案,而使用者嘗試進行 mypy 檢查,就會出現以下錯誤:<br>`error: Skipping analyzing "...": module is installed, but missing library stubs or py.typed marker [import-untyped]` |
| 然後記得 == 要先在 virtual environment 下載 mypy (是誰那麼蠢會忘記,是的就是我!) |
```toml
[build-system]
requires = ["setuptools>=61.0.0", "wheel"]
build-backend = "setuptools.build_meta" # 使用 setuptools 作為 build backend
[tool.setuptools.packages.find]
include = ["pdfize*"]
exclude = ["pdfize.data*", "tests*"] # 注意不是 ["pdfize.data.*", "tests.*"]
[tool.setuptools.package-data]
pdfize = ["py.typed"] # 新版 setuptools 會自動包含 py.typed 或 .pyi
[project]
name = "pdfize"
version = "1.0.6"
description = "a simple tools for converting pdf to image."
authors = [{ name = "RogelioKG", email = "...@gmail.com" }]
license = { text = "MIT" }
requires-python = ">=3.11"
dependencies = [
"altgraph>=0.17.4",
"click>=8.1.7",
"colorama>=0.4.6",
"pillow>=10.3.0",
"PyMuPDF>=1.24.9",
"PyMuPDFb>=1.24.9",
"pywin32-ctypes>=0.2.2",
"tqdm>=4.66.4",
]
readme = { file = "README.md", content-type = "text/markdown" }
classifiers = [
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3.11",
"Operating System :: OS Independent",
]
[project.urls]
Repository = "https://github.com/RogelioKG/PDFize"
Changelog = "https://github.com/RogelioKG/PDFize/blob/main/CHANGELOG.md"
[project.optional-dependencies]
dev = ["build>=1.2.1", "twine>=5.1.1"]
```
### `setup.cfg`
...
### `MANIFEST.in`
包含非 Python 相關目錄或檔案
| Command | Description |
|---------|-------------|
| `include pat1 pat2 ...` | Add all files matching any of the listed patterns<br>(Files must be given as paths relative to the root of the project) |
| `exclude pat1 pat2 ...` | Remove all files matching any of the listed patterns<br>(Files must be given as paths relative to the root of the project) |
| `recursive-include dir-pattern pat1 pat2 ...` | Add all files under directories matching dir-pattern that match any of the listed patterns |
| `recursive-exclude dir-pattern pat1 pat2 ...` | Remove all files under directories matching dir-pattern that match any of the listed patterns |
| `global-include pat1 pat2 ...` | Add all files anywhere in the source tree matching any of the listed patterns |
| `global-exclude pat1 pat2 ...` | Remove all files anywhere in the source tree matching any of the listed patterns |
| `graft dir-pattern` | Add all files under directories matching dir-pattern |
| `prune dir-pattern` | Remove all files under directories matching dir-pattern |
## Build
### 🚨 caution
+ **🚨 <span class="caution">CAUTION</span> : <u>快取行為</u>**
+ 重新將套件構建為 distribution 時,構建工具可能會有一些[快取行為][caching-and-troubleshooting]。如果發現構建結果有問題,先刪除 `dist/` 和 `build/` 和 `*.egg-info/` 目錄,再重新構建一次。
### `setuptools`
+ 安裝套件到環境中
```bash
py setup.py install
```
+ 將套件構建為 source distribution
> 產生 dist/ 和 *.egg-info/
```bash
py setup.py sdist
```
+ 將套件構建為 binary distribution (egg 格式)
```bash
py setup.py bdist_egg
```
+ 將套件構建為 binary distribution (wheel 格式)
> 產生 build/ 和 dist/ 和 *.egg-info/
```bash
py setup.py bdist_wheel
```
### `build`
+ 將套件構建為 distribution
> 產生 dist/ 和 *.egg-info/
```bash
py -m build
```
## Distribute
### 🚨 caution
+ **🚨 <span class="caution">CAUTION</span> : <u>PyPI</u>**
+ 發佈到 TestPyPI 或 PyPI 需要 API key
+ **🚨 <span class="caution">CAUTION</span> : <u>distribution</u>**
+ 每次發佈都要更新 version
+ 不可重複發佈、刪除已發佈過的 distribution (覆水難收)
+ 假設某 project 叫 pdfize,它從 0.1.1 版推進到 0.3.3 版後,就被刪除了,而某天有另一團隊想開啟新 project ,名稱也剛好叫做 pdfize (可能與前者毫無相關),那此團隊就不能上傳 0.1.1 版,因為這名稱的這版本已被使用過了。
+ <mark>確保已經下載過某個套件的使用者,未來不會錯誤地獲取一個相同名稱、相同版本,但內容卻不同的 distribution</mark>
### `twine`
+ 確認
```bash
twine check dist/*
```
+ 發佈 (PyPI)
```bash
twine upload dist/*
```
+ 發佈 (TestPyPI)
```bash
twine upload -r testpypi dist/*
```
+ 發佈 (跳過已發佈過的舊版)
```bash
twine upload --skip-existing -r testpypi dist/*
```
## Download
### 🚨 caution
+ **🚨 <span class="caution">CAUTION</span> : <u>兩種安裝方式</u>**
+ 當我們在 `pip install` 時,安裝的方式有兩種方式
+ <mark>build from wheel</mark>
+ 如同前面所述,`.whl` 檔基本上等同 `.zip` 檔
+ 這個壓縮檔包含整個套件的 `.py` 檔與已編譯的 DLL `.pyd` 檔,這樣 download client 就不需要再進行編譯(非常省時間)
+ `.pyd` 檔
+ 由 C/C++ 實作的源代碼,使用 C/C++ compiler 編譯而成的 DLL,供 Python 程式呼叫
+ 只在 Windows 能見此副檔名,Linux 的直接叫 `.so` 了
+ 我們可以推得 `.whl` 檔是依賴於平台的,這就是為什麼你會看到 `.whl` 檔名一定會跟著平台前綴
+ <mark>build from source</mark>
+ 如果套件不提供 `.whl` 的安裝方式,pip 會退而求其次,使用套件的源代碼(即發佈時包含的 `.tar.gz` 壓縮檔)進行安裝。
+ 假如套件包含 C extension,會要求 download client 要有 C/C++ compiler,甚至 C extension 開發函式庫,如 `python3-dev`、`libffi-dev`、`openssl-dev` ,這裡通常會是安裝失敗的隱形爆炸點
+ **🚨 <span class="caution">CAUTION</span> : <u>相依性 dependency</u>**
+ 安裝一個套件時,會自動安裝它的依賴套件
+ 移除一個套件時,其依賴套件也會被移除,除非它被其他套件依賴
+ <mark>`pip` 沒有自動移除依賴套件的功能,`poetry` 則有</mark>
+ **🚨 <span class="caution">CAUTION</span> : <u>依賴解析 [dependency resolution][dependency-resolution]</u>**
+ 套件依賴關係是一種有向無環圖 (DAG)
+ 每個套件對其依賴套件的版本有要求,不同版本之間對依賴套件的版本的要求可能不同
+ `pip` 在安裝或更新套件時,會解析整個依賴圖,確保所有版本相容,<mark>同個套件不會有兩個版本</mark>
+ Node.js 的 `npm` 比較特別,仰賴它自身 [Node.js Module Resolution Algorithm](https://hackmd.io/@RogelioKG/package_management_js#Nodejs-Module-Resolution-Algorithm) 的特性,它可以做到同個套件有多個版本,詳見[此處](https://hackmd.io/@RogelioKG/package_management_js#npm-v3)
+ **🚨 <span class="caution">CAUTION</span> : <u>依賴衝突 dependency conflicts</u>**
+ 當多個套件對同一依賴有不同版本要求且無交集,會導致衝突
+ **🚨 <span class="caution">CAUTION</span> : <u>安裝或升級會影響其他套件</u>**
+ `pip` 在安裝或升級一個套件的時候,是有可能影響到其他套件的,這不僅限於它的直接相依套件
+ 範例:比如原先有個套件 B `==1.0`,它要求套件 A `>=1.5,<2.0`,現在它相依於套件 A `==1.5`,今天新安裝了一個套件 C,它要求套件 A `>=2.0`,因此 pip 把套件 A 重裝為 `2.0`,這個套件 B 也就需要重裝。
+ **🚨 <span class="caution">CAUTION</span> : <u>安裝差異</u>**
+ 一次安裝:一次解析所有套件依賴,避免衝突
+ 分批安裝:可能會因新套件需求,導致已安裝套件的版本變動
### `pip`
+ `install`
+ options
+ `-e` | `--editable` : 可編輯模式,套件安裝為一個軟連結,而不是將其複製到 site-packages
+ `-i` | `--index-url` : 從指定 Python package index 下載和安裝套件\
(預設 : https://pypi.org/simple/) (連結不要點,你的記憶體會原地往生)
+ `-r` | `--requirement` : requirements.txt
+ `-t` | `--target` : 下載到某個路徑
+ `-U` | `--upgrade` : 將函式庫升級到最新版
+ `--extra-index-url` : --index-url 的備案選項
+ `--force-reinstall` : 強制重新安裝\
(套件下載到一半出錯,但目錄已創建,pip 會誤認為已安裝)
+ `--no-cache-dir` : 強制從網路下載套件,而非從本地 cache 目錄抓取
+ `--pre` : 安裝預發佈套件 (比如 alpha / beta 版本)
+ `--no-deps` : 不進行依賴解析 ([dependency resolution][dependency-resolution])
+ `--no-binary` : 強制使用 build from source 方式安裝
+ arguments
+ `name[extras_require]`
+ ##### `cache`
| 📘 <span class="note">NOTE</span> |
| :--- |
| pip 在安裝套件時,若之前有下載過,不會從網路重新下載,而是使用本地 cache 來安裝,加快速度 |
| Windows : `C:\Users\username\AppData\Local\pip\Cache` |
| Linux : `~/.cache/pip` |
+ subcommands
+ `dir` : 本地 cache 目錄位置
+ `purge` : 完全清除本地 cache
## Common Error
### ☢️ `ERROR: Could not find a version that satisfies the requirement ...`
翻譯蒟蒻 : 找不到對應的版本
+ Scenario 1. 下載某個指定版本的套件
> Solution :\
> 可能是你 Python 版本太高,\
> 你可以去 PyPI 找一下這套件這版本的 python_requires,\
> 可能考慮 Python 要降版,或者套件要升版。
+ Scenario 2. 把自己寫的套件上傳到 TestPyPI 然後想載下來測試 (結果和你說找不到依賴套件的版本)
> Solution :\
> 簡單來說,\
> 這是因為你套件的依賴套件沒有在 TestPyPI 發佈,所以 pip 找不到套件。\
> 解決方法也很簡單,就再給 pip 一個備案 URL 就好\
> (由 `--extra-index-url` 指定,叫它找不到去 PyPI 找)。
```bash
pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ package-name
```
+ Scenario 3. 把自己寫的套件上傳到 PyPI 然後想載下來使用 (結果和你說找不到你寫的套件的版本)
> Solution :\
> 剛上傳,要等一下,再試一次就可以了。
### ☢️ `ERROR: THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS FILE ...`
翻譯蒟蒻 : 下載後的檔案 hash 值與 PyPI 提供的 hash 值不一致
> Solution :\
> pip 會檢查 hash 值,確保下載檔案的完整性與安全性。\
> 發生這種錯誤表示下載過程中檔案可能遭到損毀或被惡意篡改。\
> 大部分情況是網路不好,所以下載檔案損毀了。
### ☢️ `ERROR: To modify pip, please run the following command: python -m pip install ...`
翻譯蒟蒻 : 你現在要下載的這個套件,它的 dependency 之一就是 pip,而且還要求比你現在還高版的 pip
> Solution :\
> 你可能原本使用 `pip install ...`,\
> 但使用 `python -m pip install ...` 才有辦法自動更新 pip。
### ☢️ `WARNING: Retrying (...) after connection broken by 'ReadTimeoutError(...)'`
翻譯蒟蒻 : 你斷網了
> Solution :\
> 請連上網路。