ניהול חבילות וסביבות בפייתון - pipenv


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

בשנת 2018 נוצר הכלי pipenv שמטרתו לפתור בעיות נפוצות ב pip, ב venv ובעבודה עם קובץ ה requirements עליו דיברנו בפוסט הקודם.

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

מה הבעיה עם requirements.txt ואיך pipenv פותר לנו אותה?

אז דיברנו על היכולות המרשימות שנותן קובץ ה requirements ובאיזו קלות ניתן למלא אותו עם pip freeze.
אבל לצערנו הוא דורש לא מעט התעסקות ידנית בפרוייקטים מתקדמים יותר.

דמיינו את המקרה הבא:
אנחנו מתקינים את הספריה requests בגרסתה האחרונה (2.24.0) וכשאנחנו מתקינים אותה מותקנת לנו באופן אוטומטי גם תת-תלות (sub-dependency) בשם urllib3 בגרסה 1.25.11.

עכשיו בואו נניח שהתעדכנה גרסה של התגלתה פרצת אבטחה ב urllib3 והם שחררו את גרסה 1.25.12.
כעת, הקוד שלנו בפרודקשן רץ מול הגרסה הישנה. עלינו לעדכן את הגרסה כמה שיותר מהר.
הבעיה היא שאת זאת נצטרך לעשות באופן ידני משום שקובץ ה requirements כובל אותנו לגרסה ספציפית אם מילאנו אותו עם pip freeze.

קיימת בעיה נוספת שיכולה להיווצר לנו משימוש ב-pip. נניח ואנחנו עובדים מול החבילה dependency_a שיש לה תלות ב dependency_c בגרסה שגדולה מ 1.0.0 ועם dependency_b שיש לה תלות ב dependency_c בגרסה שקטנה מ2.0.0.
כלומר, הפרויקט שלנו צריך את החבילה dependency_c בגרסה בין 1.0.0 ל 2.0.0.

מה שיקרה בפועל - זה שאם אנחנו נבוא להתקין את dependency_a והוא יתקין לנו את dependency_c בגרסתו האחרונה - נגיד 3.0.0 (כי התנאי הוא גדול מ 1.0.0) ואז כשנבוא להתקין את dependency_b אנחנו נכשל כי מותקנת לנו כבר גרסה שלא עונה על התנאי שלנו.

גם כאן הפתרון הוא לעשות pinning ידני והוסיף לקובץ ה requirements.txt שהתלות dependency_c צריכה להיות מותקנת בגרסאות שבין 1.0.0 ל 2.0.0. אבל גם במקרה הזה התערבנו ידנית בקובץ על מנת להצליח לבצע את ההתקנה.

קודם כל, מה זה pipenv?

pipenv הוא חבילת פייתון ו-CLI tool שעוטף את pip ואת virtualenv תחת ממשק אחד, פותר כמה בעיות, ומאפשר כל מיני פיצ׳רים חדשים ונוחים.

מהרגע שהתקנו את pipenv באמצעות pip (עם הפקודה pip install pipenv) אנחנו יכולים לשכוח מהשימוש בpip.

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

tomers-MacBook-Pro:trypipenv tomer$ pipenv install requests # פקודת ההתקנה

Warning: the environment variable LANG is not set!

We recommend setting this in ~/.profile (or equivalent) for proper expected behavior.

Creating a virtualenv for this project… יוצר לנו סביבה באופן אוטומטי pipenv

Pipfile: /private/tmp/trypipenv/Pipfile

Using /usr/local/bin/python3 (3.8.6) to create virtualenv…

⠙ Creating virtual environment...created virtual environment CPython3.8.6.final.0-64 in 756ms

  creator CPython3Posix(dest=/Users/qa/.local/share/virtualenvs/trypipenv-67YrnDNG, clear=False, global=False)

  seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/Users/qa/Library/Application Support/virtualenv)

    added seed packages: pip==20.2.4, setuptools==50.3.2, wheel==0.35.1

  activators BashActivator,CShellActivator,FishActivator,PowerShellActivator,PythonActivator,XonshActivator


✔ Successfully created virtual environment! 

Virtualenv location: /Users/qa/.local/share/virtualenvs/trypipenv-67YrnDNG

Creating a Pipfile for this project… # pipfile יצירת הקובץ

Installing requests… # התקנת החבילה

Adding requests to Pipfile's [packages]… # pipfile הוספת המידע על החבילה ל

✔ Installation Succeeded

Pipfile.lock not found, creating… # pipfile.lock יצירת הקובץ

Locking [dev-packages] dependencies…

Locking [packages] dependencies…

Building requirements...

Resolving dependencies...

✔ Success! 

Updated Pipfile.lock (fbd99e)!

Installing dependencies from Pipfile.lock (fbd99e)… # התקנת תתי התלויות

  🐍   ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 0/0 — 00:00:00

To activate this project's virtualenv, run pipenv shell.

Alternatively, run a command inside the virtualenv with pipenv run.

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

הקובץ pipfile שנוצר (אוטומטית) הוא התחליף ל requirements.txt שעלינו ליצור ידנית עם pip freeze.
ניתן לפתוח את הקובץ ולראות את המבנה שלו.

[[source]]

name = "pypi"

url = "https://pypi.org/simple"

verify_ssl = true


[dev-packages]


[packages]

requests = "*"


[requires]

python_version = "3.8"

