python 잔소리꾼과 함께 코딩하기.

pylint aka 시어머니.

  • c, java 등을 코딩하다가 파이썬으로 코딩하기 시작하면, 갑자기 신세계에 당도한 기분이죠. 변수에 타입을 지정할 필요도 없고, 알아서 잘 처리해주니까요.
  • 이 문제는 짧게 짧게 코딩할 때는 별 문제가 없지만, 길게 코딩을 하기 시작하면, 몇 가지 문제점들이 발생하기 시작합니다. 모든 사람들이 자기 마음대로 코딩을 하기 시작하는 것이죠. 즉 code reuse 측면에서 문제점들이 발생하고, 협업/공유 측면에서 생산성이 극도로 떨어지기 쉽습니다.
  • 파이썬으로 만들어지는 코드들을 어떤 표준대로 제어하기 위해서 PEP08이 있습니다. 자세한 내용은 해당 링크를 타고 들어가시면 자세하게 알 수 있습니다.
    • 변수 들여쓰기, 변수 명은 capitalized 여야 하는가, underscore로 써야 하는가 등에 대해서 공통된 스타일 가이드를 제시한다고 보시면 됩니다.
  • 그런데, 사실 이를 달달 외워서 쓰는 건 어렵잖아요. 그래서 pylint라는 잔소리꾼이 있습니다.

pylint

  • pylint는 이미 만들어진 .py에 대해서 코드를 텍스트로 죽 읽고, 뭐가 문제인지 에러메시지를 출력해주는 프로그램을 말합니다.
  • python code 내부에서 돌아가는 것이 아니고, 외부에서 즉, 터미널에서 돌려야 합니다.

  • 일단 설치를 하고(맥의 경우는 pip로 설치합니다. ),
pip install pylint
  • 다음으로 실행을 해줍니다. 그럼 잘 됩니다.
pylint aaa.py

in jupyter

  • 그러나, 저는 jupyter notebook에서 작업을 합니다. 이 경우에는 작업 파일이 .py로 있는 것이 아니라, .ipynb 파일로 있습니다. 즉, pylint로는 잘 안되죠.
  • 분명히, 누군가 해놨을 것이라는 강한 믿음 아래, 찾아보니 pycodestyle이라는 것이 있습니다.

pycodestyle

  • 설치를 합시다.
pip install pycodestyle
pip install pycodestyle_magic
  • 이제 jupyter notebook에서 한번 돌려봅시다. 우선 아무 셀에서나 아래 부분을 실행해주고
%load_ext pycodestyle_magic
  • 아래의 개떡 같은 코드를 실행해봅시다. 어디에서 PEP8을 위반하는지를 잘 알려줍니다.
    • 다만, naming에서 발생하는 문제점들은 말해주지 않아요.
    • 예를 들어서, 원래 Class를 정의할 때 class 이름은 Capitalized로 해주고, 변수 이름들은 소문자+언더스코어 로 해주는 것이 기본인데, 그런 부분은 여기에서 따로 체크해주지 않네요. 그 부분은 좀 아쉽습니다.
%%pycodestyle
# remove whitespace
import pandas as pd    

class aaa(object):
    def __init__(self):
        print("dd")
A = aaa()
print(A)

# import module should be at top 
import numpy as np 
# not A is B ==> A is not B
if not True is False:
    print("d")

# line too long 
print("dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd")
  • 아래처럼 어디에서 문제가되는지를 자세하게 알려줍니다.
2:20: W291 trailing whitespace
4:1: E302 expected 2 blank lines, found 1
7:1: E305 expected 2 blank lines after class or function definition, found 0
10:33: W291 trailing whitespace
11:1: E402 module level import not at top of file
11:19: W291 trailing whitespace
13:4: E714 test for object identity should be 'is not'
16:16: W291 trailing whitespace
17:80: E501 line too long (95 > 79 characters)
  • 문제가 있는 부분을 모두 바꾸어 주면 댜음과 같습니다. 단 앞서 말씀드린 바와 같이 naming convention에 대해서는 pycodestyle이 잡아주지 않습니다. 이 부분이 좀 아쉽네요.
%%pycodestyle
# remove whitespace
import pandas as pd
import numpy as np


class aaa(object):
    def __init__(self):
        print("dd")


A = aaa()
print(A)
if True is not False:
    print("d")
print("dddddddddddddddddddddddddddddddddddddddddd")

NamingConvention

  • pycodestyle documentation을 보니 아래와 같은 부분이 있습니다.

naming conventions: this kind of feature is supported through plugins. Install flake8 and the pep8-naming extension to use this feature.

  • flake8, pep8-naming 을 설치하면, 된다는 말이므로, 이 둘을 설치하고 다시 해볼게요.
