מה היא סביבה וירטואלית?
נניח שאתם מתכנתים כמה וכמה פרויקטים על אותו מחשב.
פרויקט אחד הוא של העבודה, בו אתם עורכים קוד שנכתב לפני 5 שנים ומשתמש במודולים ישנים יחסית.
אחד המודולים, Flask, נניח, הוא בגרסה ישנה מאוד (נניח, גרסה 0.12), ושדרוג שלו ישבור את המערכת.
הפרויקט השני הוא מערכת התרגילים של הקורס, שמשתמש בטכנולוגיה חדשה ונוצצת.
בין המודולים שמככּבים בו נמצא Flask בגרסתו החדשה ביותר – גרסה 1.1.
בפרויקט שלישי משל עצמכם אתם משתמשים במודולים אחרים, אבל חלק מהמודולים שבהם אתם משתמשים, נסמכים בעצמם על מודולים אחרים.
בין המודולים, ישנו גם מודול A שבעצמו נסמך מודול B – הרי הוא Flask בגרסה 1.0.
כשאתם מנסים לעבור בין פרויקטים אתם מגלים שגרסה 0.12 של Flask אינה תואמת עם גרסה 1.1,
וגרסה 1.1 מתנהגת מעט שונה מגרסה 1.0.
מובן שישנה בעיה: לא פרקטי להסיר את Flask ולהתקין את הגרסה הרצויה כל פעם שנעבור לעבוד על פרויקט אחר.
הפתרון? סביבות וירטואליות!
סביבה וירטואלית מאפשרת לנו להגדיר מעין קופסה מבודדת שבה רץ פייתון בלי כל המודולים שהתקנו על המחשב.
אם נרצה, נוכל להתקין עבור המערכת שאנחנו כותבים בעבודה סביבה וירטואלית (דמיינו חדר מבוּדד וסטרילי) שבה מותקנים רק המודולים ששייכים לפרויקט שלנו.
כשנרצה לעבור לפרויקט אחר, נניח, לזה של מערכת הגשת התרגילים בקורס – נוכל להגדיר עבורו סביבה וירטואלית משלו, שבה יהיו מותקנים רק המודולים שקשורים למערכת הגשת התרגילים בקורס.
אלו שתי סביבות שונות, נפרדות אחת מהשנייה, שלא יודעות על קיומן אחת של השנייה, או על המודולים שהותקנו אחת אצל השנייה.
זה מאפשר לנו (ולכל מי שיעבוד על המערכת שלנו) להפעיל כל אחת מהסביבות בנפרד, ולעבוד עליהן באופן בלתי תלוי.
איך עושים את זה?
כל מה שצריך כדי ליצור סביבה וירטואלית, זה להריץ בשורת הפקודה:
python -m venv PROJECT_NAME
הפקודה הזו תיצור תיקייה חדשה בשם PROJECT_NAME, שבתוכה יהיו תיקיות וקבצים.
התיקיות והקבצים הללו הם מה שמאפשר לסביבה הווירטואלית לפעול.
מרובם אתם רוצים להתעלם בשלב הזה, אבל אתם עדיין צריכים להיכנס לתוך הסביבה הווירטואלית כדי שתפעל.
כדי להפעיל את הסביבה הווירטואלית, אם כך, תצטרכו לרשום:
- בווינדוס:
PROJECT_NAME\Scripts\activate.bat
- ב־MacOS/בלינוקס:
source PROJECT_NAME/bin/activate
וזהו. כעת אתם בתוך הסביבה הווירטואלית.
ליד שורת הפקודה שלכם אמורה להתווסף המילה PROJECT_NAME, כדי להזכיר לכם שזה המצב.
תוכלו לצאת מהסביבה הווירטואלית בכל זמן אם תכתבו deactivate
.
הסביבה הווירטואלית תקפה רק בחלון שבו הפעלתם אותה.
אם תריצו פקודות בחלון אחר אתם לא תפעלו תחת הסביבה הווירטואלית ותשפיעו על גרסת הפייתון “הראשית” שמותקנת אצלכם במחשב.
כדי להתקין מודול חדש תוכלו להשתמש ב־pip, כרגיל.
שימו לב שהתקנת המודול תתבצע בתוך הסביבה הווירטואלית ולא תשפיע על גרסת הפייתון הראשית שמותקנת אצלכם במחשב.
כדי לשמור את כל ההתקנות שביצעתם בסביבה וירטואלית מסוימת ולשלוח אותם לחבריכם שיוכלו להתקין אותן בקלות, כתבו:
pip freeze > requirements.txt
הפקודה הזו תיצור קובץ בשם requirements.txt, שמכיל את כל החבילות שמותקנות אצלכם בסביבה.
אם תכתבו אותו כשאתם מחוץ לסביבה הווירטואלית, ישפכו לקובץ הזה כל החבילות שמותקנות אצלכם על הגרסה הראשית של פייתון, ולא דווקא מותקנות בסביבה הווירטואלית. משמע: חשוב לוודא שאתם בתוך הסביבה הווירטואלית שלכם לפני שהרצתם pip freeze.
טיפ: ודאו ש־requirements.txt נמצא אצלכם בתיקייה הראשית של הפרויקט. זה הנוהג, ויש אפילו שירותים שיאפשרו לכם להעלות חבילה של פייתון וידרשו שהקובץ הזה ישב שם.
כדי להתקין את כל החבילות שנמצאות בקובץ requirements.txt שקיבלתם מחבר או בתוך פרויקט כלשהו, היכנסו לסביבה הווירטואלית וכתבו:
pip install -r requirements.txt
זהו. אתם יודעים להשתמש בסביבות וירטואליות
גרסאות נוספות
נוסף על השיטה שהוצגה פה פה, יש שיטות נוספות ליצור סביבות וירטואליות.
(קריאת העשרה, ממילא אין צורך לזכור את כל זה ותתקלו בדברים האלו ככל שתעבדו יותר עם פייתון)
- המודול virtualenv – אותו רעיון, אבל לא חלק מהמודולים שבאים עם פייתון ויש לו יותר פיצ’רים.
- המודול conda – שילוב מעניין בין מנהל חבילות לסביבה וירטואלית. לא בשימוש נרחב, אבל נוח יחסית. מאפשר גם לבחור את גרסת הפייתון שרוצים להשתמש בה, אם אתם עובדים על פרויקטים שתומכים בגרסאות פייתון שונות.
- המודול pipenv הוא מודול פופולרי מאוד, שבדומה ל־conda עשיר בפיצ’רים לניהול סביבה וחבילות. מעבר לתפקידה של הפקודה
pipenv
שיוצרת סביבה וירטואלית, היא גם יודעת לנהל גם את החבילות שמותקנות באותה סביבה. הטריק של pipenv הוא ליצור קובץ נפרד בשם Pipfile.lock שבו יש את גרסאות החבילות שהותקנו, ללא התלויות שלהם (יכולים לחשוב על היתרונות והחסרונות? פתחו על זה דיון בפורום). - המודול poetry הוא מודול שמתחיל לתפוס תאוצה בשנה האחרונה. הוא דומה ל־pipenv אבל תומך בתקנים החדשים ביותר של השפה, ונראה שהוא עובד די טוב בפועל.
הקהילה די מפוצלת בנוגע לשיטת ניהול התלויות והסביבה של הפרויקט, כך שאתם מוזמנים לבחור בדרך המועדפת עליכם.
אשאיר את קריאת ההרחבה וההשוואה על כל אחד מהמודולים הללו עבורכם.
תבניות לאתר: Jinja2
כשאנחנו כותבים קוד לאתר שלנו קצה־לקצה, אנחנו כבר יודעים שנצטרך להשתמש לא מעט ב־HTML.
הצרה היא שכשאנחנו רוצים לשלב את ה־HTML הזה עם פייתון, פעמים רבות נצטרך להכניס לדף מידע באופן דינאמי. למשל:
- אם קיבלנו מ־API חיצוני רשימת עצמים (מזג האוויר בשבוע הקרוב, העמלות של השקל מול הדולר בשבוע האחרון, רשימת כל הפוקימונים שקיימים) ואנחנו רוצים לשלב אותם ב־HTML באמצעות לולאה.
- אם אנחנו רוצים להציג חלק מסוים בדף רק במקרים מסוימים. לדוגמה, במערכת התרגילים מוצגת בדף התרגיל האפשרות להוסיף הערה אך ורק אם המשתמש הוא המנהל.
- אם אנחנו רוצים להציג בכל הדפים תפריט, אבל לא רוצים לשכפל את הקוד כשאנחנו יוצרים דף חדש עבור המערכת.
לצורך כך סביבות שמתעסקות באתרי אינטרנט לרוב מנגישות למתכנתים דבר שנקרא Web template engine.
הרעיון של Web template engines הוא לאפשר למתכנתים לכתוב HTML, ולהוסיף לו Syntax מוסכם מראש שיאפשר למתכנתים להטמיע בתוך אותו HTML תוכן דינאמי.
הנה, לדוגמה, קוד ב־Jinja2 (שנלקח מכאן). נניח שלקובץ קוראים user_list.html:
{% extends "layout.html" %}
{% block body %}
<ul>
{% for user in users %}
<li><a href="{{ user.url }}">{{ user.username }}</a></li>
{% endfor %}
</ul>
{% endblock %}
בקוד הזה השורה הראשונה אומרת שה־HTML במסמך הזה הולך להיות “מודבק” בתוך מסמך אחר, שנקרא layout.html.
התוכן שבא אחר־כך מורה על ul (רשימה לא מסודרת של תבליטים) שמכילה תבליט אחד עבור כל משתמש במשתנה user.
עבור כל user מתוך רשימת users, יופיע קישור שיילקח מתוך user.url, והמלל של הקישור יהיה user.username.
שימו לב לשילוב המעניין בין תחביר פייתוני לבין תחביר של HTML. זו היכולת המעניינת (והמועילה) שאנחנו מקבלים ב־Web template engines בכלל, וב־Jinja2 בפרט.
התשתית שבה אנחנו משתמשים לבניית אתרים, Flask, משתמשת ב־Jinja2 בתור ה־Web template engine שמגיע יחד איתה.
כל קובץ שיהיה בתוך תיקיית templates בפרויקט שלנו נחשב ל"template", ו־Flask תדע לטעון אותו באמצעות הפונקציה render_template.
אם נניח נרצה לטעון את ה־template שנקרא user_list.html
כאשר משתמש נכנס לעמוד /all_users
, נכתוב את הקוד הבא:
@app.route('/all_users')
def login():
registered_users = get_all_users() # נניח שהפונקציה הזו מחזירה את רשימת המשתמשים במערכת
render_template('user_list.html', users=registered_users)
ל־Jinja2 יש הרבה פיצ’רים וטריקים נחמדים.
אתם יכולים לקרוא עליהם בתיעוד, או לראות דוגמאות בקוד המקור של מערכת התרגילים.
אחסון באינטרנט
כשאנחנו רוצים להעלות אתר דינאמי שבנינו לאינטרנט, חשוב לנו שיקרו כמה דברים:
- שהוא יהיה נגיש מכל מקום בעולם, והניהול שלו יהיה נגיש מכל מקום בעולם.
- שהגישה אליו תהיה מהירה יחסית – זה אומר שהחיבור של המקום שמאחסן את האתר לאינטרנט יהיה טוב מאוד, ואולי אפילו הגורם המאחסן ישתמש בכל מני טריקים כדי להמהיר את הגישה לאתר שלנו.
- שיאפשר לנו לשלוט על המחשב שעליו מאוחסן האתר שלנו כאילו זה היה המחשב שלנו.
ישנם מספר שירותים שעונים על הדרישות האלו, ומוגדרים כפלטפורמה נוחה להעלות אליה את אתרי האינטרנט שתכנתנו באמצעות Flask (או באמצעות כל מודול/שפת תכנות אחרים).
השרת
למרות ש־Flask יודע לקבל בקשות HTTP, זו לא המומחיות שלו והוא מבצע את זה בצורה איטית וגרועה יחסית.
Flask מיועד בעיקר לספק לנו את הסביבה לפיתוח האתר, ופחות כרכיב שאחראי להיות השרת שמטפל בפניות HTTP.
לעומתו, ישנן תוכנות שייעודן לטפל בבקשות HTTP במהירות וביעילות. הן נקראות “שרתי אינטרנט” או “שרתי HTTP”, ולפעמים הן מגיעות עם פיצ’רים חשובים, כמו ניהול עומסים, תמיכה ב־IPv6, ניהול זיכרון מטמון, אפשרות להוספת headers לתשובות ששולח השרת, ניהול הקונפיגורציה של התקשורת המאובטחת (HTTPS) וכן הלאה.
שרתי האינטרנט המוכרים ביותר כיום הם nginx, Apache, lighttpd ו־IIS.
לצרכים שלנו הבחירה בינו לבין Apache או לבין lighttpd היא די שרירותית.
בשביל להשלים את הפאזל, נצטרך רכיב שידע לתרגם את מה שמגיע לשרת האינטרנט ל־Flask, ואת מה שיוצא מ־Flask לשרת האינטרנט. הרכיב הזה נקרא WSGI, או Web Server Gateway Interface. התוכנה שנבחר לצורך הזה נקראת Gunicorn.
לסיכום:
- יש לנו פייתון שמריץ Flask, שהוא מודול בפייתון שיודע לקבל בקשות לפי מיקום הבקשה (
app.route
) ולפיהן להחזיר תשובות. - בד"כ נרצה לטעון בעזרתו templates שכתובים ב־Jinja2.
- את הבקשות מהאינטרנט תקבל תוכנה שזה הייעוד שלה ובזה היא טובה: שרת אינטרנט, כמו nginx או Apache.
- בין שרת האינטרנט ל־Flask יישב רכיב שיודע לתרגם דברים אחד לשני (WSGI). בחרנו באחד שנקרא Gunicorn.
ספק האחסון
כיום יש תחרות גדולה בקרב החברות הגדולות על מי “יזכה” לארח את האתר שלנו באינטרנט. בין החברות הגדולות שמציעות שירותים כאלו נמצאות Amazon, Goolge ו־Microsoft עם השירותים AWS, GCP ו־Azure (בהתאמה). החברות האלו מנסות לצוד בעיקר לקוחות ענק שישלמו להם אלפי דולרים ועד מיליוני דולרים בחודש על השכרת שרתים, אבל כדגי הרקק שאנחנו – רובן יציעו לנו תוכניות חינמיות משתלמות, לפחות לשנה בחינם.
ספק האחסון שנסקור הוא Heroku, שדואג לפשט את כל תהליך העלאת האתר. כדי להשתמש בו, תצטרכו לפתוח חשבון ב־Heroku.
צרו במחשב שלכם סביבה וירטואלית, כמו שלמדנו בתחילת היום. לצורך הדוגמה, נקרא לה heroku:
python -m venv heroku
הפעילו את הסביבה הוירטואלית באמצעות source heroku/bin/activate
או heroku\Scripts\activate.bat
, בתלות במערכת ההפעלה שלכם.
לאחר מכן התקינו את flask ואת gunicorn באמצעות pip.
צרו קובץ חדש ששמו app.py, וכיתבו בו קוד בסיסי שמשתמש ב־Flask:
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/')
def index():
return "<h1>Welcome to our server !!</h1>"
if __name__ == '__main__':
app.run(threaded=True, port=5000)
הריצו flask run, היכנסו ל־http://localhost:5000 ובדקו שהסביבה שלכם עובדת.
צרו את קובץ requirements.txt, שמכיל את כל התלויות ש־Heroku צריכים להכיר:
pip freeze > requirements.txt
צרו קובץ נוסף בשם Procfile (ללא סיומת).
הקובץ הזה הוא קובץ מיוחד עבור Heroku, שמגדיר עבור Heroku אילו פקודות להריץ בכל מני מקרים.
אנחנו נכתוב בתוכו רק את השורה:
web: gunicorn app:app
שאומרת: ברגע שמגיעה בקשת אינטרנט, תפנה אותה ל־gunicorn.
(מבולבלים? זה בסדר. Heroku דואג להרים Web server מאחורי הקלעים כדי שלא נצטרך להתעסק בזה. הוא יקבל את הבקשות ל־Web server שיעביר אותם ל־gunicorn).
עכשיו צרו git שינהל את כל הקוד שכתבתם – זה הדבר המנומס לעשות.
כתבו git init .
בתיקייה הרלוונטית, ואז git add -A
ו־git commit -a -m "First commit"
.
אנחנו כמעט מוכנים לשיגור. התחברו ל־Heroku, ולחצו על New -> Create new app.
צרו אפליקציה בשם שמתחשק לכם (שמרו אותו), וכשתגיעו לעמוד הניהול יהיו לכם שתי אפשרויות:
- העלאה באמצעות GitHub – אם העלתם את האפליקציה שלכם לגיטהאב זה מעולה, ותוכלו לעשות את זה ביתר קלות מכאן.
- העלאה באמצעות Heroku CLI – תצטרכו להתקין לפי המדריך כאן. אחרי שהתקנתם, כתבו
heroku login
בחלון חדש של שורת פקודה. אחרי שהתחברתם היכנסו לפרויקט שלכם, הפעילו את הסביבה הוירטואלית, כיתבוheroku git:remote -a PROJECT_NAME
ואזgit push heroku master
.
עכשיו אתם אמורים להיות מסוגלים לראות את האתר שלכם ב־ https://PROJECT_NAME.herokuapp.com/
. מזל טוב!