- เมื่อเปลี่ยนมาใช้ uv ความเร็วในการติดตั้ง dependency ของ Python จะเร็วกว่า pip ประมาณ 10 เท่า และยังสามารถรันด้วยผู้ใช้แบบ non-root ได้โดยไม่ต้องมี venv แยก
- หากใช้ pyproject.toml เป็นฐาน แล้วระบุเฉพาะ dependency ระดับบนสุด uv จะจัดการ lock file ให้อัตโนมัติ ทำให้ การจัดการ dependency tree และเวอร์ชันที่แม่นยำ ดีกว่า pip freeze
- ใน Dockerfile ต้องปรับทีละขั้น เช่น คัดลอกไบนารี uv และ uvx, ใช้ไฟล์ pyproject.toml/uv.lock และตั้งค่าตัวแปรสภาพแวดล้อม
- สามารถจัดการ dependency ได้สะดวกด้วยคำสั่งอย่าง uv sync/add/remove, uv:outdated สำหรับเพิ่ม·ลบ·อัปเดต dependency และตรวจสอบเวอร์ชันล่าสุดของแพ็กเกจ
- ทำให้สามารถดูแล lock file และอัปเดต dependency ได้อย่างสม่ำเสมอ จึงมีข้อดีด้านความสม่ำเสมอในงานร่วมกันและสภาพแวดล้อมการ deploy
ติดตั้ง dependency เร็วขึ้น 10 เท่า, ไม่ใช้ venv, ตั้งค่าสภาพแวดล้อมแบบ non-root
- uv เป็นเครื่องมือที่ช่วยปรับปรุงความเร็วในการติดตั้ง dependency ของโปรเจกต์ Python ได้อย่างมากเมื่อเทียบกับ pip
- ด้วยการนำ uv มาใช้ โปรเจกต์หลากหลายประเภทอย่าง Flask/Django สามารถสัมผัสได้ถึง ความเร็วในการติดตั้งที่มากกว่าราว 10 เท่า เมื่อเทียบกับ pip
- แม้ไม่มี virtual environment (venv) แยก ก็ยังสามารถ รันภายในคอนเทนเนอร์ด้วยผู้ใช้แบบ non-root ได้อย่างปลอดภัย
pyproject.toml vs requirements.txt
- แทนที่จะใช้ requirements.txt แบบเดิม สามารถระบุเฉพาะ dependency ระดับบนสุดไว้ในไฟล์ pyproject.toml แล้ว uv จะสร้างไฟล์ uv.lock ให้อัตโนมัติ
- เพิ่มรายการ
[project] dependencies ใน pyproject.toml
- ลบ requirements.txt เดิมออก
- lock file ของ uv คล้ายกับผลลัพธ์จาก pip freeze แต่มี dependency tree และข้อมูลเวอร์ชันที่แม่นยำกว่า
การปรับโครงสร้าง Dockerfile
- ใช้ ไบนารี uv และ uvx โดยคัดลอกเข้าไปในคอนเทนเนอร์ (เป็นไบนารี Rust ที่คอมไพล์แบบสแตติก)
- แทน requirements*.txt เดิมด้วยการคัดลอกไฟล์ pyproject.toml, uv.lock*
- เพิ่มตัวแปรสภาพแวดล้อม:
UV_COMPILE_BYTECODE=1: คอมไพล์เป็น bytecode ล่วงหน้าในขั้นตอน build
UV_PROJECT_ENVIRONMENT="/home/python/.local": ติดตั้งแพ็กเกจไปยังพาธที่กำหนดโดยไม่สร้าง venv แยก
- เปลี่ยนคำสั่งติดตั้ง dependency จาก
pip3-install เดิมเป็น uv-install
- ตัวอย่าง:
RUN chmod 0755 bin/* && bin/uv-install
การจัดการ dependency เช่น เพิ่ม ลบ และอัปเดต
- สามารถรันคำสั่ง uv ภายในคอนเทนเนอร์ผ่านสคริปต์ run แยกได้
./run deps:install: ติดตั้งหลัง build image และ export lock file กลับไปยังโฮสต์
./run deps:install --no-build: อัปเดตเฉพาะ lock file โดยไม่ต้อง build
./run uv add mypackage --no-sync: อัปเดตเฉพาะ pyproject.toml และ lock file ส่วนการติดตั้งจริงให้รันแยกภายหลัง
./run uv remove mypackage --no-sync: ลบแพ็กเกจ
./run uv:outdated: ตรวจสอบเวอร์ชันล่าสุดของ dependency ปัจจุบัน
มีวิดีโอและคู่มือทดลองใช้งาน
- มีเดโมจริงและตัวอย่าง git diff สำหรับการนำ uv มาใช้ การเขียน pyproject.toml การแก้ไข Dockerfile คำสั่ง lock/sync การเพิ่ม/ลบ dependency และการตรวจสอบเวอร์ชันล่าสุด
- สามารถอ้างอิง migration diff ของทั้งโปรเจกต์ Flask และ Django ได้ด้วย
2 ความคิดเห็น
เดิมทีก็กำลังจะย้ายจากที่ดีพลอยด้วย poetry อยู่พอดี แบบนี้ดูทั้งเสถียรและเรียบง่ายดีนะครับ ^^
ความคิดเห็นบน Hacker News
ควรสังเกตว่า uv รองรับเวิร์กโฟลว์ที่ใช้แทน pyenv, virtualenv และ pip ได้โดยตรง ไม่ได้เป็นแนวทางที่ถูกบังคับด้วย lockfile หรือ pyproject.toml เสมอไป สามารถใช้คำสั่ง
uv python pin <version>เพื่อสร้างไฟล์ .python-version ในไดเรกทอรีปัจจุบัน, ใช้uv virtualenvเพื่อดาวน์โหลด Python เวอร์ชันนั้นและสร้างสภาพแวดล้อมเสมือน .venv แบบเดียวกับ pyenv, ใช้uv pip install -r requirements.txtเพื่อติดตั้งแพ็กเกจจาก requirements.txt และใช้uv run <command>เพื่อรันคำสั่งพร้อมตัวแปรสภาพแวดล้อมจากไฟล์ .env ได้ แต่ต้องระวังปัญหาเรื่องลำดับความสำคัญของตัวแปรสภาพแวดล้อม (ประเด็นที่เกี่ยวข้อง)uv pipก็ช้าโดยไม่รู้สาเหตุ สงสัยว่าอาจเป็นปัญหาเครือข่ายในบริษัทวิธีแบบนี้ทำให้ความหมายของการมี lock file ลดความสำคัญลงไปมาก ถ้าไฟล์ไม่มีอยู่หรือใช้ไม่ได้ ก็แปลว่าสถานะของ lock file มีปัญหาร้ายแรง และควรให้คนที่คุ้นเคยกับโปรเจกต์นั้นมาจัดการเอง ไม่อย่างนั้นก็ไม่มีเหตุผลที่จะต้องมี lock file เลย ใน CI ก็อาจเกิดความสับสนได้หากมีการแทนที่ lock file อัตโนมัติ
uv lockจะล้มเหลวพร้อมข้อความที่อธิบายอย่างชัดเจน และด้วยerrexitในเชลล์สคริปต์ กระบวนการจะหยุดทันที การ redirect error ของuv lock --checkมีไว้เพื่อป้องกันไม่ให้ข้อความผิดพลาดเดียวกันแสดงซ้ำสองครั้ง ถ้าตั้งใจทำ lock file ให้ผิดแล้วรันสคริปต์ บิลด์จะหยุดพร้อมข้อความผิดพลาดที่ระบุชัดเจน สคริปต์ถูกแก้ให้เป็น if-else เพื่อให้ชัดเจนขึ้น ถ้าไม่มี lock file การสร้างใหม่ก็เป็นขั้นตอนที่ถูกต้อง จากนั้นก็ commit ได้uv sync --lockedรองรับอยู่แล้ว ถ้า lock file ไม่มีหรือเก่า มันจะล้มเหลวอย่างชัดเจน แนะนำให้บิลด์พร้อมตัวเลือก--lockedเสมอ--frozenlock file ควรจะไม่ถูกอัปเดต แต่ในความเป็นจริงกลับทำงานตรงกันข้าม เห็นด้วยว่าถ้า lock file ไม่มีหรือไม่ตรง คนควรเข้ามาแทรกแซงเองไม่เห็นด้วยอย่างมากกับแนวคิดที่ให้เครื่องมือ Python ถูกพัฒนาด้วยภาษาที่ไม่ใช่ Python มี C อยู่แล้วและ CPython ก็เป็นมาตรฐานอยู่แล้ว จึงไม่เห็นความจำเป็นต้องมีภาษาใหม่อย่าง Rust เพิ่มเข้ามา แพ็กเกจ Pendulum รองรับ 3.13 ช้ากว่า 7 เดือน และมองว่าส่วนหนึ่งเป็นเพราะมันใช้ Rust native จึงมีคนที่แก้ปัญหานี้ได้ไม่มาก ถ้าเป็น C ก็คงแก้เองไปแล้ว (ประเด็นที่เกี่ยวข้อง) ในอุดมคติ ถ้าอยากได้ datetime ที่เร็วด้วยภาษาภายนอกอย่าง Rust ก็ควรทำผ่าน FFI ให้หลายภาษานำไปใช้ได้ Rust-based ยังไม่ค่อยถูกใจ และเริ่มเข้าใจแล้วว่าทำไมชุมชน Linux ถึงไม่ชอบมัน
ถ้าจะใช้ uv แทน pip ต้องระวัง เพราะค่าเริ่มต้นจะไม่สร้างไฟล์ pyc ซึ่งอาจทำให้บริการเริ่มทำงานช้าลงได้ (อ้างอิง)
ถ้าลองใช้ uv กับคอนเทนเนอร์ Flask จะพบว่าความต่างของเวลา build มากจนรู้สึกได้ชัด และกระบวนการติดตั้งก็คาดเดาได้มากขึ้นมาก ไม่มีความปวดหัวจากการที่เวอร์ชัน dependency เปลี่ยนเองแบบที่เคยเจอกับ pip ใช้ pyproject.toml แล้วสั่ง
uv lockก็จบ ใน Docker ถ้าคัดลอกเฉพาะไฟล์ pyproject.toml กับ uv.lock (HOT COPY) แล้วรันuv sync --frozen --no-install-projectก็สามารถข้ามโค้ดแอปไปก่อน และ cache เลเยอร์ติดตั้งไว้ได้ ถ้าเคยเจ็บปวดกับการต้อง rebuild ทั้งเลเยอร์เพียงเพราะเปลี่ยนแพ็กเกจตัวเดียว ก็จะเข้าใจทันทีว่าทำไมความสามารถนี้ถึงสำคัญ ถ้าตั้งค่า environment variableUV_PROJECT_ENVIRONMENT=/home/python/.localก็สามารถ pre-warm base image โดยไม่ต้องใช้ venv ช่วยแชร์ build และลดต้นทุนโครงสร้างพื้นฐานได้ ใช้ตัวเลือกUV_COMPILE_BYTECODE=1เพื่อสร้างไฟล์ .pyc ระหว่าง build เมื่อกำจัด mutable environment และบังคับ reproducibility ได้แล้ว ถ้าบิลด์พังก็จะรู้ชัดว่าต้นเหตุคือ lockfileเข้าสู่ปี 2025 แล้ว แต่การจัดการแพ็กเกจและ dependency ของ Python ก็ยังคงสับสนอยู่ดี
อยากรู้ว่าในแง่ความปลอดภัย uv, pip, conda และตัวจัดการแพ็กเกจ Python อื่น ๆ ต่างกันอย่างไร ความเร็วก็สำคัญ แต่คิดว่าความปลอดภัยของ package manager สำคัญกว่ามาก
ในฐานะคนที่อัปโหลดแพ็กเกจขึ้น PyPI ส่วนตัวอยากใช้ uv เพราะเร็วมาก แต่ถ้าไม่มีหลักประกันว่ามันจะทำงานเหมือน pip ทุกประการ ก็เปลี่ยนได้ยาก เพราะถ้าผู้ใช้เจอปัญหาจาก
pip install xxxฉันก็ต้องสามารถจำลองและดีบักในสภาพแวดล้อมเดียวกันได้คิดว่า UV เป็นหนึ่งในการเปลี่ยนแปลงเชิงบวกที่สุดของโลก Python packaging ในช่วงหลัง เพราะแค่รันก็ให้ผลลัพธ์ที่ดีได้เลย
ยังมีการแนะนำคู่มือดีมากสำหรับการใช้ uv เพื่อสร้างคอนเทนเนอร์ production ด้วย (ดูคู่มือ)