แก้ error “externally-managed-environment” ใน Python

รัน pip install ตามปกติ แล้วเจอข้อความยาวๆ แบบนี้แทนที่จะติดตั้งสำเร็จ

error: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
    python3-xyz, where xyz is the package you are trying to
    install.

    If you wish to install a non-Debian-packaged Python package,
    create a virtual environment using python3 -m venv path/to/venv.

error externally-managed-environment ไม่ใช่ bug ของ pip หรือ Python แต่เป็นกลไกป้องกันที่ตั้งใจใส่มาโดยเฉพาะ เริ่มบังคับใช้ตั้งแต่ Python 3.11 เป็นต้นไปในหลายระบบ บทความนี้จะอธิบายว่าทำไมถึงมี error นี้ และวิธีแก้ที่ถูกต้องแต่ละแบบ

สาเหตุที่แท้จริง (PEP 668)

Python ที่ติดตั้งมากับระบบปฏิบัติการ (เช่น Python ที่มากับ Ubuntu, Debian หรือ macOS ผ่าน Homebrew) ไม่ได้เป็นแค่ “Python สำหรับเขียนโปรแกรมของคุณ” เท่านั้น แต่ OS เองก็ใช้ Python ตัวเดียวกันนี้รันเครื่องมือระบบหลายตัวด้วย ถ้าคุณ pip install package บางตัวทับ system Python ตรงๆ แล้วดันไปทับเวอร์ชันที่เครื่องมือระบบต้องใช้พอดี อาจทำให้ระบบปฏิบัติการพังได้

เพื่อป้องกันปัญหานี้ Python เพิ่มมาตรฐานที่ชื่อ PEP 668 (“Marking Python base environments as externally managed”) ระบบที่ทำตามมาตรฐานนี้จะสร้างไฟล์ EXTERNALLY-MANAGED ไว้ในโฟลเดอร์ Python บอก pip ว่า “environment นี้มีคนอื่นดูแลอยู่ (คือตัวจัดการแพ็กเกจของ OS เช่น apt) อย่าเพิ่งติดตั้งอะไรทับตรงๆ” พอ pip เจอไฟล์นี้ก็จะปฏิเสธการติดตั้งทันทีพร้อมโชว์ error ที่เห็น

พูดง่ายๆ error นี้คือ Python กำลังปกป้องระบบปฏิบัติการของคุณเองจากอุบัติเหตุที่เคยเกิดขึ้นบ่อยในอดีต ไม่ใช่ปัญหาที่ต้อง “ฝ่า” ให้ผ่านไปแบบไม่คิดหน้าคิดหลัง ก่อนหน้านี้เคยมีเคสจริงที่ผู้ใช้ pip install package บางตัวแล้วไปเขียนทับเวอร์ชันที่เครื่องมือระบบ (เช่น apt เอง หรือโปรแกรมจัดการแพ็กเกจอื่น) ต้องพึ่งพา จนทำให้คำสั่งพื้นฐานของระบบใช้งานไม่ได้ ต้องติดตั้งระบบปฏิบัติการใหม่ทั้งหมด นี่คือเหตุผลที่ทีม Python ตัดสินใจใส่กลไกป้องกันนี้เข้ามา

วิธีที่ 1: ใช้ virtual environment (แนะนำที่สุด)

วิธีที่ถูกต้องและเป็นมาตรฐานที่สุดคือสร้าง virtual environment แยกต่างหากสำหรับแต่ละโปรเจกต์ วิธีนี้ไม่กระทบ system Python เลย เพราะ package ที่ติดตั้งจะอยู่แค่ในโฟลเดอร์ venv ของโปรเจกต์นั้นเท่านั้น

python3 -m venv .venv
source .venv/bin/activate
pip install <ชื่อ package>

บน Windows ขั้นตอน activate ต่างออกไปเล็กน้อย

python -m venv .venv
.venv\Scripts\activate
pip install <ชื่อ package>

หลัง activate แล้ว prompt ใน terminal จะเปลี่ยนไปมีคำว่า (.venv) นำหน้า บอกว่ากำลังอยู่ใน virtual environment แล้ว ตอนนี้ pip install จะทำงานได้ปกติไม่เจอ error อีก เพราะ venv ไม่ถือเป็น “externally managed” — เป็นพื้นที่ที่คุณควบคุมเองทั้งหมด

ทุกครั้งที่กลับมาทำงานโปรเจกต์นี้ใหม่ ต้อง source .venv/bin/activate (หรือ .venv\Scripts\activate บน Windows) ก่อนเสมอ ถ้าต้องการออกจาก virtual environment พิมพ์ deactivate

วิธีที่ 2: ใช้ pipx สำหรับโปรแกรม command-line

ถ้า package ที่ต้องการติดตั้งเป็นโปรแกรม command-line ที่ต้องการเรียกใช้จากทุกที่ (ไม่ใช่ library ที่ import ในโค้ด) เช่น black, httpie หรือ poetry pipx เหมาะกว่า venv มาก เพราะสร้าง virtual environment แยกให้แต่ละโปรแกรมอัตโนมัติ แต่ยังเรียกใช้คำสั่งได้จากทุกที่เหมือนติดตั้งแบบ global

