מנהל חבילות לפייתון – pipenv

מנהל החבילות לפייתון pipenv הוא חלופה מודרנית וטובה יותר ל pip+venv.

במאמר הקודם כתבתי על היסודות של מנהל החבילות של פייתון (לפחות זה מגרסה 3 וצפונה) כלומר על הבסיס pip ו-venv וקובץ ה-requirements.txt. מומלץ לקרוא אותו לפני מאמר זה. במאמר הזה אני הולך לדבר על האלטרנטיבה הראשונה של pip + venv הלא הוא pipenv. בפוסטים הבאים אדבר על אחרים. כיוון שיש רבים כחול שעל שפת הים 😇

אז בגדול מה הבעיה ש-pipenv באה לפתור? יש כמה בעיות עם השימוש הסטנדטי של pip + venv. הראשונה היא ניהול התלויות. requirements.txt גרם למה שנקרא dependency hell – כי ברגע שיש לנו פרויקט אמיתי עם כמה וכמה חבילות ולכל אחת מהן יש את התלויות שלה – יש סיכוי שיהיו קונפליקטים. איזה קונפליקטים? למשל אני משתמש בשתי חבילות: A ו-B. חבילה A משתמשת בחבילה C מגרסה 1.2. חבילה B משתמשת בחבילה C מגרסה 2.1. באיזו גרסה אני יכול להשתמש? אם אני אבחר בחבילה C מגרסת 2.1, אז חבילה A תצרח. אם אני אבחר בחבילה C מסוג 1.2. חבילה B תתפוצץ.

בעיה נוספת שיש שלנהל ולהבין את קובץ requirements.txt זה מאוד מאוד בעייתי. לכן תבינו איזו חבילה משתמשת בחבילת משנה ואיזה גרסאות. לא נעים. גם אם אנו רוצים לעדכן את החבילות צריך לשחק בכל מיני משחקים של למחוק את הקובץ requirements.txt ולמחוק אותו מחדש. בלעכס. מה קורה אם למשל אין לי בעיה לעדכן minors ו-patch אבל יש לי בעיה לעדכן majors? (אם אין לכם מושג על מה אני מדבר – זה הזמן לקרוא על Semantic versions!). מאוד בעייתי לנהל את זה כך.

מעבר לעובדה שבאמת קשה לעדכן ולשדרג חבילה אחת מבין כל החבילות ואין לנו ממש כלי ניהול ב-CLI לתפעל את כל הדבר הזה.

אז בשנת 2017, קנת׳ ריץ, שהוא פייתוניסט מאוד ידוע (יצר גם את requests!) יצר את pipenv. שהוא בעצם מנהל חבילות, שעובד בדומה ל-pip ופותר את כל הבעיות האלו. אז בואו נראה איך מתקינים ועובדים עם pipenv ואז נדבר על איך הוא פותר את הבעיות שלעיל ואיזה בעיות אחרות נוצרות.

אז איך מתקינים את pipenv?

למשתמשי לינוקס וחלונות יש הוראות בדוקומנטציה הרשמית של pipenv. זה די פשוט. במק מתקינים עם brew באמצעות הפקודה

brew install pipenv

והנה טיפ חשוב ממני: מומלץ להוסיף ל-zshrc את משתנה הסביבה:

export PIPENV_VENV_IN_PROJECT=1

על מנת לוודא שה-venv יווצר בתיקית הפרויקט ולא בתיקית המשתמש. איך עושים את זה? אם יש לכם zsh במק אז פשוט nano ~/.zshrc ולהוסיף את הטקסט לעיל בסוף הקובץ. לשמור ולפתוח מחדש את הטרמינל. בחלונות עושים זאת כך.

איך מוודאים שיש לנו את pipenv? מקלידים pipenv –version.

pipenv --version                                                                           ok
pipenv, version 2023.2.4

וזהו. אפשר להתחיל לעבוד.

הפעלה ראשונית

ברגע שאני נכנס לפרויקט פייתוני לראשונה. אני אקליד pipenv shell על מנת להכנס ל״סביבה הוירטואלית״. הסביבה שבה הפרויקט שלי חי והמודולים שלי נמצאים בה. אם זו הפעם הראשונה שאני עושה את זה, אז תיווצר תיקית venv. בתיקית הפרויקט (אלא אם כן שכחתי להכניס את export PIPENV_VENV_IN_PROJECT=1)

