說明
MindSpore Python編程規(guī)范以PEP8為基礎(chǔ),參考華為Python通用編碼規(guī)范、安全編程規(guī)范,并結(jié)合業(yè)界共識(shí)整理而成,參與MindSpore社區(qū)開發(fā)需要首先遵循本規(guī)范內(nèi)容(與PEP8沖突部分),其余遵循PEP8規(guī)范。
如果對(duì)規(guī)則異議,建議提交issue并說明理由,經(jīng)MindSpore社區(qū)運(yùn)營(yíng)團(tuán)隊(duì)評(píng)審接納后可修改生效。
a
適用范圍
MindSpore開源社區(qū)(https://gitee.com/mindspore/)。
1. 代碼風(fēng)格
1.1 命名
<font size=3>規(guī)則 1.1.1 包名,模塊名:小寫,不使用下劃線。</font>
<font size=3>規(guī)則 1.1.2 類名:使用駝峰格式,首字母大寫,私有類下劃線前綴。</font>
class _Foo:
_instance = None
pass
<font size=3>規(guī)則 1.1.3 函數(shù)名、變量名:小寫,多個(gè)單詞下劃線分割。</font>
def _func_example(path):
pass
<font size=3>建議 1.1.4 除迭代器與計(jì)數(shù)器除外,禁止使用單字符命名。</font>
1.2 格式
<font size=3>規(guī)則 1.2.1 每行字符數(shù)不要超過 120 個(gè)。</font>
如果超過120個(gè)字符,請(qǐng)選擇合理的方式進(jìn)行換行。
<font size=3>規(guī)則 1.2.2 使用空格進(jìn)行縮進(jìn),每次縮進(jìn)4個(gè)空格,禁止tab縮進(jìn)。</font>
<font size=3>規(guī)則 1.2.3 import順序:標(biāo)準(zhǔn)庫、第三方、自定義模塊。</font>
<font size=3>規(guī)則 1.2.4 返回語句和條件語句中不使用括號(hào)。</font>
<font size=3>規(guī)則 1.2.5 模塊級(jí)函數(shù)和類之間雙空行,類成員函數(shù)之間一空行,注釋與代碼間按需添加空行,原則上不超過兩空行。</font>
<font size=3>規(guī)則 1.2.6 無效或冗余代碼直接刪除,不要以注釋、TODO等方式保留在代碼中,建議提issue記錄。</font>
1.3 注釋
<font size=3>規(guī)則 1.3.1 文件頭注釋必須包含版權(quán)聲明。</font>
所有python文件,均需包含如下版權(quán)聲明:
# 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
關(guān)于版權(quán)說明,應(yīng)注意:
2020年新建的文件,應(yīng)該是Copyright 2020 Huawei Technologies Co., Ltd
2019年創(chuàng)建年份,2020年修改年份,應(yīng)該是Copyright 2019-2020 Huawei Technologies Co., Ltd
<font size=3>規(guī)則 1.3.2 對(duì)外的類、方法、算子、Cell注釋格式。</font>
-
class和def的注釋格式相同,采用業(yè)界通用的python注釋語法,寫在聲明下方并縮進(jìn),所有的class和def都需要寫注釋,模塊內(nèi)部的類和方法可以只寫一條簡(jiǎn)介。 - 注釋格式詳見MindSpore注釋規(guī)范。
<font size=3>規(guī)則 1.3.3 不允許通過注釋屏蔽pylint告警。</font>
1.4 日志
<font size=3>規(guī)則 1.4.1 異常日志文本首字母大寫。</font>
<font size=3>規(guī)則 1.4.2 日志文本中變量名必須使用單引號(hào)注明。</font>
2. 通用編碼
2.1 接口聲明
<font size=3>規(guī)則 2.1.1 用戶接口在文件的all中說明,all擺放在import與代碼之間。</font>
<font size=3>規(guī)則 2.1.2 當(dāng)前文件使用的非對(duì)外方法命名采用下劃線前綴,內(nèi)部跨模塊使用的方法無需下劃線前綴,用戶接口在all中聲明。</font>
2.2 數(shù)據(jù)校驗(yàn)
<font size=3>規(guī)則 2.2.1 對(duì)所有外部數(shù)據(jù)進(jìn)行合法性檢查,包括但不限于:函數(shù)入?yún)?、外部輸入命名行、文件格式,文件大小、環(huán)境變量、用戶數(shù)據(jù)等。</font>
<font size=3>建議 2.2.2 必須對(duì)文件路徑進(jìn)行規(guī)范化后再使用。</font>
當(dāng)文件路徑來自外部數(shù)據(jù)時(shí),需要先將文件路徑規(guī)范化,如果沒有作規(guī)范化處理,攻擊者就有機(jī)會(huì)通過惡意構(gòu)造文件路徑進(jìn)行文件的越權(quán)訪問:
例如,攻擊者可以構(gòu)造“../../../etc/passwd”的方式進(jìn)行任意文件訪問。
在linux下,使用realpath函數(shù),在windows下,使用PathCanonicalize函數(shù)進(jìn)行文件路徑的規(guī)范化。
<font size=3>規(guī)則 2.2.3 禁止調(diào)用OS命令解析器執(zhí)行命令或運(yùn)行程序。</font>
使用未經(jīng)校驗(yàn)的不可信輸入作為系統(tǒng)命令的參數(shù)或命令的一部分,可能導(dǎo)致命令注入漏洞。對(duì)于命令注入漏洞,命令將會(huì)以與Python應(yīng)用程序相同的特權(quán)級(jí)別執(zhí)行,它向攻擊者提供了類似系統(tǒng)shell的功能。在Python中,os.system 或 os.popen 經(jīng)常被用來調(diào)用一個(gè)新的進(jìn)程,如果被執(zhí)行的命令來自于外部輸入,則可能會(huì)產(chǎn)生命令和參數(shù)注入。
執(zhí)行命令的時(shí)候,請(qǐng)注意以下幾點(diǎn):
- 命令執(zhí)行的字符串不要去拼接輸入的參數(shù),如果必須拼接時(shí),要對(duì)輸入?yún)?shù)進(jìn)行白名單過濾。
- 對(duì)傳入的參數(shù)要做類型校驗(yàn),例如:整數(shù)數(shù)據(jù),可以對(duì)數(shù)據(jù)進(jìn)行整數(shù)強(qiáng)制轉(zhuǎn)換。
- 保證格式化字符串的正確性,例如:int類型參數(shù)的拼接,對(duì)于參數(shù)要用%d,不能用%s。
【錯(cuò)誤代碼示例1】
攻擊者可以通過找到環(huán)境變量APPHOME對(duì)應(yīng)的值,并且在相應(yīng)目錄下放置常量INITCMD對(duì)應(yīng)的攻擊程序,達(dá)到執(zhí)行的效果:
home = os.getenv('APPHOME')
cmd = os.path.join(home, INITCMD)
os.system(cmd)
【錯(cuò)誤代碼示例2】
沒有校驗(yàn)屬性 backuptype 的值,這個(gè)是用戶輸入的,攻擊者可能進(jìn)行攻擊,
例如:用戶輸入的是:" && del c:\dbms\. ":
# 值來自用戶配置
btype = req.field('backuptype')
cmd = "cmd.exe /K \"c:\\util\\rmanDB.bat " + btype + "&&c:\\util\\cleanup.bat\""
os.system(cmd)
【錯(cuò)誤代碼示例3】
沒有校驗(yàn)屬性 backuptype 的值,這個(gè)是用戶輸入的,攻擊者可能進(jìn)行攻擊,例如:用戶輸入的是:" && del c:\dbms\. ":
import os
import sys
try:
print(os.system("ls " + sys.argv[1]))
except Exception as ex:
print('exception:', ex)
攻擊者可以通過以下命令來利用這個(gè)漏洞程序:
python test.py ". && echo bad"
實(shí)際將會(huì)執(zhí)行兩個(gè)命令:
ls .
echo bad
【正確代碼示例】
避免使用 os.system,可以使用標(biāo)準(zhǔn)的 API 替代運(yùn)行系統(tǒng)命令來完成任務(wù):
import os
import sys
try:
print(os.listdir(sys.argv[1]))
except Exception as ex:
print(ex)
2.3 異常行為
<font size=3>規(guī)則 2.3.1 異常必須被妥當(dāng)處理,禁止抑制或者忽略已檢查異常。</font>
每一個(gè)except 塊都應(yīng)該確保程序只會(huì)在繼續(xù)有效的情況下才會(huì)繼續(xù)運(yùn)行下去。except 塊必須要么從異常情況中恢復(fù),要么重新拋出適合當(dāng)前catch塊上下文的另一個(gè)異常以允許最鄰近的外層try-except 語句塊來進(jìn)行恢復(fù)工作。
【正確代碼示例】
正確的做法是,避免使用 os.system,可以使用標(biāo)準(zhǔn)的 API 替代運(yùn)行系統(tǒng)命令來完成任務(wù):
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()
【例外情況】:
- 在資源釋放失敗不會(huì)影響程序后續(xù)行為的情況下,釋放資源時(shí)發(fā)生的異常可以被抑制。釋放資源的例子包括關(guān)閉文件、網(wǎng)絡(luò)套接字、線程等等。這些資源通常是在except或者fianlly塊中被釋放,并且在后續(xù)的程序運(yùn)行中都不會(huì)再被使用。因此,除非資源被耗盡,否則不會(huì)有其他途徑使得這些異常會(huì)影響程序后續(xù)的行為。在充分處理了資源耗盡問題的情況下,只需對(duì)異常進(jìn)行凈化和記錄日志(以備日后改進(jìn))就足夠了;在這種情況下沒必要做其他額外的錯(cuò)誤處理。
- 如果在特定的抽象層次上不可能從異常情況中恢復(fù)過來,則在那個(gè)層級(jí)的代碼就不用處理這個(gè)異常,而是應(yīng)該拋出一個(gè)合適的異常,讓更高層次的代碼去捕獲處理,并嘗試恢復(fù)。對(duì)于這種情況,最通常的實(shí)現(xiàn)方法是省略掉catch語句塊,允許異常被廣播出去。
<font size=3>規(guī)則 2.3.2 使用try…except…結(jié)構(gòu)對(duì)代碼作保護(hù)時(shí),需要在異常后使用finally…結(jié)構(gòu)保證操作對(duì)象的釋放。</font>
使用try…except…結(jié)構(gòu)對(duì)代碼作保護(hù)時(shí),如果代碼執(zhí)行出現(xiàn)了異常,為了能夠可靠地關(guān)閉操作對(duì)象,需要使用finally…結(jié)構(gòu)確保釋放操作對(duì)象。
【正確代碼示例】
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>規(guī)則 2.3.3 不要使用“except:”語句來捕獲所有異常。</font>
在異常這方面, Python非常寬容,“except:”語句真的會(huì)捕獲包括Python語法錯(cuò)誤在內(nèi)的任何錯(cuò)誤。使用“except:”很容易隱藏真正的bug,我們?cè)谑褂胻ry…except…結(jié)構(gòu)對(duì)代碼作保護(hù)時(shí),應(yīng)該明確期望處理的異常。Exception類是大多數(shù)運(yùn)行時(shí)異常的基類,一般也應(yīng)當(dāng)避免在except語句中使用。通常,try只應(yīng)當(dāng)包含必須要在當(dāng)前位置處理異常的語句,except只捕獲必須處理的異常。比如對(duì)于打開文件的代碼,try應(yīng)當(dāng)只包含open語句,except只捕獲FileNotFoundError異常。對(duì)于其他預(yù)料外的異常,則讓上層函數(shù)捕獲,或者透?jìng)鞯匠绦蛲獠縼沓浞直┞秵栴}。
【錯(cuò)誤代碼示例】
如下代碼可能拋出兩種異常,使用“except:”語句進(jìn)行統(tǒng)一處理時(shí),如果是open執(zhí)行異常,將在“except:”語句之后handle無效的情況下調(diào)用close,報(bào)錯(cuò)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>規(guī)則 2.3.4 不在except分支里面的raise都必須帶異常。</font>
raise關(guān)鍵字單獨(dú)使用只能出現(xiàn)在try-except語句中,重新拋出except抓住的異常。
【錯(cuò)誤代碼示例】
a = 1
if a == 1:
raise
【正確代碼示例1】raise一個(gè)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 序列化和反序列化
<font size=3>規(guī)則 2.4.1 pickle存在安全性問題,禁止使用pickle.load、cPickle.load和shelve模塊加載不可信數(shù)據(jù)。
<font size=3>規(guī)則 2.4.2 使用安全隨機(jī)數(shù)。</font>
Python產(chǎn)生隨機(jī)數(shù)的功能在random模塊中實(shí)現(xiàn),實(shí)現(xiàn)了各種分布的偽隨機(jī)數(shù)生成器。產(chǎn)生的隨機(jī)數(shù)可以是均勻分布,高斯分布,對(duì)數(shù)正態(tài)分布,負(fù)指數(shù)分布以及alpha,beta分布,但是這些隨機(jī)數(shù)都是偽隨機(jī)數(shù),不能應(yīng)用于安全加密目的的應(yīng)用中。
請(qǐng)使用/dev/random生成安全隨機(jī)數(shù),或者使用在python 3.6版本官方引入的secrets模塊生成安全隨機(jī)數(shù)。
【錯(cuò)誤代碼示例】
import random
# 偽隨機(jī)數(shù)
func = random.SystemRandom()
print(func.random())
print(func.randint(0, 10))
【正確代碼示例】
import platform
# 長(zhǎng)度請(qǐng)參見密碼算法規(guī)范,不同場(chǎng)景要求長(zhǎng)度不一樣
randLength = 16
if platform.system() == 'Linux':
with open("/dev/random", 'rb') as file:
sr = file.read(randLength)
print(sr)
<font size=3>規(guī)則 2.4.3 assert語句通常只在測(cè)試代碼中使用,禁止在Release版本中包含assert功能。</font>
assert只應(yīng)在研發(fā)過程中內(nèi)部測(cè)試時(shí)使用,出現(xiàn)了AssertionError異常說明存在軟件設(shè)計(jì)或者編碼上的錯(cuò)誤,應(yīng)當(dāng)修改軟件予以解決。在對(duì)外發(fā)布的生產(chǎn)版本中禁止包含assert功能。