August 13, 2022

פייתון 27 - לינטרים ופורמט


פרק קודם:

פייתון 26 - אסינכרוניות חלק ב

מה נלמד

  • כלים לאיכות קוד
  • Code smells
  • PyLint
  • Bandit
  • כלים נוספים ששווה בדיקה

כלי איכות קוד

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

הבעיה בבדיקות אנושיות

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

המתכנתים שכתבו את הקוד לא תמיד יימצאו את כל הבעיות בעצמם - זו הבעיה בכתיבת קוד.
אז כמו כתיבת ספר - עורך ועמיתים נוספים עוברים על הכתוב ומוודאים שאין בעיות.
בשפה המקצועית זה נקרא Code Review - בדיקת הקוד.

בשלב הזה גם המתכנתים שכתבו וגם המתכנתים שייבדקו את הקוד הולכים לעבור עליו שוב.
זהו שלב חשוב ולמרות שיש לנו כלים אוטומטיים - כדאי לבצע את השלב הזה.

אז מה הבעיה כאן אם כמות אנשים נכבדת עוברת על הקוד ומוודא שהכל תקין?

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

למשל בעיות כתיב פשוטות שיכולות לפגוע:
רוב הכלים האוטומטיים כבר יודעים לזהות כשלים כאלו.

1
2
3
4
def Functon():
pass

a = Function() # Oh oh - Missing 'i'
  1. היכולת להתמקד בקוד בכל בדיקת קוד היא שעה-שעתיים.
    ככל שממשיכים בבידקה איכות הבדיקה יורדת.
  2. לעיתים אנשים אחרים לא זמינים לביצוע הבדיקה.

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

מהן הבדיקות שיש לבצע

  1. בדיקת פורמט וסטייל של הקוד.
    ווידוא שהקוד נראה בצורה אחידה, ההזחות קיימות, רווחים סטנדרטיים.
    למשל קוד כזה ייראה לנו מעט מוזר:
1
a=  1      +      2-3*               2

כלי פורמט ייתקן לנו את זה מיד ל:

1
a = 1 + 2 - 3 * 2

הצרה הקלאסית ביותר של מתכנתי פייתון זה הזחות או רווחים.

1
2
3
def function():
# Spaces or tabs?
pass
  1. כלי למציאת בעיות פוטנציאליות או - Code smells

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

קוד נפוץ שמעלה תהיות:

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

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

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

נרצה למצוא את כלל הכשלים הלוגיים לפני שנריץ את הקוד.

  1. באגים אבטחתיים
    בין אם זה שימוש בפונקציות ישנות, קוד לא מאובטח או חיבור לא מוצפן אנחנו נרצה לזהות כשלים אבטחתיים בקוד שלנו.

  2. קוד שלא בשימוש
    בדרך כלל גם ה-IDE שאנחנו משתמשים בו יתריע על קוד שלא בשימוש,
    אך גם עבודת הלינטרים היא לבצע את הבידקות האלו.

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def Add(a , b):
return a + b

def FailAdd(a , b):
return a - b

def TestAdd(AddFunction):
a = 2
b = 3
result = 5

realResult = AddFunction(a , b)
if realResult == result:
print("Success")
else:
print("Fail")

TestAdd(Add)
TestAdd(FailAdd)
  1. כלים רחביים לבדיקת המערכת
    אלו כלים שרצים בקנה מידה רחב המוודא שכלל המערכת עובדת כראוי, ללא באגים, ללא כשלים אבטחתיים.
    ולעיתים גם עובדים עם הכלים האלו בצורה ידנית כדי לוודא תקינות מערכת ע”י דגימת פרמטרים.
    בדרך כלל מערכות כאלו נקראות “שליטה ובקרה”.

אלו כשלים יכולים להיות בכלים אוטומטיים?

  1. התרעה מוגזמת או בשפה המקצועית False Positive.
  2. בכמות קוד רבה חלק המכלים יגזלו יותר זמן בבדיקה.
  3. כלים פשוטים לא ייזהו בעיות מורכבות יותר כמו Multi threading.
  4. פוטנציאל באגים בכלים עצמם - הסנדלר הולך יחף.

Pylint

אתר הפרוייקט

כדי להוריד יש להשתמש ב-pip:
pip install pylint

שימוש ב- PyLint

להלן קוד תקול:

1
2
3
4
5
6
7
8
def addFunc():
pass

a = addFunc()

b = AddFunc()

c = 1 / 0

בכל שורה ל- a, b, c זהו מהי הבעיה.

נשמור את הקוד התקול בקובץ myFile.py ונריץ מה-Commandline את הפקודה pylint עם שם הקובץ בצורה הבאה:

1
pylint myFile.py

התובנות שקיבלנו מה-pylint:

  1. כל קובץ צריך להסתיים בשורה חדשה
1
myFile.py:8:0: C0304: Final newline missing (missing-final-newline)
  1. חסרה לנו דקומטנציה למודול
1
myFile.py:1:0: C0114: Missing module docstring (missing-module-docstring)

כדי להוסיף docstring יש להוסיף:

1
2
3
"""
high level support for doing this and that.
"""

בתחילת הקובץ.

  1. שם הקובץ לא לפי הסטנדרט.
1
myFile.py:1:0: C0103: Module name "myFile" doesn't conform to snake_case naming style (invalid-name)

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

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

1
pylint myFile.py --module-rgx=.*
  1. חסר לנו Doc string על הפונקציה.
1
myFile.py:1:0: C0116: Missing function or method docstring (missing-function-docstring)

במידת הצורך ניתן לבטל גם את זה:

1
pylint myFile.py --errors-only

האופציה --errors-only נותנת לנו רק שגיאות לוגיות ולכן כדאי להתחיל רק עם זה.

  1. שם של הפונקציה לא לפי הסטנדרט בברירת מחדל.
    הריצה הדיפולטיבית מגיעה עם סט כלים שנבנה מראש - לא חייבים בהכרח לעקוב אחריהם וניתן להתעלם או לשנות את הקונפיגורציה של הכלי כך שיתאים לצרכים שלנו.
1
myFile.py:1:0: C0103: Function name "addFunc" doesn't conform to snake_case naming style (invalid-name)
  1. השגיאה האינדיקטיבית הראשונה שלנו.
1
myFile.py:4:0: E1111: Assigning result of a function call, where the function has no return (assignment-from-no-return)

אין ערך החזרה מהפונקציה ואנחנו מצפים לזה, כעת שהוא גילה לנו את זה אנחנו יכולים לחזור לקוד ולתקן!

  1. כמובן שהוא לא מזהה את הפונקציה שיש בה שגיאת כתיב.
1
myFile.py:6:4: E0602: Undefined variable 'AddFunc' (undefined-variable)

שימו לב שהכלי pylint לא הודיע לנו על חילוק באפס!

לא להסתמך על הכלים בעיניים עצומות

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


תרגול PyLint

ניתן למצוא מידע כאן:
Pylint Docs

  1. הריצו את ה-pylint עם הרמה - HIGH.

  2. הריצו את הכלי על תיקייה פרט לקובץ בשם dontrun.py

  3. הריצו את הכלי עם הגבלת כמות פרמטרים לפונקציה - 5 פרמטרים מקסימום


פתרונות:


Ruff

Ruff הוא כמו PyLint אך הרבה הרבה יותר מהיר!

https://github.com/charliermarsh/ruff

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


Bandit

הכלי Bandit ייעזור לנו לגלות בעיות אבטחיות בסקריפטים.

להלן קוד:

1
2
3
4
5
6
7
8
9
10
import os
import subprocess

scriptToRun = os.environ.get('MY_SCRIPT')

exec(open(scriptToRun).read())

os.startfile(scriptToRun)

p = subprocess.run(scriptToRun)

כדי להריץ את הכלי נוכל לתת לו קובץ והוא ייעבור עליו:

bandit myFile.py

התוצאה:

  1. בעיתיות ה - Subprocess
    הדבר הראשון שהוא מציג לנו זה הערה על המודול subprocess.
1
import subprocess

הרצה של סקריפטים או כלים חיצוניים לסקריפט שלנו אף פעם לא מוודאים שמה שיירוץ זה באמת מה שהתכוונו אליו.
דרך החלפה האקרים מיומנים יכולים להשתיל קוד זדוני מבלי שנשים לב.

איך פותרים את הבעיה הספציפית הזו?
בעדיפות לבצע import או בדיקת Checksum לכלים חיצוניים שאנחנו לא סומכים עליהם.

  1. Exec
1
exec(open(scriptToRun).read())

הפונקציה exec מקבלת טקסט חופשי ומריצה אותו.
במידה והטקסט הזה הוא קוד זדוני אז אנחנו נריץ בצורה עיוורת קוד שמטרתו להרוס ולפגוע.

אל תשמשו בזה אף פעם!

  1. הרצה של תהליכים

גם os.startfile(scriptToRun) וגם p = subprocess.run(scriptToRun) מריצים תהליך בשני פונקציות שורות.

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

בגלל השימוש ב-Environment Variable זה מלה את הסיכון של הסקריפט הזה להריץ קוד זדוני.

טיפ אבטחתי

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

אינטגרציה ל - IDE

IDE - Integrated Development Environment אלו כלים שנותנים לכם הכל.
זאת אומרת שלינטרים וכלים אוטומטיים חלקיים יהיו כבר מובנים כגון כלי בדיקה.

PyCharm

מי שמשתמש בכלי המדהים הזה יכול לערוך את ההגדרות שלו:
Configure PyCharm

VS Code

ב-VS Code יש לנו הרחבות שנותנות לנו יכולות שונות.
למשל כמה הרחבות שלי יש:


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

הצגתי לכם 2 כלים פשוטים שמכסים לכם חלק מהתרחישים.
יש כלים נוספים - מהירים יותר, ומכסים תרחישים שונים שיכולים לעזור לכם:

Flake8

הפלייק 8 ייעזור לכם בפרמוט וסטייל של הקוד:

Flake8

Mypy

בודק טייפים בצורה מהימנה.

Mypy

Black

פורמטר לקוד - מהיר ויעיל

Black


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

פייתון 28 - בדיקות

על הפוסט

הפוסט נכתב על ידי Ilya, רישיון על ידי CC BY-NC-ND 4.0.

שתפו את הפוסט

Email Facebook Linkedin Print

קנו לי קפה

#Software#Python