MindSpore Python編程規范以PEP8為基礎,參考華為Python通用編碼規范、安全編程規范,並結合業界共識整理而成,參與MindSpore社區開發需要首先遵循本規范內容(與PEP8沖突部分),其余遵循PEP8規范。
如果對規則異議,建議提交issue並說明理由,經MindSpore社區運營團隊評審接納後可修改生效。 a
MindSpore開源社區(https://gitee.com/mindspore/)。
<font size=3>規則 1.1.1 包名,模塊名:小寫,不使用下劃線。</font>
<font size=3>規則 1.1.2 類名:使用駝峰格式,首字母大寫,私有類下劃線前綴。</font>
class _Foo: _instance = None pass
<font size=3>規則 1.1.3 函數名、變量名:小寫,多個單詞下劃線分割。</font>
def _func_example(path): pass
<font size=3>建議 1.1.4 除迭代器與計數器除外,禁止使用單字符命名。</font>
<font size=3>規則 1.2.1 每行字符數不要超過 120 個。</font>
如果超過120個字符,請選擇合理的方式進行換行。
<font size=3>規則 1.2.2 使用空格進行縮進,每次縮進4個空格,禁止tab縮進。</font>
<font size=3>規則 1.2.3 import順序:標准庫、第三方、自定義模塊。</font>
<font size=3>規則 1.2.4 返回語句和條件語句中不使用括號。</font>
<font size=3>規則 1.2.5 模塊級函數和類之間雙空行,類成員函數之間一空行,注釋與代碼間按需添加空行,原則上不超過兩空行。</font>
<font size=3>規則 1.2.6 無效或冗余代碼直接刪除,不要以注釋、TODO等方式保留在代碼中,建議提issue記錄。</font>
<font size=3>規則 1.3.1 文件頭注釋必須包含版權聲明。</font>
所有python文件,均需包含如下版權聲明:
# Copyright 2019 Huawei Technologies Co., Ltd## Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at## http://www.apache.org/licenses/LICENSE-2.0## Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.# ============================================================================"""Add notes."""import xxx
關於版權說明,應注意:
2020年新建的文件,應該是Copyright 2020 Huawei Technologies Co., Ltd
2019年創建年份,2020年修改年份,應該是Copyright 2019-2020 Huawei Technologies Co., Ltd
<font size=3>規則 1.3.2 對外的類、方法、算子、Cell注釋格式。</font>
class
和 def
的注釋格式相同,采用業界通用的python注釋語法,寫在聲明下方並縮進,所有的 class
和 def
都需要寫注釋,模塊內部的類和方法可以只寫一條簡介。<font size=3>規則 1.3.3 不允許通過注釋屏蔽pylint告警。</font>
<font size=3>規則 1.4.1 異常日志文本首字母大寫。</font>
<font size=3>規則 1.4.2 日志文本中變量名必須使用單引號注明。</font>
<font size=3>規則 2.1.1 用戶接口在文件的__all__中說明,__all__擺放在import與代碼之間。</font>
<font size=3>規則 2.1.2 當前文件使用的非對外方法命名采用下劃線前綴,內部跨模塊使用的方法無需下劃線前綴,用戶接口在__all__中聲明。</font>
<font size=3>規則 2.2.1 對所有外部數據進行合法性檢查,包括但不限於:函數入參、外部輸入命名行、文件格式,文件大小、環境變量、用戶數據等。</font>
<font size=3>建議 2.2.2 必須對文件路徑進行規范化後再使用。</font>
當文件路徑來自外部數據時,需要先將文件路徑規范化,如果沒有作規范化處理,攻擊者就有機會通過惡意構造文件路徑進行文件的越權訪問:
例如,攻擊者可以構造“../../../etc/passwd”的方式進行任意文件訪問。
在linux下,使用realpath函數,在windows下,使用PathCanonicalize函數進行文件路徑的規范化。
<font size=3>規則 2.2.3 禁止調用OS命令解析器執行命令或運行程序。</font>
使用未經校驗的不可信輸入作為系統命令的參數或命令的一部分,可能導致命令注入漏洞。對於命令注入漏洞,命令將會以與Python應用程序相同的特權級別執行,它向攻擊者提供了類似系統shell的功能。在Python中,os.system 或 os.popen 經常被用來調用一個新的進程,如果被執行的命令來自於外部輸入,則可能會產生命令和參數注入。
執行命令的時候,請注意以下幾點:
【錯誤代碼示例1】
攻擊者可以通過找到環境變量APPHOME對應的值,並且在相應目錄下放置常量INITCMD對應的攻擊程序,達到執行的效果:
home = os.getenv('APPHOME') cmd = os.path.join(home, INITCMD) os.system(cmd)
【錯誤代碼示例2】
沒有校驗屬性 backuptype 的值,這個是用戶輸入的,攻擊者可能進行攻擊,
例如:用戶輸入的是:" && del c:\dbms\. ":
# 值來自用戶配置 btype = req.field('backuptype') cmd = "cmd.exe /K \"c:\\util\\rmanDB.bat " + btype + "&&c:\\util\\cleanup.bat\"" os.system(cmd)
【錯誤代碼示例3】
沒有校驗屬性 backuptype 的值,這個是用戶輸入的,攻擊者可能進行攻擊,例如:用戶輸入的是:" && del c:\dbms\. ":
import os import sys try: print(os.system("ls " + sys.argv[1])) except Exception as ex: print('exception:', ex)
攻擊者可以通過以下命令來利用這個漏洞程序:
python test.py ". && echo bad"
實際將會執行兩個命令:
ls . echo bad
【正確代碼示例】
避免使用 os.system,可以使用標准的 API 替代運行系統命令來完成任務:
import os import sys try: print(os.listdir(sys.argv[1])) except Exception as ex: print(ex)
<font size=3>規則 2.3.1 異常必須被妥當處理,禁止抑制或者忽略已檢查異常。</font>
每一個except 塊都應該確保程序只會在繼續有效的情況下才會繼續運行下去。except 塊必須要麼從異常情況中恢復,要麼重新拋出適合當前catch塊上下文的另一個異常以允許最鄰近的外層try-except 語句塊來進行恢復工作。
【正確代碼示例】
正確的做法是,避免使用 os.system,可以使用標准的 API 替代運行系統命令來完成任務:
validFlag = False while not validFlag: try: # If requested file does not exist, throws FileNotFoundError # If requested file exists, sets validFlag to true validFlag = True except FileNotFoundError: import traceback traceback.print_exc()
【例外情況】:
<font size=3>規則 2.3.2 使用try…except…結構對代碼作保護時,需要在異常後使用finally…結構保證操作對象的釋放。</font>
使用try…except…結構對代碼作保護時,如果代碼執行出現了異常,為了能夠可靠地關閉操作對象,需要使用finally…結構確保釋放操作對象。
【正確代碼示例】
handle = open(r"/tmp/sample_data.txt") # May raise IOError try: data = handle.read() # May raise UnicodeDecodeError except UnicodeDecodeError as decode_error: print(decode_error) finally: handle.close() # Always run after try:
<font size=3>規則 2.3.3 不要使用“except:”語句來捕獲所有異常。</font>
在異常這方面, Python非常寬容,“except:”語句真的會捕獲包括Python語法錯誤在內的任何錯誤。使用“except:”很容易隱藏真正的bug,我們在使用try…except…結構對代碼作保護時,應該明確期望處理的異常。Exception類是大多數運行時異常的基類,一般也應當避免在except語句中使用。通常,try只應當包含必須要在當前位置處理異常的語句,except只捕獲必須處理的異常。比如對於打開文件的代碼,try應當只包含open語句,except只捕獲FileNotFoundError異常。對於其他預料外的異常,則讓上層函數捕獲,或者透傳到程序外部來充分暴露問題。
【錯誤代碼示例】
如下代碼可能拋出兩種異常,使用“except:”語句進行統一處理時,如果是open執行異常,將在“except:”語句之後handle無效的情況下調用close,報錯handle未定義。
try: handle = open(r"/tmp/sample_data.txt") # May raise IOError data = handle.read() # May raise UnicodeDecodeError except: handle.close()
【正確代碼示例】
try: handle = open(r"/tmp/sample_data.txt") # May raise IOError try: data = handle.read() # May raise UnicodeDecodeError except UnicodeDecodeError as decode_error: print(decode_error) finally: handle.close() except(FileNotFoundError, IOError) as file_open_except: print(file_open_except)
<font size=3>規則 2.3.4 不在except分支裡面的raise都必須帶異常。</font>
raise關鍵字單獨使用只能出現在try-except語句中,重新拋出except抓住的異常。
【錯誤代碼示例】
a = 1 if a == 1: raise
【正確代碼示例1】raise一個Exception或自定義的Exception
a = 1 if a == 1: raise Exception
【正確代碼示例2】在try-except語句中使用
try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except IOError as e: print("I/O error({0}): {1}".format(e.errno, e.strerror)) except ValueError: print("Could not convert data to an integer.") except Exception: print("Unexpected error:", sys.exc_info()[0]) raise
<font size=3>規則 2.4.1 pickle存在安全性問題,禁止使用pickle.load、cPickle.load和shelve模塊加載不可信數據。
<font size=3>規則 2.4.2 使用安全隨機數。</font>
Python產生隨機數的功能在random模塊中實現,實現了各種分布的偽隨機數生成器。產生的隨機數可以是均勻分布,高斯分布,對數正態分布,負指數分布以及alpha,beta分布,但是這些隨機數都是偽隨機數,不能應用於安全加密目的的應用中。
請使用/dev/random生成安全隨機數,或者使用在python 3.6版本官方引入的secrets模塊生成安全隨機數。
【錯誤代碼示例】
import random # 偽隨機數 func = random.SystemRandom() print(func.random()) print(func.randint(0, 10))
【正確代碼示例】
import platform # 長度請參見密碼算法規范,不同場景要求長度不一樣 randLength = 16 if platform.system() == 'Linux': with open("/dev/random", 'rb') as file: sr = file.read(randLength) print(sr)
<font size=3>規則 2.4.3 assert語句通常只在測試代碼中使用,禁止在Release版本中包含assert功能。</font>
assert只應在研發過程中內部測試時使用,出現了AssertionError異常說明存在軟件設計或者編碼上的錯誤,應當修改軟件予以解決。在對外發布的生產版本中禁止包含assert功能。