pip install flake8
pip install pep8-naming
  • 그러나, 이걸 설치하고 나서도 jupyter notebook에서는 잘 되지 않습니다.
  • flake8의 경우, jupyter notebook에서 바로 돌아가지는 않고 shell에서 .py을 대상으로 잘 돌아갑니다.
  • test.py에 위에서 작성한 코드를 만들고 실행을 하면, 아래 처럼 다양한 코드들을 잡아주는 것을 알 수 있습니다. 각 코드는 다음으로 구분된다고 합니다.
    • E/W: pep8 errors and warnings
    • F: PyFlakes codes (see below)
    • C9: McCabe complexity plugin mccabe
    • N8: Naming Conventions plugin pep8-naming
Leeseunghoonui-MacBook-Air:~ frhyme$ vi test.py
Leeseunghoonui-MacBook-Air:~ frhyme$ flake8 test.py
test.py:2:1: F401 'pandas as pd' imported but unused
test.py:2:20: W291 trailing whitespace
test.py:4:1: E302 expected 2 blank lines, found 1
test.py:4:7: N801 class name 'aaa' should use CapWords convention
test.py:7:1: E305 expected 2 blank lines after class or function definition, found 0
test.py:10:33: W291 trailing whitespace
test.py:11:1: E402 module level import not at top of file
test.py:11:1: F401 'numpy as np' imported but unused
test.py:11:19: W291 trailing whitespace
test.py:13:4: E714 test for object identity should be 'is not'
test.py:16:16: W291 trailing whitespace
test.py:17:80: E501 line too long (95 > 79 characters)
  • 당연히도, 아래 내용도 터미널에서 수행할 수 있지만, naming convention을 수행하지 못합니다.
pycodestyle test.py

flake8 in jupyter notebook

  • 자, 이제 쥬피터 노트북에서 flake8을 매직 커맨드의 형식으로 수행할 수 있으면 최고라고 생각되는데, 어떻게 할 수 있을까요?

  • 만약 파일이 있을 경우에는 그냥 다음처럼 수행해도 되기는 합니다. jupyter notebook에서 !로 magic command를 수행할 경우, 연결된 터미널에서 직접 bash command를 수행하는 것과 마찬가지인 것 같아요. 또한 그 결과값을 리턴하기 때문에, 그 값을 다음처럼 바로 보여줄 수 있죠.

!flake8 ../../../test.py
../../../test.py:2:1: F401 'pandas as pd' imported but unused
../../../test.py:2:20: W291 trailing whitespace
../../../test.py:4:1: E302 expected 2 blank lines, found 1
../../../test.py:4:7: N801 class name 'aaa' should use CapWords convention
../../../test.py:7:1: E305 expected 2 blank lines after class or function definition, found 0
../../../test.py:10:33: W291 trailing whitespace
../../../test.py:11:1: E402 module level import not at top of file
../../../test.py:11:1: F401 'numpy as np' imported but unused
../../../test.py:11:19: W291 trailing whitespace
../../../test.py:13:4: E714 test for object identity should be 'is not'
../../../test.py:16:16: W291 trailing whitespace
../../../test.py:17:80: E501 line too long (95 > 79 characters)
  • 하지만, 우리는 현재 셀의 코드에 flake8을 먹이고 싶은 거니까요. 이럴 때 이런 식으로 우회해서 처리할 수 있어요.
  • 우선 magic command로 해당 코드의 파일을 임의의 파일로 저장하고,
%%writefile test.py
  • 다음 셀에서 아래 커맨드를 수행하면 됩니다. 중간에 쓸데없에 파일 하나를 거친다는 것이 좀 마음에 안들기는 하는데, 되는게 어딘가 싶어요.
!flake8 test.py

with extension

  • 마음에 안들기 때문에 extension을 설치해보기로 합니다.
jupyter labextension install jupyterlab-flake8
  • 딴 것들을 설치하라고 하는군요. 이렇게 까지 해야 하나 싶기는 한데..흠…
Leeseunghoonui-MacBook-Air:~ frhyme$ jupyter labextension install jupyterlab-flake8

Errored, use --debug for full output:
ValueError: Please install nodejs 5+ and npm before continuing. nodejs may be installed using conda or directly from the nodejs website.
  • 일단 더이상 진행하지 않기로 결정했습니다. 여러가지 이유가 있겠지만, 브라우저 위에서 돌아가는 jupyter notebook가 느려지는 건 싫거든요.

wrap-up

  • 그냥 필요할 때, 임시 파일을 만들고, flake8 test.py 로 하는게 더 좋을 것 같습니다.

댓글남기기