程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

MindSpore Python編程規範

編輯:Python

說明

本規範以PEP8為基礎,參考華為Python通用編碼規範、安全編程規範,並結合業界共識整理而成,參與MindSpore社區開發需要首先遵循本規範內容(與PEP8沖突部分),其餘遵循PEP8規範。

如果對規則異議,建議提交issue並說明理由,經MindSpore社區運營團隊評審接納後可修改生效。
a

適用範圍

MindSpore開源社區


1. 代碼風格

1.1 命名

規則 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.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.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注釋格式。

  • classdef 的注釋格式相同,采用業界通用的python注釋語法,寫在聲明下方並縮進,所有的 classdef 都需要寫注釋,模塊內部的類和方法可以只寫一條簡介。
  • 注釋格式詳見MindSpore注釋規範。

規則 1.3.3 不允許通過注釋屏蔽pylint告警。

1.4 日志

規則 1.4.1 異常日志文本首字母大寫。

規則 1.4.2 日志文本中變量名必須使用單引號注明。

2. 通用編碼

2.1 接口聲明

規則 2.1.1 用戶接口在文件的__all__中說明,__all__擺放在import與代碼之間。

規則 2.1.2 當前文件使用的非對外方法命名采用下劃線前綴,內部跨模塊使用的方法無需下劃線前綴,用戶接口在__all__中聲明。

2.2 數據校驗

規則 2.2.1 對所有外部數據進行合法性檢查,包括但不限於:函數入參、外部輸入命名行、文件格式,文件大小、環境變量、用戶數據等。

建議 2.2.2 必須對文件路徑進行規範化後再使用。

當文件路徑來自外部數據時,需要先將文件路徑規範化,如果沒有作規範化處理,攻擊者就有機會通過惡意構造文件路徑進行文件的越權訪問:

例如,攻擊者可以構造“…/…/…/etc/passwd”的方式進行任意文件訪問。

在linux下,使用realpath函數,在windows下,使用PathCanonicalize函數進行文件路徑的規範化。

規則 2.2.3 禁止調用OS命令解析器執行命令或運行程序。

使用未經校驗的不可信輸入作為系統命令的參數或命令的一部分,可能導致命令注入漏洞。對於命令注入漏洞,命令將會以與Python應用程序相同的特權級別執行,它向攻擊者提供了類似系統shell的功能。在Python中,os.system 或 os.popen 經常被用來調用一個新的進程,如果被執行的命令來自於外部輸入,則可能會產生命令和參數注入。

執行命令的時候,請注意以下幾點:

  1. 命令執行的字符串不要去拼接輸入的參數,如果必須拼接時,要對輸入參數進行白名單過濾。
  2. 對傳入的參數要做類型校驗,例如:整數數據,可以對數據進行整數強制轉換。
  3. 保證格式化字符串的正確性,例如:int類型參數的拼接,對於參數要用%d,不能用%s。

【錯誤代碼示例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 異常行為

規則 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()

【例外情況】:

  1. 在資源釋放失敗不會影響程序後續行為的情況下,釋放資源時發生的異常可以被抑制。釋放資源的例子包括關閉文件、網絡套接字、線程等等。這些資源通常是在except或者fianlly塊中被釋放,並且在後續的程序運行中都不會再被使用。因此,除非資源被耗盡,否則不會有其他途徑使得這些異常會影響程序後續的行為。在充分處理了資源耗盡問題的情況下,只需對異常進行淨化和記錄日志(以備日後改進)就足夠了;在這種情況下沒必要做其他額外的錯誤處理。
  2. 如果在特定的抽象層次上不可能從異常情況中恢複過來,則在那個層級的代碼就不用處理這個異常,而是應該拋出一個合適的異常,讓更高層次的代碼去捕獲處理,並嘗試恢複。對於這種情況,最通常的實現方法是省略掉catch語句塊,允許異常被廣播出去。

規則 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 序列化和反序列化

規則 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功能。


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved