MindSpore Python編程規(guī)范

說明

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>

  • classdef 的注釋格式相同,采用業(yè)界通用的python注釋語法,寫在聲明下方并縮進(jìn),所有的 classdef 都需要寫注釋,模塊內(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):

  1. 命令執(zhí)行的字符串不要去拼接輸入的參數(shù),如果必須拼接時(shí),要對(duì)輸入?yún)?shù)進(jìn)行白名單過濾。
  2. 對(duì)傳入的參數(shù)要做類型校驗(yàn),例如:整數(shù)數(shù)據(jù),可以對(duì)數(shù)據(jù)進(jìn)行整數(shù)強(qiáng)制轉(zhuǎn)換。
  3. 保證格式化字符串的正確性,例如: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()

【例外情況】:

  1. 在資源釋放失敗不會(huì)影響程序后續(xù)行為的情況下,釋放資源時(shí)發(fā)生的異常可以被抑制。釋放資源的例子包括關(guān)閉文件、網(wǎng)絡(luò)套接字、線程等等。這些資源通常是在except或者fianlly塊中被釋放,并且在后續(xù)的程序運(yùn)行中都不會(huì)再被使用。因此,除非資源被耗盡,否則不會(huì)有其他途徑使得這些異常會(huì)影響程序后續(xù)的行為。在充分處理了資源耗盡問題的情況下,只需對(duì)異常進(jìn)行凈化和記錄日志(以備日后改進(jìn))就足夠了;在這種情況下沒必要做其他額外的錯(cuò)誤處理。
  2. 如果在特定的抽象層次上不可能從異常情況中恢復(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功能。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容