תיקית venv בפרויקט שלי.

בדרך כלל נקבל גם אינדיקציה ויזואלית בטרמינל שאני בקונטקסט של pipenv shell. למשל סוגריים עגולות. אני משתמש ב-powerlevel10k אז אני אראה את האינדיקציה בצד. בצילום המסך הזה אני מראה איפה בדיוק. ליד הספרה 1, יש את המצב הרגיל של הטרמינל. ליד הספרה 2 יש את המצב שמראה לי שאני נמצא בקונטקסט של הפרויקט שלי.

 ok | python-demo py זו האינדיקציה שאני נמצא ב-shell.

כשאני רוצה לצאת מהקונטקסט, אני פשוט מקליד exit. או סוגר את הטרמינל. אם אני משתמש ב-pycharm אז הדברים האלו קורים אוטומטית אגב.

כשאני נמצא ב-shell, אני יכול להריץ את הפרויקט שלי בקונטקסט והתלויות.

קובץ Pipfile

התקנה נעשית באמצעות pipenv install ואז שם החבילה. כשאני מתקין, קורה משהו מדהים – נוצר קובץ בשם Pipfile. בקובץ יש מידע על החבילות התלויות. הנה דוגמה של קובץ כזה:

[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
requests = "*"

[dev-packages]

[requires]
python_version = "3.11"

אפשר לשים לב שיש לי יכולת לבצע התקנה של חבילות dev כלומר חבילות שמשתמשים בהן אך ורק בפיתוח. למשל pylint. את הלינטר אני לא צריך בסביבת פרודקשן/סביבה חיה אז אני אתקין אותו בסביבת dev. איך? באמצעות הפלאג d- אחרי שם החבילה. למשל:

pipenv install pylint -d

אם נשתמש בפלאג הזה נראה שקובץ ה-pipfile התעדכן:

[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
requests = "*"

[dev-packages]
pylint = "*"

[requires]
python_version = "3.11"

עוד משהו מעניין שיש הוא גרסת הפייתון. בגדול זו גרסת הפייתון שיש לנו באופן דיפולטיבי במחשב. אנו יכולים לבחור איזו גרסת פייתון שאנו רוצים אבל בתנאי שהיא תהיה שווה או נמוכה לזו שיש במכונה. אם אנו מנהלים את גרסת הפייתון שלנו באמצעות pyenv (כתבתי על pyenv פה) זה מתחבר יפה מאוד. אם אני רוצה להשתמש בגרסת האחרת. כל מה שאני צריך לעשות זה לשדרג את גרסת הפייתון בקובץ ולהקליד:

pipenv install --python

אבל מה שמעניין הוא לא ה-Pipfile אלא ה-Pipfile.lock שהוא הקובץ המכיל והמנהל את כל התלויות. אם תכנסו אליו תראו שמדובר ב-JSON גדול המכיל חתימות, גרסאות מדויקות וכו׳ וכו׳. זו התמונה המדויקת של התלויות שלנו במכונה. אם אני רוצה לשחזר את הקוד שלי במדויק במכונה אחרת אני צריך להתקין ממנו.

אנו מכניסים גם את Pipfile.lock וגם את Pipfile לקוד המקור שלנו. אבל לא את תיקית venv.

התקנה מ-Piplock

על מנת להתקין בדיוק את מה שיש ב-Piplock במדויק אנו מקלידים:

pipenv sync

לחבילות הרגילות

או

pipenv sync -d

לחבילות הרגילות והחבילות המשמשות לפיתוח.

ואז כל התלויות המדויקות והתת תלויות והתת תת תת תלויות ובעצם כל הסביבה כולל גרסת הפייתון מותקנות בסביבה הוירטואלית של הפרויקט. אני יכול להקליד pipenv shell ולהתחיל לעבוד.

נעילת גרסאות וניהול גרסאות

הכוח האמיתי של pipenv הוא בניהול הגרסאות והתלויות ואת זה אנו מנהלים עם Pipfile ו-Pipfile.lock.

כשאנו מתקינים תוכנה שמשתמשת ב-pipenv, אנו מתקינים את כל התלויות והסביבה עם pipenv sync והפקודה מתקינה בדיוק מה שמפורט ב-Pipfile.lock. אבל איך אני מעדכן את מה שיש ב-Pipfile.lock? הרי חבילות המשנה שלי מתעדכנות לפעמים. אז מה עושים? אפשר לעדכן את התלויות שלי לגרסה החדשה ביותר באמצעות הפקודה:

pipenv update

אבל מה קורה אם אני לא רוצה לעדכן חבילה מסוימת לגרסה החדשה ביותר? אם נניח כל הפרויקט שלי בנוי על requests ואני לא רוצה לעדכן גרסת מייג׳ור ששוברת דברים אלא רק מיינור. או patch. מה עושים? פה אני צריך לעדכן את מה שיש בקובץ ה-Pipfile. כי אם יש שם * ליד שם החבילה שאני משתמש בה, זה אומר מי שבא ברוך הבא:

requests = "*"

אם אני רוצה רק את גרסת המייג׳ור 1 – אז אני יכול להשתמש בסימון הסמנטי הידוע ~ שמסמן שאפשר לעדכן patch ו-minor אבל לא major:

requests = "~=1.2"

אפשר כמובן מראש להתקין את הגרסה עם הסימון הסמנטי. למשל:

pipenv install requests~=1.2

כשאני אעשה pipenv update, הוא יכבד את הסימון הסמנטי ויעדכן – אבל את מה שאמרתי לו לעדכן.

אני יכול בכל נקודה בזמן לדעת מה מותקן לי באמצעות הפקודה pipenv graph.

הסרה

ניתן לבצע הסרה של חבילה באמצעות pipenv uninstall.

תלויות מתנגשות

pipenv גם עוזר לנו לפתור את ה-dependency hell. אם יש לנו חבילה שרוצה גרסה מסוימת של חבילה ספציפית וחבילה אחרת שרוצה גרסה אחרת של אותה חבילה ספציפית. המנוע מאחורי pipenv ינסה למצוא גרסת ביניים שלא תגרום לתקלות בשתי החבילות שרוצות את אותה חבילה ספציפית או לפחות יתן לנו הודעות שגיאה שבה די ברור מה הבעיה:

...
Installing dependencies from Pipfile.lock (123456)...
An error occurred while installing package X==1.0.0!
...
Incompatible dependencies found:

Package Y (required version >=2.0.0) is not compatible with package Z (required version <=1.0.0)

Consider using the `--skip-lock` option to install dependencies without using a lockfile.

סיכום

חפירה ארוכה מאוד על יתרונות ה-pipenv לעומת השימוש המסורתי והותיק ב-pip + venv ובאמת pipenv במשך תקופה ארוכה הפך להיות מנהל החבילות הפופולרי ביותר בפייתון ובאמת עדיף להשתמש בו על פני מה שקדם לו. אבל כמובן בפייתון כמו בפייתון, התהפוכות רבות ויש לו כמה וכמה יורשים שיש להם יתרונות משלהם. למרות שרובם עובדים קרוב אליו. בפוסט הבא נלמד על פואטרי. מנהל חבילות פופולרי שהוא הסטנדרט בפייתון.

פוסטים נוספים שכדאי לקרוא

צילום מסך של סוואגר
יסודות בתכנות

openAPI

שימוש בתשתית הפופולרית למיפוי ותיעוד של API וגם הסבר בסיסי על מה זה API

פתרונות ומאמרים על פיתוח אינטרנט

נגישות טכנית – פודקאסט ומבוא

פרק בפודקאסטעל נגישות בעברית שצולל לכלים האוטומטיים ולפן המאד מאד טכני של הנגישות.

פתרונות ומאמרים על פיתוח אינטרנט

יישום של nonce על מנת להגן מפני התקפות injection

בפוסט הקודם הסברתי על hash עם CSP על משאבי inline – שזה נחמד ומעולה אבל פחות ישים בעולם האמיתי שבו בדרך כלל התוכן ה-inline (בין

גלילה לראש העמוד