Python plugins
poetry
poetry init # 初始化一個 pyproject.toml
poetry env use python3
poetry shell
poetry install # 從現有的 pyproject.toml 或 poetry.lock 安裝
poetry install --only main --no-root
poetry add "package"
poetry add black -G dev
poetry remove "package"
poetry update "package"
poetry lock
poetry show ["package"]
poetry show --tree
poetry export -f requirements.txt -o requirements.txt
poetry export -f requirements.txt --without-hashes -o requirements.txt --only main
poetry config --list
poetry config virtualenvs.in-project true
uv
uv init
uv python install 3.13 # https://docs.astral.sh/uv/getting-started/features/#python-versions
uv python pin 3.13 # 將專案的 python 版本固定
uv venv # 建立虛擬環境
uv run main.py # 可以在不進入虛擬環境的情況下執行指令
uv add pydantic pydantic-settings
uv add --dev ruff # 同 --group dev
uv add --group test pytest
uv remove pydantic
uv remove --group test pytest
uv lock # 根據 pyproject.toml 創建/更新 lockfile,但只要 uv.lock 符合目前的安裝條件,則不會顯式升級套件
uv lock --locked # 檢查 lockfile 是否是最新的 (也可以使用 --check)
uv lock --upgrade-package pydantic # 升級指定 package
uv lock --upgrade # 升級所有 package。類似 poetry update,但不會更新到環境內,只會更新 uv.lock 檔
uv sync # 如果 lockfile 不存在,會安裝最新且符合條件的套件,相當於 poetry install + poetry update
uv sync --group test # 額外安裝 test group 的套件
uv sync --all-groups # 額外安裝所有 group 的套件
uv sync --frozen # 不會更新 lockfile,而是根據 uv.lock 來安裝套件。相當於 poetry install,但如果 lock 檔案不存在會報錯
uv sync --frozen --all-extras
uv tree
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["your_package_path"]
pyenv
pyenv --version # 查看 pyenv 版本
pyenv versions # 列出所有已安裝的 python 版本 (如果該資料夾下有 .python-version 檔案,會使用其指定的版本,否則使用全域設定)
pyenv global system # 切換全域設定到系統 python 版本
pyenv global 3.13.1 # 切換全域設定到指定 python 版本
pyenv local 3.12.7 # 將當前目錄切換到指定 python 版本,並寫入 .python-version 檔案內
pyenv install --list # 列出所有可安裝的 python 版本
pyenv uninstall 3.13.0
pyenv install 3.13.1
update pyenv
cd ~/.pyenv
git pull
pipx
pipx list # 列出目前安裝的套件
pipx upgrade <package> # 升級指定套件
isort
isort .
isort . --profile black --filter-files
black
black .
ruff
ruff check .
mypy
[tool.mypy]
plugins = ["pydantic.mypy"]
python_version = "3.12"
# mypy_path = "stubs"
strict = true
allow_untyped_calls = true # for third party library
allow_untyped_decorators = true # for third party library
explicit_package_bases = true
exclude = ["playground"] # 排除哪些資料夾不檢查
follow_imports = "silent"
no_site_packages = true
ignore_missing_imports = true
pretty = true
# disable_error_code = ["attr-defined", "type-arg", "no-untyped-def"]
[[tool.mypy.overrides]]
module = ["app.config.pydantic_config", "app.middleware.*", "app.models._settings_model"]
allow_subclassing_any = true # for pydantic
[[tool.mypy.overrides]]
module = "tests.*"
allow_any_generics = true
# <https://mypy.readthedocs.io/en/latest/type_inference_and_annotations.html>
# mypy: ignore-errors
# type: ignore[attr-defined]
mypy .
mypy . --python-version=3.12
pydantic
https://docs.pydantic.dev/latest/
pydantic-settings
https://docs.pydantic.dev/latest/concepts/pydantic_settings/
orjson
import orjson
rich
https://github.com/Textualize/rich
from rich import print
from rich.console import Console
print()
console = Console()
icecream
ic("你要 print 的東西")
pre-commit
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
pre-commit install # 啟用指定的 git hook
pre-commit autoupdate # 更新以下 repo 的版本
pre-commit run --all-files # 全檔案檢查,而不是只有被提交的檔案
pre-commit run <hook_id> --all-files # 全檔案檢查,但只檢查特定 hook
commitizen
https://commitizen-tools.github.io/commitizen/
cz c # update changelog
cz ch # git commit
cz bump # 版號升級
[tool.commitizen]
name = "cz_conventional_commits"
version_provider = "poetry"
update_changelog_on_bump = true
viztracer
https://github.com/gaogaotiantian/viztracer
from viztracer import VizTracer
tracer = VizTracer()
tracer.start()
# Something happens here
tracer.stop()
tracer.save()
with VizTracer() as tracer:
# Something happens here
# Jupyter
# You need to load the extension first
%load_ext viztracer
%%viztracer
# Your code after
celery
https://docs.celeryq.dev/en/stable/
https://github.com/celery/celery
locust
https://github.com/locustio/locust
loguru
https://loguru.readthedocs.io/en/stable/
https://github.com/Delgan/loguru
streamlit
https://github.com/streamlit/streamlit
jellyfish
https://jamesturk.github.io/jellyfish/
https://github.com/jamesturk/jellyfish
streamlit
basic
pip install streamlit
streamlit run app.py
library
Text elements
import streamlit as st
st.text("Hello, world!")
st.header("This is a header")
st.subheader("This is a subheader")
st.title("This is a title")
st.markdown("This is a markdown")
st.caption("This is a :blue[caption]. :rainbow[This is a rainbow]")
st.write(":colors[blue, green, orange, red, violet, gray/grey, rainbow.]")
st.code("print('Hello, world!')")
code_str = """
def hello(name: str) -> None:
print(f"Hello, {name}!")
"""
st.code(code_str, language="python")
st.success("This is a success")
st.warning("This is a warning")
st.info("This is an info")
st.error("This is an error")
st.exception("This is an exception")
st.write("This is a write")
st.write('Hello, *World!* :sunglasses:')
st.write(1 + 1)
st.write([1, 2, 3])
st.help(range) # st.write(range)
Data elements
import streamlit as st
import pandas as pd
df = pd.DataFrame({
'first column': [1, 2, 3, 4],
'second column': [10, 20, 30, 40],
})
st.dataframe(df) # st.write(df)
st.dataframe(df, width=200, height=100)
st.dataframe(df.style.highlight_max(axis=0))
st.table(df)
st.json({"data": "name"}) # st.write({"data": "name"})
file upload
import os
import docx2txt # pip install docx2txt
import fitz # pip install PyMuPDF
import pandas as pd # pip install pandas
import streamlit as st
from PIL import Image # pip install pillow
from streamlit.runtime.uploaded_file_manager import UploadedFile
def get_file_details(file: UploadedFile) -> dict[str, str | int]:
return {"FileName": file.name, "FileType": file.type, "FileSize": file.size}
@st.cache_data
def load_image(img_file: UploadedFile) -> Image.Image:
return Image.open(img_file)
def read_pdf(pdf_file: UploadedFile, page_number: int | None = None) -> list[str]:
with fitz.open(stream=pdf_file.read(), filetype="pdf") as pdf_document:
pages = len(pdf_document) if page_number is None else page_number
return [pdf_document.load_page(p).get_text() for p in range(pages)]
def save_uploaded_file(uploaded_file, filename: str):
path = os.path.join("./temp_dir", filename)
with open(path, "wb") as f:
f.write(uploaded_file.getbuffer())
return st.success(f"Saved file in: {path}")
st.title("File Upload Tutorial")
menu = ["Home", "Dataset", "DocumentFiles", "Audio"]
choice = st.sidebar.selectbox("Menu", menu)
if choice == "Home":
st.subheader("Home")
image_files = st.file_uploader(
"Upload Image", type=["png", "jpg", "jpeg"], accept_multiple_files=True
)
if image_files is not None:
for image_file in image_files:
st.write(get_file_details(image_file))
st.image(load_image(image_file))
save_uploaded_file(image_file, image_file.name)
elif choice == "Dataset":
st.subheader("Dataset")
data_file = st.file_uploader("Upload CSV", type=["csv"])
if data_file is not None:
st.write(get_file_details(data_file))
df = pd.read_csv(data_file)
st.dataframe(df)
edited_df = st.data_editor(df)
st.download_button(
label="Download data as CSV",
data=df.to_csv().encode("utf-8"),
file_name="large_df.csv",
mime="text/csv",
)
st.download_button(
label="Download edit_data as CSV",
data=edited_df.to_csv().encode("utf-8"),
file_name="large_df.csv",
mime="text/csv",
)
elif choice == "DocumentFiles":
st.subheader("DocumentFiles")
doc_file = st.file_uploader("Upload Document", type=["pdf", "docx", "txt"])
file_tpe = {
"txt": "text/plain",
"pdf": "application/pdf",
"docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
}
if doc_file is not None:
st.write(get_file_details(doc_file))
if doc_file.type == file_tpe["txt"]:
text = str(doc_file.read(), encoding="utf-8")
st.text(text) # 用 st.write(text) 換變成 markdown
elif doc_file.type == file_tpe["pdf"]:
texts = read_pdf(doc_file)
for i, text in enumerate(texts):
st.subheader(f"Page {i + 1}")
st.text(text)
st.divider()
elif doc_file.type == file_tpe["docx"]:
text = docx2txt.process(doc_file)
st.text(text)
else:
st.warning("File type not supported")
elif choice == "Audio":
st.subheader("Audio")
audio_file = st.file_uploader("Upload Audio", type=["mp3", "wav", "m4a", "flac"])
if audio_file is not None:
st.write(get_file_details(audio_file))
st.audio(audio_file)
st.download_button("Download Audio", audio_file, "audio.mp3", "audio/mp3")
form
from time import sleep
import streamlit as st
with st.form(key="my_form", clear_on_submit=True):
col1, col2, col3 = st.columns([3, 1, 2])
with col1:
amount = st.number_input("Enter the amount", value=50)
with col2:
hours_per_week = st.number_input("Enter the hours", 1, 120)
with col3:
st.text("Salary")
submit_button = st.form_submit_button(label="Calculate")
if submit_button:
with st.expander("Results", expanded=True):
with st.spinner("Calculating..."):
sleep(1.5)
st.write(f"Your salary is: {amount * hours_per_week}")