使用 Python 开发 CLI 工具并打包发布到 PyPI
setup.py
参考:
TODO:
poetry
参考:
poetry
Windows 10
Windows (Powershell)
(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | python -
PS: 其实现在
PowerShell
也支持curl
, 其实好像就是WebRequest
实现的By default, Poetry is installed into a platform and user-specific directory:
~/Library/Application Support/pypoetry
on MacOS.~/.local/share/pypoetry
on Linux/Unix.%APPDATA%\pypoetry
on Windows.If you wish to change this, you may define the $POETRY_HOME
environment variable:
这里我不想安装在默认路径
Windows (Powershell)
$env:APPDATA
$env:POETRY_HOME=“D:\Program Files\pypoetry”
现在可以安装了
(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | python -
注意最后有个
-
安装失败:
参考: - Poetry教程一(Poetry安装与卸载)_成都 - 阿木木的博客-CSDN博客_poetry安装
🦄 (Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | python -
Retrieving Poetry metadata
# Welcome to Poetry!
This will download and install the latest version of Poetry,
a dependency and package manager for Python.
It will add the `poetry` command to Poetry's bin directory, located at:
D:\Program Files\pypoetry\bin
You can uninstall at any time by executing this script with the --uninstall option,
and these changes will be reverted.
Installing Poetry (1.3.1)
Installing Poetry (1.3.1): Creating environment
Installing Poetry (1.3.1): Installing Poetry
Installing Poetry (1.3.1): An error occurred. Removing partial environment.
Poetry installation failed.
See F:\Repos\notebook\poetry-installer-error-5nxluwjh.log for error logs.
🦄 cat poetry-installer-error-5nxluwjh.log
WARNING: Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ProxyError('Cannot connect to proxy.', OSError(0, 'Error'))': /simple/poetry/
WARNING: Retrying (Retry(total=3, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ProxyError('Cannot connect to proxy.', OSError(0, 'Error'))': /simple/poetry/
WARNING: Retrying (Retry(total=2, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ProxyError('Cannot connect to proxy.', OSError(0, 'Error'))': /simple/poetry/
WARNING: Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ProxyError('Cannot connect to proxy.', OSError(0, 'Error'))': /simple/poetry/
WARNING: Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ProxyError('Cannot connect to proxy.', OSError(0, 'Error'))': /simple/poetry/
ERROR: Could not find a version that satisfies the requirement poetry==1.3.1 (from versions: none)
ERROR: No matching distribution found for poetry==1.3.1
WARNING: There was an error checking the latest version of pip.
Traceback:
File "<stdin>", line 919, in main
File "<stdin>", line 550, in run
File "<stdin>", line 572, in install
File "<stdin>", line 675, in install_poetry
File "<stdin>", line 367, in pip
File "<stdin>", line 364, in python
File "<stdin>", line 357, in run
指定使用 代理, 还是失败了
(Invoke-WebRequest -Proxy http://127.0.0.1:10808 -Uri https://install.python-poetry.org -UseBasicParsing).Content | python -
🦄 (Invoke-WebRequest -Proxy http://127.0.0.1:10808 -Uri https://install.python-poetry.org -UseBasicParsing).Content | python -
Invoke-WebRequest: The response ended prematurely.
尝试另外一种方式
curl
curl -sSL https://install.python-poetry.org | python -
还是相同错误 失败
curl -sSL https://install.python-poetry.org | python3 - --git https://github.com/python-poetry/poetry.git@master
从 GitHub 安装失败
(Invoke-WebRequest -Proxy http://127.0.0.1:10808 -Uri https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py -UseBasicParsing).Content | python -
还是失败
poetry
参考:
python install-poetry.py --file poetry-1.3.1.tar.gz
python install-poetry.py --path poetry-1.3.1.tar.gz
安装失败, 详细查看,还是 代理连接出错, 好奇怪的报错,
pip.ini
代理等配置与代理工具均已关闭尝试下设置
pip
国内镜像源, 发现上次电脑重装后, 还没有重新配置安装过程极慢, 但总算是安装上了
Add Poetry to your PATH
The installer creates a poetry
wrapper in a well-known, platform-specific directory:
$HOME/.local/bin
on Unix.%APPDATA%\Python\Scripts
on Windows.$POETRY_HOME/bin
if $POETRY_HOME
is set.If this directory is not present in your $PATH
, you can add it in order to invoke Poetry as poetry
.
Alternatively, the full path to the poetry
binary can always be used:
~/Library/Application Support/pypoetry/venv/bin/poetry
on MacOS.~/.local/share/pypoetry/venv/bin/poetry
on Linux/Unix.%APPDATA%\pypoetry\venv\Scripts\poetry
on Windows.$POETRY_HOME/venv/bin/poetry
if $POETRY_HOME
is set.如下图
poetry --version
poetry self update
poetry new --src my-package
my-package
├── pyproject.toml
├── README.md
├── src
│ └── my_package
│ └── __init__.py
└── tests
└── __init__.py
参考:
poetry build
poetry publish
poetry config http-basic.pypi <username> <password>
发布成功
Typer
参考:
poetry add "typer[all]"
# src/my_package/main.py
import typer
app = typer.Typer()
@app.callback()
def callback():
"""
Awesome Portal Gun
"""
@app.command()
def shoot():
"""
Shoot the portal gun
"""
typer.echo("Shooting portal gun")
@app.command()
def load():
"""
Load the portal gun
"""
typer.echo("Loading portal gun")
pyproject.toml
[tool.poetry.scripts]
my-package = "my_package.main:app"
my-package
: 是 CLI 程序的名, 用于在 terminal 中呼叫
poetry install
my-package
poetry build
# 从本地文件包安装, 注意替换文件路径
pip install --user /home/rock/code/rick-portal-gun/dist/rick_portal_gun-0.1.0-py3-none-any.whl
poetry install
安装后, 新开 Terimal 还是不能使用, 尝试 build 再 pip install
pip install --user dist/imaging-0.0.1-py3-none-any.whl
可在
main.py
最后添加__main__
用于启动测试main.py
if __name__ == "__main__":
app()
imaging
识别不到[tool.poetry.scripts]
imaging = "imaging.main:app"
注意: 不是
src.imaging.main:app
, 因为前面packages
都已经include
了目测还必须将以下路径添加到环境变量
PATH
中,就如上面的图中 Warning 一样
C:\Users\yiyun\AppData\Roaming\Python\Python38\Scripts
发现若
Scripts
文件夹 已存在imaging.exe
则反复安装并不会更新, 需要先卸载, 再安装即可成功
pip uninstall dist/pyimaging-0.0.1-py3-none-any.whl
pip install --user dist/pyimaging-0.0.1-py3-none-any.whl
PS: 很神奇, 目测有除包名外区分方法, 居然旧的包名(
imaging
)也一并卸载了
或者:
pip uninstall pyimaging
UnicodeDecodeError: 'gbk' codec can't decode byte 0x80 in position 100: illegal multibyte sequence
参考:
pip install --user dist/pyimaging-0.0.1-py3-none-any.whl
ERROR: Exception:
Traceback (most recent call last):
File "D:\anaconda3\lib\site-packages\pip\_internal\cli\base_command.py", line 188, in _main
status = self.run(options, args)
File "D:\anaconda3\lib\site-packages\pip\_internal\cli\req_command.py", line 185, in wrapper
return func(self, options, args)
File "D:\anaconda3\lib\site-packages\pip\_internal\commands\install.py", line 398, in run
installed = install_given_reqs(
File "D:\anaconda3\lib\site-packages\pip\_internal\req\__init__.py", line 67, in install_given_reqs
requirement.install(
File "D:\anaconda3\lib\site-packages\pip\_internal\req\req_install.py", line 804, in install
install_wheel(
File "D:\anaconda3\lib\site-packages\pip\_internal\operations\install\wheel.py", line 622, in install_wheel
install_unpacked_wheel(
File "D:\anaconda3\lib\site-packages\pip\_internal\operations\install\wheel.py", line 596, in install_unpacked_wheel
rows = get_csv_rows_for_installed(
File "D:\anaconda3\lib\site-packages\pip\_internal\operations\install\wheel.py", line 247, in get_csv_rows_for_installed
for row in old_csv_rows:
UnicodeDecodeError: 'gbk' codec can't decode byte 0x80 in position 100: illegal multibyte sequence
修改下方路径文件
D:\anaconda3\Lib\site-packages\pip\_internal\operations\install\wheel.py
找到
get_csv_rows_for_installed
, 发现此方法没有文件读取操作, 不是此方法, 搜索open(
, 为所有打开文件操作加上 下方 encoding 要求
, encoding="utf-8"
🦄 pip uninstall pyimaging
Found existing installation: pyimaging 0.2.0
ERROR: Exception:
Traceback (most recent call last):
File "D:\anaconda3\lib\site-packages\pip\_internal\cli\base_command.py", line 188, in _main
status = self.run(options, args)
File "D:\anaconda3\lib\site-packages\pip\_internal\commands\uninstall.py", line 85, in run
uninstall_pathset = req.uninstall(
File "D:\anaconda3\lib\site-packages\pip\_internal\req\req_install.py", line 675, in uninstall
uninstalled_pathset = UninstallPathSet.from_dist(dist)
File "D:\anaconda3\lib\site-packages\pip\_internal\req\req_uninstall.py", line 535, in from_dist
for path in uninstallation_paths(dist):
File "D:\anaconda3\lib\site-packages\pip\_internal\req\req_uninstall.py", line 67, in unique
for item in fn(*args, **kw):
File "D:\anaconda3\lib\site-packages\pip\_internal\req\req_uninstall.py", line 85, in uninstallation_paths
r = csv.reader(FakeFile(dist.get_metadata_lines('RECORD')))
File "D:\anaconda3\lib\site-packages\pip\_vendor\pkg_resources\__init__.py", line 1432, in get_metadata_lines
return yield_lines(self.get_metadata(name))
File "D:\anaconda3\lib\site-packages\pip\_vendor\pkg_resources\__init__.py", line 1424, in get_metadata
return value.decode('utf-8')
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc7 in position 908: invalid continuation byte in RECORD file at path: c:\users\yiyun\appdata\roaming\python\python38\site-packages\pyimaging-0.2.0.dist-info\RECORD
此错误也是相同解决方法, encoding 问题 注意: 下方不要添加
# ValueError: binary mode doesn't take an encoding argument
with open(path, 'rb') as stream:
Could not install packages due to an EnvironmentError: [Errno 2] No such file or directory
参考:
🦄 pip install --user dist/pyimaging-0.0.1-py3-none-any.whl
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple/, https://pypi.tuna.tsinghua.edu.cn/simple/, http://pypi.douban.com/simple/, http://pypi.mirrors.ustc.edu.cn/simple/
Requirement already satisfied: pyimaging==0.0.1 from file:///F:/Repos/imaging/dist/pyimaging-0.0.1-py3-none-any.whl in c:\users\yiyun\appdata\roaming\python\python38\site-packages (0.0.1)
WARNING: No metadata found in c:\users\yiyun\appdata\roaming\python\python38\site-packages
ERROR: Could not install packages due to an EnvironmentError: [Errno 2] No such file or directory: 'c:\\users\\yiyun\\appdata\\roaming\\python\\python38\\site-packages\\pyimaging-0.0.1.dist-info\\METADATA'
FileNotFoundError: [Errno 2] No such file or directory: 'c:\\users\\yiyun\\appdata\\roaming\\python\\python38\\site-packages\\pyimaging-0.0.1.dist-info\\RECORD'
🦄 pip uninstall pyimaging
Found existing installation: pyimaging 0.0.1
ERROR: Exception:
Traceback (most recent call last):
File "D:\anaconda3\lib\site-packages\pip\_internal\cli\base_command.py", line 188, in _main
status = self.run(options, args)
File "D:\anaconda3\lib\site-packages\pip\_internal\commands\uninstall.py", line 85, in run
uninstall_pathset = req.uninstall(
File "D:\anaconda3\lib\site-packages\pip\_internal\req\req_install.py", line 675, in uninstall
uninstalled_pathset = UninstallPathSet.from_dist(dist)
File "D:\anaconda3\lib\site-packages\pip\_internal\req\req_uninstall.py", line 535, in from_dist
for path in uninstallation_paths(dist):
File "D:\anaconda3\lib\site-packages\pip\_internal\req\req_uninstall.py", line 67, in unique
for item in fn(*args, **kw):
File "D:\anaconda3\lib\site-packages\pip\_internal\req\req_uninstall.py", line 85, in uninstallation_paths
r = csv.reader(FakeFile(dist.get_metadata_lines('RECORD')))
File "D:\anaconda3\lib\site-packages\pip\_vendor\pkg_resources\__init__.py", line 1432, in get_metadata_lines
return yield_lines(self.get_metadata(name))
File "D:\anaconda3\lib\site-packages\pip\_vendor\pkg_resources\__init__.py", line 1420, in get_metadata
value = self._get(path)
File "D:\anaconda3\lib\site-packages\pip\_vendor\pkg_resources\__init__.py", line 1616, in _get
with open(path, 'rb') as stream:
FileNotFoundError: [Errno 2] No such file or directory: 'c:\\users\\yiyun\\appdata\\roaming\\python\\python38\\site-packages\\pyimaging-0.0.1.dist-info\\RECORD'
经过测试, 直接删除 下方路径文件夹即可再次成功安装
C:\Users\yiyun\AppData\Roaming\Python\Python38\site-packages\pyimaging-0.0.1.dist-info
发现我反复安装后, 终于有了这两个在 install (METADATA) 与 uninstall(RECORD) 时会寻找的两个文件
C:\Users\yiyun\AppData\Roaming\Python\Python38\site-packages\pyimaging-0.0.1.dist-info
目前 GitHub Package 不支持 Python 包
参考:
import os
from PIL import Image, ImageDraw, ImageFont
def add_watermark(image_path, watermark_text):
image = Image.open(image_path)
font = ImageFont.truetype("arial.ttf", 32)
fill_color = (255, 255, 255)
width, height = image.size
draw = ImageDraw.Draw(image)
text_width, text_height = draw.textsize(watermark_text, font)
x = (width - text_width) / 2
y = (height - text_height) / 2
draw.text((x, y), watermark_text, font=font, fill=fill_color)
image.save(image_path)
def watermark_images_in_folder(folder_path, watermark_text):
for root, dirs, files in os.walk(folder_path):
for file in files:
if file.endswith(('.jpg', '.jpeg', '.png')):
image_path = os.path.join(root, file)
add_watermark(image_path, watermark_text)
watermark_images_in_folder(".", "Powered by Python")
参考:
poetry
添加私有仓库源 (eg: 国内 PyPi 镜像源)参考:
添加国内清华镜像源 - pypi | 镜像站使用帮助 | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror
poetry source add tsinghua https://pypi.tuna.tsinghua.edu.cn/simple
对应配置文件 (pyproject.toml) 会自动添加上以下部分
[[tool.poetry.source]]
name = "tsinghua"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
default = false
secondary = false
PS:
# 添加 foo 源 为 次要(secondary) 源
poetry source add --secondary foo https://pypi.example.org/simple/
# 指定从 foo 源下载 private-package
poetry add --source foo private-package
感谢帮助!