Skip to content

Latest commit

 

History

History
301 lines (221 loc) · 21 KB

pylint.md

File metadata and controls

301 lines (221 loc) · 21 KB

Pylint

  • Pylint - code analysis for Python | www.pylint.org #ril
    • Slogan 是 Star your Python code!
    • 可以檢查單行程式是不是過長、變數命名是否合格、引入了沒用到的 module、重複的程式碼等。
  • Introduction — Pylint 2.0.0 documentation
    • 用來要求 coding standard、找出 code smell,並提出建議;預設採用近似於 PEP 8 的 coding style。
    • 其他類似的專案有 pychecker (已死)、pyflakes、flake8 跟 mypy。
    • 分析過程會丟出一些不同 category 的 messages -- error、warning,然後提供一個總體評分 (overall mark)
    • What Pylint says is not to be taken as gospel (教義) and Pylint isn’t smarter than you are: it may warn you about things that you have conscientiously (負責盡職地) done. 這句話說得真好。Pylint 會儘力減少 false positive,面對 verboseness 的方式,就是調整哪些 message category 要啟用/停用,可以從 command line 做,也可以由 configuration file 提供 (可以用 --generate-rcfile 產生)
  • Tutorial — Pylint 2.0.0 documentation
    • "become a more aware programmer" 這說法有趣,若要讓別人參與其中 (shared code),Pylint 可以確保你的 code 對其他人是友善的。
  • What is Pylint? - Frequently Asked Questions — Pylint 2.0.0 documentation 是一個 static code checker。

Hello, World! ??

hello.py:

def hello(who):
    print 'Hello, %s!' % who

if __name__ == '__main__':
    print hello(sys.args[1] if len(sys.args) >= 2 else 'World')
$ pylint hello # 注意不是 hello.py
No config file found, using default configuration
************* Module hello
C:  1, 0: Missing module docstring (missing-docstring)
C:  1, 0: Missing function docstring (missing-docstring)
E:  5,35: Undefined variable 'sys' (undefined-variable)
E:  5,16: Undefined variable 'sys' (undefined-variable)

----------------------------------------------------------------------
Your code has been rated at -20.00/10 (previous run: -20.00/10, +0.00)

參考資料:

  • Tutorial — Pylint 2.0.0 documentation #ril
    • 觀察 pylint --long-help 的輸出,這裡只會用到 --generate-rcfile=<file>--help-msg=<msg-id>--disable=<msg-ids>--reports=<y_or_n>,最後面的 Output: 提到有 5 種 message type - Convention、refactor、warning、error、fatal (造成 Pylint 無法解析)。
    • 第一次使用,最常見的抱怨是 Pylint 很吵 (noisy),因為預設的 configuration 會啟用所有的檢查,可以調整為只做想要的檢查。
    • 這裡舉的範例其實有點複雜了,光是 Hello, World! 就可以檢查出很多問題。

新手上路 ?? {: #getting-started }

  • Running Pylint — Pylint 2.0.0 documentation #ril
    • 基本用法是 pylint [OPTIONS] MODULES_OR_PACKAGES (用空白隔開),注意是 module/package,而非 filename;實驗發現,package 下就算是沒用到的 module 也會去掃描。
    • Pylint 雖然不會 import module/package,但會用 Python 內部的機制 (internals) 來找到它們,所以一樣會受到 PYTHONPATH 的影響;也就是環境內要有 package,否則會丟出 [E0401(import-error), ] Unable to import 'xxx' 的錯誤。
    • It is also possible to analyze python files, with a few restrictions. 若傳入 file name,會試著將它轉為 module name,而這個轉換可能失敗,還是少用...
  • Running Pylint - Frequently Asked Questions — Pylint 2.0.0 documentation #ril
  • Pylint output — Pylint 2.0.0 documentation 看懂 CLI 的輸出
    • 預設的 output format 是 raw text,可以用 --output-format=<FORMAT> 調整,可接受的 format 有 text (預設)、jsonparseablecolorizedmsvs (Visual Studio?) 訊息可以用 --msg-template 自訂 message,想起 Jenkins Wranings Plugin 就是解析 build log。
    • 輸出分為 3 塊 -- Source code analysis section、Reports section (--reports=y 啟用) 與 Score section (--score=n 停用)。
    • Source code analysis section 每個 module 都會以 ************* Module XXX 劃分開來,接著條列長得像 MESSAGE_TYPE: LINE_NUM:[OBJECT:] MESSAGE 的訊息。其中 message type 可能是 [R]efactor for a "good practice" metric violation、[C]onvention for coding standard violation、[W]arning for stylistic problems, or minor programming issues、[E]rror for important programming issues (i.e. most probably bug) 或 [F]atal for errors which prevented further processing。
    • Reports section 會從不同的面向統計 message,可以用 --reports=y 啟用;若一直都能維持 10 分,就沒必要看 report 了。
    • 最後的 Score section 會以滿分 10 來計分,公式可以用 --evaluation 來自訂,預設是 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10);看起來是有任何 message,就不可能達到 10 分。

