7 คะแนน โดย GN⁺ 2025-06-25 | 2 ความคิดเห็น | แชร์ทาง WhatsApp
  • เมื่อเปลี่ยนมาใช้ 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 ความคิดเห็น

 
yangeok 2025-06-26

เดิมทีก็กำลังจะย้ายจากที่ดีพลอยด้วย poetry อยู่พอดี แบบนี้ดูทั้งเสถียรและเรียบง่ายดีนะครับ ^^

 
GN⁺ 2025-06-25
ความคิดเห็นบน 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 ใช้เวลา 10 นาที แต่ uv จัดการเสร็จใน 20~30 วินาที
    • นี่แหละคือเหตุผลที่เริ่มใช้ uv สะดวกมากจริง ๆ แต่บางครั้ง uv pip ก็ช้าโดยไม่รู้สาเหตุ สงสัยว่าอาจเป็นปัญหาเครือข่ายในบริษัท
    • เข้าใจว่าข้อมูลเวอร์ชัน Python ถูกเก็บไว้ใน pyproject.toml ด้วย เลยสงสัยว่าจำเป็นต้องมีไฟล์ .python-version จริงหรือไม่
  • # สคริปต์ที่รับประกันว่า lock file เป็นเวอร์ชันล่าสุดเสมอ
    if ! test -f uv.lock || ! uv lock --check 2>/dev/null; then
      uv lock
    fi
    

    วิธีแบบนี้ทำให้ความหมายของการมี lock file ลดความสำคัญลงไปมาก ถ้าไฟล์ไม่มีอยู่หรือใช้ไม่ได้ ก็แปลว่าสถานะของ lock file มีปัญหาร้ายแรง และควรให้คนที่คุ้นเคยกับโปรเจกต์นั้นมาจัดการเอง ไม่อย่างนั้นก็ไม่มีเหตุผลที่จะต้องมี lock file เลย ใน CI ก็อาจเกิดความสับสนได้หากมีการแทนที่ lock file อัตโนมัติ

    • (คำตอบจากผู้เขียน) ถ้า lock file ใช้ไม่ได้ มันไม่ได้ข้ามเงียบ ๆ แล้วสร้างไฟล์ใหม่ uv lock จะล้มเหลวพร้อมข้อความที่อธิบายอย่างชัดเจน และด้วย errexit ในเชลล์สคริปต์ กระบวนการจะหยุดทันที การ redirect error ของ uv lock --check มีไว้เพื่อป้องกันไม่ให้ข้อความผิดพลาดเดียวกันแสดงซ้ำสองครั้ง ถ้าตั้งใจทำ lock file ให้ผิดแล้วรันสคริปต์ บิลด์จะหยุดพร้อมข้อความผิดพลาดที่ระบุชัดเจน สคริปต์ถูกแก้ให้เป็น if-else เพื่อให้ชัดเจนขึ้น ถ้าไม่มี lock file การสร้างใหม่ก็เป็นขั้นตอนที่ถูกต้อง จากนั้นก็ commit ได้
    • ส่วนนี้มีตัวเลือก uv sync --locked รองรับอยู่แล้ว ถ้า lock file ไม่มีหรือเก่า มันจะล้มเหลวอย่างชัดเจน แนะนำให้บิลด์พร้อมตัวเลือก --locked เสมอ
    • ในโลก Python หลายคนไม่ค่อยเอา lock file เข้า version control และมักมองว่าเป็น "ขั้นตอนแปลก ๆ" ระหว่างการติดตั้ง
    • วิธีนี้มีบั๊กร้ายแรง ถ้าใช้แฟล็ก --frozen lock file ควรจะไม่ถูกอัปเดต แต่ในความเป็นจริงกลับทำงานตรงกันข้าม เห็นด้วยว่าถ้า lock file ไม่มีหรือไม่ตรง คนควรเข้ามาแทรกแซงเอง
    • ถึงอย่างนั้น ถ้าไม่มี lock file ก็อาจเป็นการรันครั้งแรก หรือเดี๋ยวก็จะถูกเขียนทับจาก git upstream อยู่ดี ถ้ามันพัง แปลว่ามีคนทำขั้นตอนติดตั้งพลาด และการสร้างใหม่ก็น่าจะเป็นวิธีที่สมเหตุสมผลที่สุดอยู่แล้ว เป็นกรณียกเว้นที่เกิดไม่บ่อย แต่สำหรับการจัดการแบบง่าย ๆ ก็เพียงพอ
  • ไม่เห็นด้วยอย่างมากกับแนวคิดที่ให้เครื่องมือ Python ถูกพัฒนาด้วยภาษาที่ไม่ใช่ Python มี C อยู่แล้วและ CPython ก็เป็นมาตรฐานอยู่แล้ว จึงไม่เห็นความจำเป็นต้องมีภาษาใหม่อย่าง Rust เพิ่มเข้ามา แพ็กเกจ Pendulum รองรับ 3.13 ช้ากว่า 7 เดือน และมองว่าส่วนหนึ่งเป็นเพราะมันใช้ Rust native จึงมีคนที่แก้ปัญหานี้ได้ไม่มาก ถ้าเป็น C ก็คงแก้เองไปแล้ว (ประเด็นที่เกี่ยวข้อง) ในอุดมคติ ถ้าอยากได้ datetime ที่เร็วด้วยภาษาภายนอกอย่าง Rust ก็ควรทำผ่าน FFI ให้หลายภาษานำไปใช้ได้ Rust-based ยังไม่ค่อยถูกใจ และเริ่มเข้าใจแล้วว่าทำไมชุมชน Linux ถึงไม่ชอบมัน

    • เคารพมุมมองนี้ แต่คิดว่าการทำเครื่องมืออย่าง uv ด้วย Rust เป็นความคิดที่ดี ถ้าทำเครื่องมือจัดการ Python ด้วย Python เอง ก็จะเกิดปัญหาแบบ "ไก่กับไข่อะไรเกิดก่อน" เพราะถ้าจะใช้เครื่องมือ Python ก็ต้องมี Python ติดตั้งไว้ก่อน ต้องคอยสนใจว่าใช้ Python เวอร์ชันไหน มีโอกาสชนกันระหว่างไลบรารีของตัวเครื่องมือกับของแอปจริง การจัดการตัวแปรสภาพแวดล้อมและการดีบักก็ซับซ้อนขึ้น แต่ถ้าเป็นเครื่องมือแบบไบนารีที่สร้างด้วย Rust หรือภาษาอื่น ก็แค่ดาวน์โหลดมาใช้และทำงานได้ทันที โดยไม่ต้องกังวลเรื่องเหล่านี้ ผู้ใช้ส่วนใหญ่ก็ไม่จำเป็นต้องสนใจว่าเครื่องมือถูกสร้างด้วยภาษาอะไร
    • ฉันชอบ Python แต่ความง่ายและความเร็วของ uv เทียบกันไม่ได้เลย เวลาเจอเซิร์ฟเวอร์ที่หมด EOL แล้วแต่ยังต้องการ Python ใหม่ หรือเวลาอยากติดตั้ง dependency ให้สคริปต์เล็ก ๆ แบบเร็ว ๆ uv คือทางเลือกที่ดีที่สุด เห็นด้วยบางส่วนด้วยว่าเมื่อก่อนเขียน pure Python อย่างเดียว แล้วค่อย ๆ ใช้ C extension มากขึ้น พอชนข้อจำกัดก็เริ่มอยากเขียนเกือบทั้งหมดด้วย C แต่เพราะ C ยาก ช่วงหลังเลยรีแฟกเตอร์ไปใช้ Rust ถ้าโค้ดภายนอกเยอะกว่าโค้ดภายในแล้ว การเปลี่ยนทั้งก้อนไปเป็นภาษาอื่นอาจดีกว่า
    • ถ้าใครยังยึดติดว่าต้องทำเครื่องมือด้วย Python ล้วน ก็เชิญรอ Pylint แบบช้า ๆ ไป ส่วนฉันจะออกไปเดินเล่น
    • การรองรับหลายภาษาแทบไม่เพิ่มภาระให้ผู้ใช้ ขอแค่เครื่องมือเร็วและแก้ปัญหาได้ดีก็พอ ซึ่งในทางปฏิบัติก็เร็วกว่ามาก เครื่องมือจัดการมีไว้สำหรับนักพัฒนา เพื่อจัดการสิ่งที่จะถูกใช้งาน
    • สำหรับฉันจะเขียนด้วยภาษาอะไรก็ได้ ขอแค่ทำงานได้ดี แม้ผู้ใช้ Python จะมีข้อได้เปรียบตรงที่ช่วย contribute ให้เครื่องมือได้ แต่ถ้าเครื่องมือทำหน้าที่ได้ดี ภาษาที่ใช้ก็ไม่สำคัญ ตรงกันข้าม ถ้าไปเจอปัญหาเรื่อง environment เครื่องมือที่เขียนด้วย Python ก็อาจโดนปัญหาเดียวกันลากลงไปด้วย
  • ถ้าจะใช้ uv แทน pip ต้องระวัง เพราะค่าเริ่มต้นจะไม่สร้างไฟล์ pyc ซึ่งอาจทำให้บริการเริ่มทำงานช้าลงได้ (อ้างอิง)

    • ถ้าใช้ uv ในคอนเทนเนอร์ เอกสารไกด์จะช่วยได้มากกว่า (คู่มือ Docker)
  • ถ้าลองใช้ 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 variable UV_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 กันหมด
    • นี่เป็นบทเรียนว่าควรออกแบบเรื่องพวกนี้ให้ดีตั้งแต่ช่วงแรกของการออกแบบภาษา อย่าผลักไปหลัง v2.0 และควรคิดให้รอบคอบหลายครั้งก่อนจะฝัง metadata ลงในสคริปต์ที่รันได้ บางแนวทางอาจเหมาะกับบางภาษา แต่ไม่เหมาะกับ Python
    • ฉันไม่เคยมีปัญหาเรื่อง dependency เลย แค่ใช้ requirements.txt กับ venv ก็พอแล้ว
    • การจัดการ dependency ยังเละเหมือนเดิม ตอนนี้แถมมี Rust เพิ่มเข้ามาอีก
  • อยากรู้ว่าในแง่ความปลอดภัย uv, pip, conda และตัวจัดการแพ็กเกจ Python อื่น ๆ ต่างกันอย่างไร ความเร็วก็สำคัญ แต่คิดว่าความปลอดภัยของ package manager สำคัญกว่ามาก

    • uv ปลอดภัยกว่า pip ในบางด้าน มันวิเคราะห์ dependency โดยไม่ต้องรันโค้ดตามอำเภอใจ ตรวจสอบแฮชของแพ็กเกจเป็นค่าเริ่มต้น และหลีกเลี่ยงความเสี่ยงหลายแบบ เช่น typosquatting ทั้งยังเร็วและทำซ้ำได้ดี (บทความเทคนิค, เอกสารความเข้ากันได้)
    • แต่ในแก่นแท้แล้ว package manager ทุกตัวก็ต้องดาวน์โหลดและรันโค้ดจากบุคคลที่สามที่ยังไม่ได้รับการตรวจสอบอยู่ดี เมื่อเทียบกับความแตกต่างด้านความปลอดภัยของ implementation ระหว่าง uv กับ pip ความเสี่ยงที่สำคัญกว่าคือการไม่มีนโยบายชัดเจนต่อโค้ดภายนอกตั้งแต่แรก
  • ในฐานะคนที่อัปโหลดแพ็กเกจขึ้น PyPI ส่วนตัวอยากใช้ uv เพราะเร็วมาก แต่ถ้าไม่มีหลักประกันว่ามันจะทำงานเหมือน pip ทุกประการ ก็เปลี่ยนได้ยาก เพราะถ้าผู้ใช้เจอปัญหาจาก pip install xxx ฉันก็ต้องสามารถจำลองและดีบักในสภาพแวดล้อมเดียวกันได้

    • มันไม่ได้ทำงานเหมือน pip 100% ความแตกต่างหลัก ๆ ถูกอธิบายไว้ในเอกสารความเข้ากันได้ บางส่วนเป็นความต่างจากการขยับไปตามมาตรฐาน บางส่วนเป็นการตัดสินใจด้านการออกแบบเฉพาะของ uv
  • คิดว่า UV เป็นหนึ่งในการเปลี่ยนแปลงเชิงบวกที่สุดของโลก Python packaging ในช่วงหลัง เพราะแค่รันก็ให้ผลลัพธ์ที่ดีได้เลย

  • ยังมีการแนะนำคู่มือดีมากสำหรับการใช้ uv เพื่อสร้างคอนเทนเนอร์ production ด้วย (ดูคู่มือ)