python3 -m pip install --user pipx
pipx install <ชื่อโปรแกรม>

หลังติดตั้งผ่าน pipx แล้ว เรียกคำสั่งนั้นได้ทันทีจาก terminal ไหนก็ได้ โดยที่แต่ละโปรแกรมแยก dependency ไม่ปนกัน ไม่กระทบ system Python เลยเหมือนกับ venv

วิธีที่ 3: ใช้ –break-system-packages (ใช้เมื่อจำเป็นจริงๆ เท่านั้น)

pip เปิดทางให้ข้าม safety check นี้ได้ด้วย flag --break-system-packages แต่ ไม่แนะนำให้ใช้เป็นวิธีหลัก เพราะเป็นการยอมรับความเสี่ยงที่ package ใหม่จะไปทับเวอร์ชันที่เครื่องมือระบบต้องใช้พอดี

pip install --break-system-packages <ชื่อ package>

ใช้วิธีนี้เฉพาะกรณีทดสอบเร็วๆ บนเครื่องที่ไม่ใช่เครื่อง production หรือรู้แน่ชัดแล้วว่า package ที่ติดตั้งไม่ชนกับเครื่องมือระบบแน่นอน ถ้าเป็นงานที่ทำประจำ ควรกลับไปใช้ venv หรือ pipx แทนเสมอ

วิธีที่ 4: ติดตั้งผ่าน apt แทน pip (เฉพาะ Debian/Ubuntu)

ถ้า package ที่ต้องการมีอยู่ใน apt repository อยู่แล้ว บางครั้งวิธีที่ตรงไปตรงมาที่สุดคือติดตั้งผ่าน apt แทน pip ตรงๆ ตามที่ error message แนะนำไว้เลย

sudo apt install python3-<ชื่อ package>

วิธีนี้ปลอดภัยเพราะ apt รู้จักจัดการ dependency ร่วมกับเครื่องมือระบบอื่นอยู่แล้ว แต่ข้อจำกัดคือ package บน apt มักเป็นเวอร์ชันเก่ากว่าที่อยู่บน PyPI (แหล่งที่ pip ดึงมา) และบาง package อาจไม่มีให้บน apt เลย

ทำงานกับทีมด้วย requirements.txt ผ่าน venv

ข้อดีอีกอย่างของการใช้ venv คือทำงานร่วมกับทีมได้ง่ายขึ้นมาก เพราะสามารถบันทึกรายชื่อ package ทั้งหมดที่ใช้ในโปรเจกต์ไว้ในไฟล์ requirements.txt แล้วให้คนอื่นติดตั้งชุดเดียวกันได้ทันที

หลังติดตั้ง package ที่ต้องการใน venv ครบแล้ว บันทึกรายชื่อทั้งหมดด้วย

pip freeze > requirements.txt

เมื่อเพื่อนร่วมทีมโคลนโปรเจกต์ไปเปิดที่เครื่องอื่น แค่สร้าง venv ใหม่แล้วติดตั้งจากไฟล์นี้ทีเดียว

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

ทุกคนในทีมจะได้ package เวอร์ชันเดียวกันเป๊ะ ลดปัญหา “ทำงานบนเครื่องฉันได้ปกติ แต่พังบนเครื่องเพื่อน” ที่มักเกิดจากเวอร์ชัน package ไม่ตรงกัน และยังช่วยให้ deploy ขึ้นเซิร์ฟเวอร์ง่ายขึ้นด้วย เพราะรู้แน่ชัดว่าต้องติดตั้ง package เวอร์ชันไหนบ้างจากไฟล์เดียว ไม่ต้องเดาหรือไล่ติดตั้งทีละตัวเอง

เปรียบเทียบว่าควรใช้วิธีไหน

สถานการณ์วิธีที่แนะนำ
กำลังพัฒนาโปรเจกต์ Python (มี requirements.txt)venv
ต้องการเครื่องมือ command-line ใช้ทั่วเครื่องpipx
package มีอยู่แล้วใน apt และไม่ต้องการเวอร์ชันล่าสุดapt install
ทดสอบเร็วๆ บนเครื่องทดลองที่ไม่ใช่ production–break-system-packages (ชั่วคราวเท่านั้น)

คำถามที่พบบ่อย

ทำไมก่อนหน้านี้ไม่เคยเจอ error นี้เลย เพราะ PEP 668 เริ่มถูกนำมาใช้จริงในระบบปฏิบัติการตั้งแต่ราวๆ Ubuntu 23.04, Debian 12 และ macOS ที่ใช้ Python จาก Homebrew เวอร์ชันใหม่ ถ้าเพิ่งอัปเดตระบบปฏิบัติการหรือเปลี่ยนเครื่องใหม่มา มีโอกาสสูงที่จะเจอ error นี้เป็นครั้งแรก แม้จะเขียน Python มานานแล้วก็ตาม

