- เมื่อใช้ uv จะช่วยทำให้ การจัดการ dependency เป็นอัตโนมัติขณะรันสคริปต์ Python
- โดยไม่ต้องจัดการ virtual environment แยกต่างหาก ระบบจะสร้างและดูแล environment ให้โดยอัตโนมัติในระดับสคริปต์
- สามารถประกาศแพ็กเกจที่ต้องใช้ได้หลายวิธี เช่น inline metadata หรือออปชันบนบรรทัดคำสั่ง
- การจัดการ เวอร์ชัน Python และแพ็กเกจก็สามารถประกาศในระดับสคริปต์และปรับให้อัตโนมัติได้เช่นกัน
- ใช้ ไฟล์ lock และตัวเลือกจำกัดเวอร์ชัน dependency เพื่อเพิ่มความสามารถในการทำซ้ำสภาพแวดล้อมและการบำรุงรักษา
ภาพรวม
- uv เป็นเครื่องมือที่ช่วยจัดการ package dependency ที่สคริปต์ต้องใช้โดยอัตโนมัติขณะรันสคริปต์ Python
- ผู้ใช้จึงไม่จำเป็นต้องสร้าง virtual environment หรือ ติดตั้งแพ็กเกจ ด้วยตนเองซึ่งค่อนข้างยุ่งยาก
- รองรับออปชันการรันหลายแบบ การใช้งาน inline metadata วิธีประกาศ dependency ที่หลากหลาย และฟังก์ชันควบคุมต่างๆ
สภาพแวดล้อม Python และบทบาทของ uv
- Python แต่ละการติดตั้งจะมี สภาพแวดล้อมเฉพาะของตัวเอง
- โดยทั่วไปแนะนำให้สร้างและจัดการ virtual environment
- uv จะจัดการ virtual environment ให้อัตโนมัติ และดูแล dependency ในรูปแบบ declarative
- สคริปต์ง่ายๆ สามารถรันได้ทันทีด้วย
uv run example.py
- หากใช้ standard library ก็ทำงานได้โดยไม่ต้องตั้งค่าเพิ่มเติม
การส่งอาร์กิวเมนต์และรูปแบบการรับอินพุต
- สามารถส่ง อาร์กิวเมนต์บรรทัดคำสั่ง ให้กับสคริปต์ได้
- รองรับการรับโค้ดสคริปต์จาก standard input เพื่อรันโดยตรง รวมถึงรองรับ here-document
สภาพแวดล้อมโปรเจกต์และออปชัน --no-project
- หากสคริปต์ถูกรันใน โฟลเดอร์โปรเจกต์ (เช่น มี
pyproject.toml) dependency ของโปรเจกต์จะถูกติดตั้งด้วย
- หากไม่ต้องการ สามารถใส่แฟล็ก
--no-project ไว้หน้าชื่อสคริปต์เพื่อ ละเว้น สภาพแวดล้อมของโปรเจกต์ได้
การประกาศและจัดการ dependency ของสคริปต์
- หากต้องใช้แพ็กเกจภายนอก สามารถระบุ dependency ผ่านออปชันบรรทัดคำสั่ง
--with แล้วรันได้
- รองรับเงื่อนไขจำกัดเวอร์ชันเฉพาะ และสามารถระบุหลาย dependency ได้โดยใส่ออปชันซ้ำ
- สามารถเพิ่ม dependency เพิ่มเติมทับบนสภาพแวดล้อมโปรเจกต์ได้ และหากไม่ต้องการก็ใช้
--no-project เพื่อควบคุมได้
Inline Script Metadata (รูปแบบ PEP 723)
- ตอนนี้ Python รองรับฟอร์แมตมาตรฐานสำหรับประกาศ dependency หรือเวอร์ชัน Python ไว้ในตัวสคริปต์เอง
- สามารถสร้างสคริปต์ที่มี inline metadata ได้อย่างง่ายดายด้วย
uv init --script
- สามารถใช้
uv add --script เพื่อเพิ่มและจัดการ dependency ที่สคริปต์ต้องใช้ในรูปแบบ TOML
- หากมี inline metadata ระบบจะ ไม่สนใจ dependency ของโปรเจกต์ และใช้เฉพาะ dependency ของสคริปต์เท่านั้น
การประกาศและจัดการเวอร์ชัน Python
- สามารถระบุ เวอร์ชัน Python ที่ต้องการได้ทั้งในสคริปต์หรือขณะสั่งรัน
- หากยังไม่มีเวอร์ชันที่ระบุ ระบบจะดาวน์โหลดและตั้งค่าให้อัตโนมัติ
การเขียนสคริปต์ที่รันได้โดยตรงด้วย shebang
- สามารถใช้ shebang (
#!...) เพื่อสร้างไฟล์รันได้โดยตรงในรูปแบบ uv run --script
- ในกรณีนี้ก็ยังสามารถประกาศ dependency และเวอร์ชัน Python ไว้ที่ด้านบนของสคริปต์ได้เช่นกัน
การรองรับ package index และการยืนยันตัวตน
- สามารถใช้ custom package index ได้ผ่านออปชัน
--index
- ข้อมูล index สามารถรวมไว้ใน metadata ได้เช่นกัน
- หากต้องมีการยืนยันตัวตน สามารถดูเอกสารเพิ่มเติมแยกต่างหากได้
การล็อก dependency และเพิ่มความสามารถในการทำซ้ำสภาพแวดล้อม
- สามารถสร้างและจัดการไฟล์ lock ในระดับสคริปต์ ได้ด้วย
uv lock --script
- หลังจากนั้นเมื่อรันหรือเพิ่ม dependency ระบบจะนำไฟล์ lock กลับมาใช้ซ้ำและอัปเดตเมื่อจำเป็น
- มีออปชัน
exclude-newer (ยกเว้นรีลีสหลังจากวันที่กำหนด) เพื่อช่วยให้ทำซ้ำเวอร์ชันได้แม่นยำ
- ระบุวันที่ด้วย timestamp รูปแบบ RFC 3339
ความยืดหยุ่นของเวอร์ชัน Python
- ในแต่ละการรันสามารถระบุให้ใช้ เวอร์ชัน Python ใดก็ได้ ผ่านออปชันบรรทัดคำสั่ง
- ตัวอย่าง:
uv run --python 3.10 example.py
การรองรับ Windows
- สคริปต์ที่ใช้นามสกุล
.pyw จะถูกรันด้วย pythonw บน Windows
- สคริปต์แบบ GUI ก็สามารถรันพร้อม dependency ได้เช่นกัน
เอกสารอ้างอิง
- หากต้องการดูวิธีใช้คำสั่งโดยละเอียดเพิ่มเติม สามารถดูเอกสารอ้างอิง CLI และคู่มือการรัน/ติดตั้งเครื่องมือได้
บทสรุป
- uv เป็นเครื่องมือที่ช่วยจัดการสภาพแวดล้อมการรัน dependency เวอร์ชัน package index และความสามารถในการทำซ้ำสภาพแวดล้อมของสคริปต์ Python ได้อย่างอัตโนมัติและสะดวก ช่วยเพิ่มทั้ง ประสิทธิภาพการทำงาน และ ความน่าเชื่อถือ ไปพร้อมกัน
7 ความคิดเห็น
ผมก็ลองย้ายจาก pip ไปใช้ uv เหมือนกัน แล้วก็รู้สึกว่าความเร็วที่เร็วมากนี่อย่างเดียวก็คุ้มพอให้ย้ายแล้วจริงๆ
เห็นโพสต์นี้บ่อยเลยเพิ่งได้ลองใช้ครั้งแรกเมื่อวาน... เร็วมากจริง ๆ เลย โอ้โห..
เหมือนจะเห็นโพสต์เกี่ยวกับ uv ที่นี่มาแล้วเกิน 5 โพสต์เลยนะ;;;
ฟังก์ชันอื่น ๆ ยังไม่ต้องพูดถึง แค่เรื่องความเร็วอย่างเดียวก็มีเหตุผลมากพอให้ใช้งานแล้ว
ถ้าจะให้กลับไปใช้ pip อีก บอกเลยว่าไม่มีทางทำได้แล้ว
ตอนนี้ผมใช้ flake.nix มาแทนการจัดการ system package ของ conda อยู่ และนอกจากงานที่ต้องทำร่วมกันหรือโปรเจกต์เดิมที่ดูแลต่อด้วย conda+pip แล้ว โดยส่วนตัวคิดว่าจากนี้ไปคงจะใช้ uv+nix เป็นหลัก
Uv - เครื่องมือแพ็กเกจจิง Python ความเร็วสูงพิเศษที่พัฒนาด้วย Rust
ปฏิวัติเวอร์กโฟลว์การพัฒนา Python ด้วย UV
ใช้งานสคริปต์ Python ด้วย uv และ PEP 723
ประสบการณ์ใช้งาน uv ตลอด 1 ปี: ข้อดีข้อเสียและสิ่งที่ควรพิจารณาเมื่อต้องย้ายมาใช้
ช่วงหลังผมเปลี่ยนการรัน Python เกือบทั้งหมดมาใช้ uv ซึ่งมันเร็วมาก
แม้ว่าจะมีฟีเจอร์ขั้นสูงบางอย่างที่ยังเข้ากันได้ไม่สมบูรณ์ แต่ในกรณีส่วนใหญ่ก็ทำงานได้แทบจะเหมือนกัน
ความเห็นจาก Hacker News
ได้ลองใช้ฟีเจอร์ "การประกาศ dependency ของสคริปต์" แล้วรู้สึกว่ามีประโยชน์มาก
ตามที่แนะนำไว้ในคู่มืออย่างเป็นทางการ เราสามารถระบุ dependency เป็นคอมเมนต์ไว้ด้านบนสุดของโค้ด Python ได้แบบนี้
บันทึกไฟล์นี้เป็น
script.pyแล้วรันด้วยuv run script.pydependency ที่ระบุไว้จะถูกติดตั้งลงใน virtual environment ชั่วคราวแบบเหมือนเสกขึ้นมา และสามารถรันได้ทันทีนี่คือการ implement PEP 723 ของ Python และ Claude 4 ก็รู้จักเทคนิคนี้ด้วย ดังนั้นถ้า prompt ว่า “เขียน Python script ที่มี inline script dependencies” มันก็จะสร้างออกมาได้ถูกต้อง
ตัวอย่างเช่น สามารถขอให้เขียนโค้ดที่ใช้ httpx กับ click เพื่อดาวน์โหลดไฟล์ขนาดใหญ่พร้อมแสดง progress bar ได้
ก่อนยุค Claude 4 ฟีเจอร์ลักษณะนี้ต้องใช้โปรเจกต์ที่ปรับแต่งเองพร้อมคำอธิบายเพิ่มเติม แต่ตอนนี้ไม่จำเป็นแล้ว
ดูตัวอย่างการใช้งานแบบละเอียดเพิ่มเติมได้
โหมด shebang ก็มีประโยชน์มากจริงๆ
ถ้าเพิ่ม shebang ไว้บรรทัดแรกของสคริปต์แบบนี้ ก็จะสามารถรันเหมือน
./script.shได้อยากให้รูปแบบมันเหมือนกับไฟล์ requirements
ถ้าเป็นแบบนั้น สำหรับผู้ใช้ที่ไม่มี uv ก็จะสามารถใส่คอมเมนต์ง่ายๆ เพิ่มไว้เพื่อบอก one-liner สำหรับติดตั้งด้วย pip แบบเดียวกันได้
เช่นอาจใช้แนวทางอย่าง
pip install -r <(head myscript.py)จริงๆ แล้ว PEP723 ตอนนี้ไม่ได้รองรับแค่ uv ที่กำลังมาแรง แต่ pipx และ hatch ก็รองรับด้วย
และเครื่องมืออย่าง pip-tools ก็อยู่ใน roadmap ที่จะรองรับเช่นกัน
(ดูissue ที่เกี่ยวข้อง)
ตอนเห็นครั้งแรก เคยคิดว่าเป็นอีโมจิรูปหัวใจอยู่ข้าง requests
คิดว่าวิธีนี้เจ๋งมาก
แต่สักวันหนึ่งก็หวังว่าจะถูกรับเข้าไปเป็น syntax ของภาษาโดยตรง ไม่ใช่ magic comment
คอมเมนต์แบบนี้ดูรกไปนิด
แน่นอนว่าในมุมของเครื่องมือ magic comment นั้น parse ง่ายกว่า และก็เข้าใจว่าฝั่ง Python core เองไม่ได้มีความรู้เรื่อง packaging มากนัก รวมถึงมีข้อพิจารณาเชิงโครงสร้างอื่นๆ แต่ก็ยังหวังว่าสักวันจะมี syntax ในภาษาโดยตรง
เห็นด้วยกับแนวทางนี้มาก
แม้ Python จะไม่ได้บังคับว่าต้องมีไฟล์ requirements.txt แต่ถ้าดูแลไม่ดี ก็มักเกิดปัญหาที่อะไรบางอย่างพังเพราะการจัดการไม่ดีได้บ่อย ซึ่งน่าเสียดาย
ดูทวีตที่เกี่ยวข้อง
อยากแชร์กับดักที่เคยเจอจากวิธีนี้
เคยเอาไปใช้กับสคริปต์สำหรับรีสตาร์ตเราเตอร์ตอนอินเทอร์เน็ตหลุด แต่เพราะกระบวนการติดตั้ง dependency ต้องพึ่งการเชื่อมต่ออินเทอร์เน็ต พอเน็ตใช้งานไม่ได้ สคริปต์เองก็รันไม่ได้ไปด้วย
โชคดีที่เจอก่อนเลยแก้ด้วยการติดตั้ง dependency ไว้ล่วงหน้า แต่ก็อยากเตือนว่าอย่าพลาดแบบผม และไม่แนะนำให้ใช้ในสภาพแวดล้อมแบบ airgapped จริงๆ (สภาพแวดล้อมที่ตัดขาดเครือข่ายโดยสมบูรณ์)
ต่อให้มี uv cache ก็ยังอาจเกิด cache miss ได้
ถ้าใช้ตัวเลือก
uv run --offlineก็จะใช้ dependency ที่ cache ไว้ได้ โดยไม่ต้องเช็กเวอร์ชันใหม่ก่อนรันฟีเจอร์เดียวกันนี้ใช้กับ
uvxได้ด้วย (uvx --offline ...)เข้าใจว่าโครงสร้างมันเป็นแบบที่ ถ้าจะใช้ dependency หรือ venv อย่างน้อยต้องรันตอนมีอินเทอร์เน็ตสักหนึ่งครั้งก่อน หลังจากนั้นถึงจะใช้งานแบบออฟไลน์ได้
ช่วงนี้รู้สึกว่าฟีเจอร์หลายอย่างใน ecosystem ของ Python เริ่มทำงานเข้ากันได้ดีขึ้นเรื่อยๆ
ตอนนี้กำลังเริ่มทำเครื่องมือรายงาน/วินิจฉัยที่ reproducible และเหมาะให้ทีมอื่นใช้งานต่อ โดยใช้ Marimo ร่วมกับ uv script dependencies
ฟีเจอร์นี้ของ uv คือสิ่งที่ชอบที่สุด และเป็นเหตุผลที่ย้ายมาใช้ uv
มีสคริปต์ git-hooks หลายตัวที่แต่ละตัวมี dependency ของตัวเอง และไม่อยากติดตั้งพวกนั้นลงใน main venv
แค่เพิ่ม
#!/usr/bin/env -S uv run --script --python 3.13บรรทัดเดียว ก็แค่บอกทีม dev ให้brew install uvแล้วก็ใช้งานจากในสคริปต์ได้เลย โดยไม่ต้องสร้าง venv แยกมีใครพอรู้ไหมว่าทำไมต้องใช้แฟลก
-Sบนสภาพแวดล้อม BSD ของผม
/usr/bin/env -S uv run --python 3.11 pythonกับ/usr/bin/env uv run --python 3.11 pythonต่างก็เปิด Python shell เหมือนกัน เลยรู้สึกว่าได้ผลไม่ต่างกันอ่านคู่มือของ env แล้วก็ยังไม่เข้าใจชัดเจน ถ้าใครมีข้อมูลที่เป็นประโยชน์ก็อยากฟัง
(
-Sในที่นี้ทำหน้าที่แยกอาร์กิวเมนต์ตามช่องว่าง)เดิมทีเพราะ UV เลยวางแผนจะย้ายงาน migration ขนาดใหญ่ของ Python ไปทำด้วย golang แต่ตอนนี้ UV ช่วยลดขอบเขตการย้ายได้มาก
โดยเฉพาะงานเล็กๆ ในรูปแบบสคริปต์ ไม่จำเป็นต้องย้ายอีกต่อไป
มั่นใจว่านี่คือฟีเจอร์ระดับ ‘killer feature’ จริงๆ
ถ้ามี Pytorch อยู่ใน dependency วิธีนี้อาจมีข้อจำกัดอยู่บ้าง
แม้ Uv จะมีการรองรับแบบบูรณาการสำหรับ Pytorch ที่ดีมาก แต่แค่จาก header ของสคริปต์อย่างเดียว ยังไม่มีวิธีเลือก wheel index ที่เหมาะที่สุดได้อย่างชัดเจน (เช่น CPU, CUDA, ROCm)
อยากให้ VS Code มองเห็น venv ที่ uv สร้างอัตโนมัติได้ง่ายกว่านี้
ตอนนี้ Python extension แสดงเส้นแดงกับ import ของ third-party ทั้งหมด
วิธีแก้ชั่วคราวคือต้องไปหา path ของ venv ในไดเรกทอรี Cache ของ uv แล้วลงทะเบียนเอง แต่ถ้า venv ถูกสร้างใหม่บ่อยๆ ก็ต้องทำซ้ำเรื่อยๆ ซึ่งยุ่งยากมาก
uv python find --script "${filePath}"เพื่อหา path ของ env ได้ตอนนี้กำลังพัฒนาส่วนขยายสำหรับ VS Code ที่จะตรวจจับและเปิดใช้งานฟีเจอร์นี้ให้อัตโนมัติ
ชอบฟีเจอร์นี้ของ UV มาก
แม้แต่ jupyter notebook ก็รันแบบ one-liner ได้โดยไม่ต้องติดตั้งแยก ดังนี้
ทุกอย่างจะถูกติดตั้งลงใน virtual environment ชั่วคราว แล้วหลังจากนั้นก็ถูกจัดเก็บออกไปอย่างสะอาด
ถ้ารันภายในโปรเจกต์ มันจะตรวจจับ dependency ของโปรเจกต์นั้นให้อัตโนมัติด้วย
แต่อันที่จริงมันไม่ได้ถูกจัดเก็บออกไปแบบ ‘สะอาด’ ทั้งหมด เพราะโฟลเดอร์ cache ของ uv อาจโตขึ้นเรื่อยๆ ได้
ผมเองก็ใช้บ่อยในรูปแบบ
uv run --with ipython --with boto3 ipythonและช่วยประหยัดเวลาได้มากจริงๆไม่นานมานี้เจอ issue เล็กๆ เกี่ยวกับ
uv runถ้ารันสคริปต์จากนอกโฟลเดอร์โปรเจกต์ มันจะค้นหา
pyproject.tomlจาก current working directory ไม่ใช่จากตำแหน่งของไฟล์สคริปต์จริงเพราะฉะนั้นสคริปต์ที่เก็บ dependency ไว้ใน
pyproject.tomlอาจทำงานไม่ถูกต้อง หากรันจากภายนอกในลักษณะuv run path/to/my/script.pyปัญหานี้แก้ได้ด้วยการใช้ inline dependencies เสมอ หรือใช้พารามิเตอร์
--projectแต่ก็ต้องพิมพ์ path ของสคริปต์ซ้ำสองครั้ง ทำให้ไม่สะดวกตัว uv เองยอดเยี่ยมมาก แต่พฤติกรรมเล็กๆ ตรงนี้ค่อนข้างน่าหงุดหงิด
ใช้งาน uv-specific shebang และวิธีใส่ dependency ในสคริปต์อยู่แล้ว และพอใจมาก
ยิ่งไปกว่านั้นยังประทับใจกับการที่สามารถสร้าง lock file สำหรับสคริปต์เดียวได้ด้วยคำสั่ง
uv lock --script example.pyPython packaging ดำเนินมากว่า 20 ปีแล้ว แต่ก็น่าทึ่งที่ประสบการณ์ที่เป็นธรรมชาติแบบนี้เพิ่งมาถึงตอนนี้
ในองค์กรของเรา ยังใช้ lockfile dependency สำหรับสแกนด้วย
trivy fs uv.lockเพื่อช่วยป้องกันไม่ให้รันโค้ดที่มี CVE ที่รู้จักอยู่ด้วย