---
author: deng
date: "20250103"
category: Note
tags:
  - Python
  - Packaging
  - PyProject
  - Note
---
![cover](resources/should_setup_py_be_deleted.png)
[image resource](https://packaging.python.org/en/latest/guides/modernize-setup-py-project/)

- Abstract
	- 將辛辛苦苦寫出來的python專案打包發布至你想發布的地方！
- Concept
	- 工作分成兩階段
		1. 將原始碼封裝成whl檔，稱為build，常見的build engine有[setuptools](https://setuptools.pypa.io/en/latest/)或是[hatchling](https://pypi.org/project/hatchling/)
		2. 將whl檔發佈（不管是公開的[PyPI](https://pypi.org/)還是私人的[pip server](https://hub.docker.com/r/pypiserver/pypiserver)），可透過[twine](https://github.com/pypa/twine)進行發佈
- Steps
	1. 在Python環境中安裝[setuptools](https://pypi.org/project/setuptools/)及[twine](https://pypi.org/project/twine/)（也可以使用其他build engine）
	2. 依據[官方文件](https://packaging.python.org/en/latest/tutorials/packaging-projects/)改造你的專案結構，並添加LICENSE、README.md與pyproject.toml檔案
	3. 依據需求填寫上述三檔案，pyproject.toml內容可參考Note最後之範例程式碼
	4. 執行`python -m build`指令建立whl檔案，該檔將會生成於dist資料夾中
	5. 執行`twine upload dist/*`將專案上傳至PyPI（需事先申請帳號），或者先上傳至[TestPyPI](https://packaging.python.org/en/latest/guides/using-testpypi/)進行測試（同樣地也需要事先申請帳號哦）
- Note
	- 使用setup.py進行封裝雖然很常見於各種教學文件中（甚至是GPT-4o給的答案），但Python[官方文件](https://packaging.python.org/en/latest/)現在都使用pyproject.toml進行套件封裝，如封面所示（所以讀文件吧，親愛的工程師）
	- 若想進行發布測試可使用官方提供的測試伺服器[TestPyPI](https://test.pypi.org/)（其資料庫會定期清理資料，發布的套件與使用者帳號可能都會被刪除）
	- 用ChatGPT生成LICENSE還蠻方便的呢，即便一些在prompt中沒提到的細節它也會留意到，修改完的文件需要請主管確認範圍，再請法務部門審閱，之後就能加進套件了
	- 可參考[本文](https://semver.org/lang/zh-TW/)瞭解版本管理哲學（雖不一定會使用SemVer，但有些概念可借鏡）
	- 以下為pyproject.toml範例程式碼

```toml
# pyproject.toml
#
# author: deng
# date  : 20250103

[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"

[tool.setuptools.packages.find]
where = ["src"]

[tool.setuptools.package-data]
"PlayYourHeart.data" = ["*.csv"]
# this can package additional data in project

[project]
name = "PlayYourHeart"
version = "0.1.0"
authors = [
  { name="deng", email="gt810034@gmail.com" },
]
maintainers = [
  { name="deng", email="gt810034@gmail.com" },
]
description = "Let's enjoy."
readme = "README.md"
license = {file = "LICENSE"}
dependencies = [
  "numpy~=1.26.4",
  "scipy~=1.14.1",
  ...
]
requires-python = "==3.10.*"
classifiers = [
    "Programming Language :: Python :: 3",
    "Operating System :: Unix"
]

[project.optional-dependencies]
dev = [
  "pytest",
  "flake8",
  "pre-commit"
]
# use "pip install xxx.whl[dev]" to install them

[project.urls]
Gitlab = "..."
```