כאשר נבצע את הפקודה pipenv uninstall requests נוכל לראות התלות הוסרה מ-pipfile.

בילד דטרמיניסטי

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

בדיוק לשם כך נוצר הקובץ pipefile.lock.

pipfile.lock הוא קובץ שנוצר אוטומטית ומשתנה אוטומטית ואנחנו לא אמורים לגעת בו לאורך העבודה עם הפרויקט. לאחר שביצענו את הבדיקות שלנו ואנחנו יודעים שהחבילות שמותקנות אצלנו הן החבילות אותן אנחנו רוצים בפרודקשן, כל שנצטרך לעשות זה לנעול את הקובץ באמצעות הפקודה pipenv lock.

אם נכנס ל pipfile.lock נראה שיש בו הרבה יותר מידע. הוא מכיל את כל תתי התלויות, את הגרסאות הספציפיות שלהן ואפילו את חתימות ה sha256 שלהן.

ובכך, pipfile.lock מאפשר לנו בילד דטרמניסטי. כל מה שנצטרך לעשות על מנת להתקין את הפרויקט שלנו בסביבה אחרת זה להריץ את הפקודה pipenv install --ignore-pipfile.

תהליך הפיתוח יראה כך:
במהלך הפיתוח אנחנו נתקין חבילות ומשום שב pipfile אנחנו לוקחים תמיד את הגרסה האחרונה של החבילה שהתקנו (במידה ולא ציינו אחרת) אנחנו נהיה תמיד מעודכנים.
אחרי שסיימנו את הפיתוח ואנחנו יודעים שהחבילות איתן אנחנו עובדים מתאימות לנו, אנחנו נבצע את הפקודה
pipenv lock ואז כדי להתקין את אותו הבילד בדיוק בכל סביבה שנרצה כל שנצטרך לעשות זה להריץ pipenv install --ignore-pipfile שיקח את החבילות כמו שהן מופיעות ב pipfile.lock.


סביבת dev

פיצ׳ר נחמד ש pipenv נותן לנו הוא להחזיק כמה סביבות תחת אותה קונפיגורציה.
אם לדוגמה ברצוני להתקין את החבילה pytest אבל אינני רוצה שהיא תותקן בפרודקשן, כל מה שאצטרך לעשות זה להתקין אותה עם הדגל dev באופן הבא:

tomers-MacBook-Pro:trypipenv tomer$ pipenv install --dev pytest

Installing pytest

Adding pytest to Pipfile's [dev-packages]… dev-packages הוספה ל

✔ Installation Succeeded 

Pipfile.lock (56beaa) out of date, updating to (9e60ab)…

Locking [dev-packages] dependencies…

Building requirements...

Resolving dependencies...

✔ Success! 

Locking [packages] dependencies…

Building requirements...

Resolving dependencies...

✔ Success! 

Updated Pipfile.lock (9e60ab)!

Installing dependencies from Pipfile.lock (9e60ab)…

  🐍   ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 2/2 — 00:00:00

To activate this project's virtualenv, run pipenv shell.

Alternatively, run a command inside the virtualenv with pipenv run.

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

מבט על גרף החבילות

עם pipenv אפשר לראות גם את היררכיית ההתקנות של החבילות באמצעות הפקודה pipenv graph כך:

tomers-MacBook-Pro:trypipenv tomer$ pipenv graph

pytest==6.1.2

  - attrs [required: >=17.4.0, installed: 20.3.0]

  - iniconfig [required: Any, installed: 1.1.1]

  - packaging [required: Any, installed: 20.4]

    - pyparsing [required: >=2.0.2, installed: 2.4.7]

    - six [required: Any, installed: 1.15.0]

  - pluggy [required: >=0.12,<1.0, installed: 0.13.1]

  - py [required: >=1.8.2, installed: 1.9.0]

  - toml [required: Any, installed: 0.10.2]

requests==2.25.0

  - certifi [required: >=2017.4.17, installed: 2020.11.8]

  - chardet [required: >=3.0.2,<4, installed: 3.0.4]

  - idna [required: >=2.5,<3, installed: 2.10]

  - urllib3 [required: >=1.21.1,<1.27, installed: 1.26.2]

בדיקת תקינות החבילות

באמצעות הפקודה pipenv check נוכל לבצע סריקה על החבילות שלנו ולקבל כפלט האם החבילות עומדות ב PEP 508 וגם לקבל מידע על פרצות אבטחה שהתגלו. (יכולים לנסות את django בגרסה 2.1.0 ולראות)

המעבר מpip ו-venv ל-pipenv

המעבר ל pipenv הוא פשוט למדי. pipenv יודע לבצע התקנה דרך קובץ ה requirements.txt ולהמיר אותו ל Pipfile, והתמיכה הזו מאפשרת לחברי הצוות לבחור באיזה מנהל חבילות יעבדו.

באמצעות הפקודה pipenv lock -r נוכל לקבל את אותו הפלט שקיבלנו מ pip freeze על מנת לעדכן גם את קובץ ה requirements שלנו במידה ויש בכך צורך.

סיכום

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

נתראה בפוסט הבא :)


תגובות

הוסף רשומת תגובה

פוסטים פופולריים מהבלוג הזה

תכנות מונחה עצמים | Dependency Inversion Principle

מהם קבצי DLL ואיך להשתמש בהם?

מה ההבדל בין אוטומציה לפיתוח רגיל