Checker, Message, Message Category ??

Message Control ??

  • Messages control — Pylint 2.0.0 documentation

    用程式碼裡的註解 # pylint: disable=...# pylint: enable=... 來微調。

    """pylint option block-disable""" <-- 這裡也可以寫 pylint: disable=... 嗎??
    
    __revision__ = None
    
    class Foo(object):
        """block-disable test"""
    
        def __init__(self):
            pass
    
        def meth1(self, arg):
            """this issues a message"""
            print self
    
        def meth2(self, arg):
            """and this one not"""
            # pylint: disable=unused-argument
            print self\
                  + "foo"
    
        def meth3(self):
            """test one line disabling"""
            # no error
            print self.bla # pylint: disable=no-member <-- 從這一行開始作用
            # error
            print self.blop
    
        def meth4(self):
            """test re-enabling"""
            # pylint: disable=no-member
            # no error
            print self.bla
            print self.blop
            # pylint: enable=no-member <-- 同一層 block,可以重新 enable/disable
            # error
            print self.blip
    
        def meth5(self):
            """test IF sub-block re-enabling"""
            # pylint: disable=no-member <-- 作用在所在的 block 及 sub-block
            # no error
            print self.bla
            if self.blop:
                # pylint: enable=no-member <-- sub-block 也可以重新 enable/disable
                # error
                print self.blip
            else:
                # no error
                print self.blip
            # no error
            print self.blip
    
        def meth6(self):
            """test TRY/EXCEPT sub-block re-enabling"""
            # pylint: disable=no-member
            # no error
            print self.bla
            try:
                # pylint: enable=no-member
                # error
                print self.blip
            except UndefinedName: # pylint: disable=undefined-variable <-- except 可以分開控制
                # no error
                print self.blip
            # no error
            print self.blip
    
        def meth7(self):
            """test one line block opening disabling"""
            if self.blop: # pylint: disable=no-member <-- 可以寫在 block opening 那行
                # error
                print self.blip
            else:
                # error
                print self.blip
            # error
            print self.blip
    
    
        def meth8(self):
            """test late disabling"""
            # error
            print self.blip
            # pylint: disable=no-member
            # no error
            print self.bla
            print self.blop
    
  • Configuration — Pylint 2.0.0 documentation #ril

  • Pylint - code analysis for Python | www.pylint.org Fully customizable 提到可以用 pylintrc 自訂 convention。

  • Pylint features — Pylint documentation https://pylint.readthedocs.io/en/latest/technical_reference/features.html #ril

Lint 也要透過 tox 來做 ??

  • Static analysis 應該發生在 build 之前;若 Pylint 會檢查 import 的套件在不在,就必須要把 dependencies 都裝起來。
  • tox --disable=import-error 似乎是個方法? 更何況這種錯誤本來就不像 lint,應該用 unit tests 去確保才對。

參考資料:

在多個專案間共用 pylintrc ??

  • 用 "pylintrc share" 找不到任何資料,或許自己維護幾個 template project 吧?

如何在 Jenkins 裡顯示 Pylint 檢查的結果??

如何在 Vim 裡就提示不符合 Pylint 要求的部份??

如何產生 UML diagrams??

no-else-return (R1705) ??

  • 如果做為 guard statements,當然不用 else,但如果是在做分支 -- 模擬 swtich 互斥的結構,覺得還是用 else 比較好。
  • 當然,如果遵守 function 不該有多個 return 的原則,就不會有這個問題。

參考資料:

unsubscriptable-object (E1136) ??

useless-object-inheritance (R0205) ??

en-as-condition (C1801) ??

broad-except (W0702)

  • Pylint features — Pylint 2.4.0-dev0 documentation

    broad-except (W0703):

    • Catching too general exception %s Used when an except catches a too general exception, possibly BURYING unrelated errors.

      上面 "burying unrelated errors" 暗示著,只要在 handler 裡有 reraise 的話,就不會有警告。

安裝設置 {: #setup }

Pylint 1.x, 2.x ??

安裝 Pylint

參考資料:

整合 Jenkins??

參考資料 {: #reference }

社群:

文件:

手冊: