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

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://locust.io/

https://github.com/locustio/locust

loguru

https://loguru.readthedocs.io/en/stable/

https://github.com/Delgan/loguru

streamlit

https://docs.streamlit.io/

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

Library Documentation

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}")