本規范以PEP8為基礎,參考華為Python通用編碼規范、安全編程規范,並結合業界共識整理而成,參與MindSpore社區開發需要首先遵循本規范內容(與PEP8沖突部分),其余遵循PEP8規范。
如果對規則異議,建議提交issue並說明理由,經MindSpore社區運營團隊評審接納後可修改生效。
a
MindSpore開源社區
規則 1.1.1 包名,模塊名:小寫,不使用下劃線。
規則 1.1.2 類名:使用駝峰格式,首字母大寫,私有類下劃線前綴。
class _Foo:
_instance = None
pass
規則 1.1.3 函數名、變量名:小寫,多個單詞下劃線分割。
def _func_example(path):
pass
建議 1.1.4 除迭代器與計數器除外,禁止使用單字符命名。
規則 1.2.1 每行字符數不要超過 120 個。
如果超過120個字符,請選擇合理的方式進行換行。
規則 1.2.2 使用空格進行縮進,每次縮進4個空格,禁止tab縮進。
規則 1.2.3 import順序:標准庫、第三方、自定義模塊。
規則 1.2.4 返回語句和條件語句中不使用括號。
規則 1.2.5 模塊級函數和類之間雙空行,類成員函數之間一空行,注釋與代碼間按需添加空行,原則上不超過兩空行。
規則 1.2.6 無效或冗余代碼直接刪除,不要以注釋、TODO等方式保留在代碼中,建議提issue記錄。
規則 1.3.1 文件頭注釋必須包含版權聲明。
所有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
規則 1.3.2 對外的類、方法、算子、Cell注釋格式。
class
和 def
的注釋格式相同,采用業界通用的python注釋語法,寫在聲明下方並縮進,所有的 class
和 def
都需要寫注釋,模塊內部的類和方法可以只寫一條簡介。規則 1.3.3 不允許通過注釋屏蔽pylint告警。
規則 1.4.1 異常日志文本首字母大寫。
規則 1.4.2 日志文本中變量名必須使用單引號注明。
規則 2.1.1 用戶接口在文件的__all__中說明,__all__擺放在import與代碼之間。
規則 2.1.2 當前文件使用的非對外方法命名采用下劃線前綴,內部跨模塊使用的方法無需下劃線前綴,用戶接口在__all__中聲明。
規則 2.2.1 對所有外部數據進行合法性檢查,包括但不限於:函數入參、外部輸入命名行、文件格式,文件大小、環境變量、用戶數據等。
建議 2.2.2 必須對文件路徑進行規范化後再使用。
當文件路徑來自外部數據時,需要先將文件路徑規范化,如果沒有作規范化處理,攻擊者就有機會通過惡意構造文件路徑進行文件的越權訪問:
例如,攻擊者可以構造“…/…/…/etc/passwd”的方式進行任意文件訪問。
在linux下,使用realpath函數,在windows下,使用PathCanonicalize函數進行文件路徑的規范化。
規則 2.2.3 禁止調用OS命令解析器執行命令或運行程序。
使用未經校驗的不可信輸入作為系統命令的參數或命令的一部分,可能導致命令注入漏洞。對於命令注入漏洞,命令將會以與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)
規則 2.3.1 異常必須被妥當處理,禁止抑制或者忽略已檢查異常。
每一個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()
【例外情況】:
規則 2.3.2 使用try…except…結構對代碼作保護時,需要在異常後使用finally…結構保證操作對象的釋放。
使用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:
規則 2.3.3 不要使用“except:”語句來捕獲所有異常。
在異常這方面, 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)
規則 2.3.4 不在except分支裡面的raise都必須帶異常。
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
規則 2.4.1 pickle存在安全性問題,禁止使用pickle.load、cPickle.load和shelve模塊加載不可信數據。
規則 2.4.2 使用安全隨機數。
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)
規則 2.4.3 assert語句通常只在測試代碼中使用,禁止在Release版本中包含assert功能。
assert只應在研發過程中內部測試時使用,出現了AssertionError異常說明存在軟件設計或者編碼上的錯誤,應當修改軟件予以解決。在對外發布的生產版本中禁止包含assert功能。