ใช้ venv ทุกโปรเจกต์เลยจะยุ่งยากไปไหม ไม่ยุ่งยากอย่างที่คิด เป็นแนวทางที่แนะนำอยู่แล้วในวงการ Python มานานก่อน PEP 668 ด้วยซ้ำ เพราะแต่ละโปรเจกต์มักต้องการเวอร์ชัน package ต่างกัน การแยก venv ต่อโปรเจกต์ช่วยป้องกันปัญหาเวอร์ชันชนกันระหว่างโปรเจกต์ได้ด้วย ไม่ใช่แค่แก้ error นี้อย่างเดียว

Conda หรือ Anaconda เจอปัญหานี้ไหม ปกติไม่เจอ เพราะ Conda จัดการ environment ของตัวเองแยกจาก system Python อยู่แล้วโดยธรรมชาติ ถ้าใช้ conda install หรือ pip install ภายใน conda environment ที่ activate อยู่ จะไม่ถือว่าเป็น externally-managed-environment เพราะ Conda สร้าง Python interpreter ของตัวเองแยกออกมาต่างหากตั้งแต่แรก ไม่ได้ใช้ system Python ร่วมกับเครื่องมือระบบเลย

ลบไฟล์ EXTERNALLY-MANAGED ทิ้งเพื่อแก้ปัญหาถาวรได้ไหม ทำได้ในทางเทคนิค แต่ไม่แนะนำอย่างยิ่ง เพราะเท่ากับปิดกลไกป้องกันทั้งหมดถาวร มีความเสี่ยงสูงที่จะทำให้เครื่องมือระบบพังในอนาคตโดยไม่รู้ตัว ควรใช้ venv หรือ pipx ตามคำแนะนำในบทความนี้แทนเสมอ

pip install –user ช่วยแก้ปัญหานี้ได้ไหม ช่วยได้ในบางระบบ เพราะติดตั้งไปที่โฟลเดอร์ของผู้ใช้แทนที่จะทับ system-wide แต่บางระบบที่บังคับใช้ PEP 668 เข้มงวดก็ยังปฏิเสธแม้ใช้ --user อยู่ดี ทางที่ปลอดภัยและแน่นอนที่สุดยังคงเป็น venv

ต้องสร้าง venv ใหม่ทุกครั้งที่เปิดโปรเจกต์เดิมไหม ไม่ต้อง สร้างครั้งเดียวต่อโปรเจกต์ก็พอ โฟลเดอร์ .venv จะถูกเก็บไว้ในเครื่องถาวรพร้อม package ที่ติดตั้งไปแล้วทั้งหมด ครั้งต่อไปแค่ activate เข้าไปใช้งานต่อได้เลยไม่ต้องติดตั้งใหม่ ยกเว้นย้ายไปทำงานคนละเครื่องถึงจะต้องสร้างใหม่และติดตั้งจาก requirements.txt อีกครั้ง โฟลเดอร์ .venv เองไม่ควร commit เข้า git ด้วย ควรใส่ไว้ใน .gitignore แล้วให้ทุกคนสร้างของตัวเองจาก requirements.txt แทน

VS Code รู้จัก venv ที่สร้างไว้เองไหม รู้จัก ถ้าเปิดโฟลเดอร์โปรเจกต์ที่มี .venv อยู่ข้างใน VS Code จะเสนอให้เลือก interpreter เป็น venv นั้นโดยอัตโนมัติผ่านแถบด้านล่างขวา หรือเลือกเองผ่าน Command Palette พิมพ์ Python: Select Interpreter แล้วเลือกตัวที่อยู่ในโฟลเดอร์ .venv

สรุป

error externally-managed-environment เกิดจากมาตรฐาน PEP 668 ที่ป้องกันไม่ให้ pip ติดตั้ง package ทับ system Python โดยไม่ตั้งใจ ไม่ใช่ bug และไม่ควรมองว่าเป็นสิ่งกีดขวางที่ต้องข้ามให้พ้นแบบไม่คิด

เรียงลำดับวิธีแก้ที่แนะนำ

  1. venv — สำหรับโปรเจกต์ที่กำลังพัฒนา เป็นวิธีมาตรฐานที่สุด
  2. pipx — สำหรับโปรแกรม command-line ที่ต้องเรียกใช้ทั่วเครื่อง
  3. apt install — ถ้า package มีอยู่ใน apt repository แล้ว
  4. –break-system-packages — ใช้เฉพาะกรณีจำเป็นจริงๆ บนเครื่องทดลองเท่านั้น

สร้างนิสัยใช้ venv เป็นค่าเริ่มต้นทุกโปรเจกต์ตั้งแต่ตอนนี้ จะไม่ต้องเจอ error นี้อีกเลยในระยะยาว และยังได้ประโยชน์เพิ่มเติมเรื่องจัดการเวอร์ชัน package ให้เป็นระเบียบขึ้นไปพร้อมกันด้วย

1 thought on “แก้ error “externally-managed-environment” ใน Python”

Leave a Comment