파이썬 코드 스타일(pep8)을 Black으로 자동 포맷팅하기

PEP8

파이썬을 배우고 있거나 사용하는 개발자라면 대부분 PEP8 에 대해 들어 봤거나 한두 번씩은 읽어 봤을 것이다. PEP8은 파이썬 코드 스타일에 대한 가이드이다.

파이썬 개발자라면 대부분 이 PEP8을 준수하려고 노력할 것이다. 하지만 오랜 시간 파이썬으로 개발해 온 개발자라도 PEP8의 내용을 전부 기억하고, 지키는 것은 힘들 수 있다.

그래서 보통 코드 스타일을 자동으로 검사해주는 flake8 이나 pycodestyle 같은 도구들을 함께 사용한다. 하지만 이 도구들은 코드 스타일을 체크해주는 역할만 하기 때문에 코드를 고쳐야 하는 것은 개발자 스스로 수행해야 한다.

처음 파이썬을 배울 때는 이런 식으로 직접 검사하고, 교정하는 것이 학습에 도움이 될 수 있지만, 코드량이 많거나 다른 개발자들과 협업하는 프로젝트에서 실수로 수정되지 않은 스타일의 코드가 커밋될 위험이 있다.

아래에서는 파이썬 코드 스타일을 자동으로 검사하고, 교정해주는 포맷터(formatter)에 대해 알아볼 것이다.

Black 공식 Github에는 “타협하지 않는”, “단호한” 의미를 풍기는 Uncompromising Code Formatter라고 소개하고 있으며, 개발자가 코드 스타일을 고치기 위해 고민하고 결정하는 시간을 절약할 수 있고 코드 스타일로 생기는 사소한 갈등을 방지해서 더 중요한 문제 해결에 집중할 수 있게 해준다.

black {소스코드 파일 경로 또는 디렉터리}

본인의 파이썬 개발 환경에서 사용하는 IDE나 Editor에 Black을 바로 실행할 수 있도록 설정 해두면 더 편하고 빠르게 코드 스타일을 보정할 수 있다.

def sample_function(
    template, file_path, header, debug=None, db_sync=False, *args, **kwargs
):
    pass

만약 그래도 88자가 넘으면 콤마로 구분된 실행 파라미터들을 하나씩 분해해서 위와 동일한 규칙을 적용한다

def sample_function(
    template,
    file_path,
    title,
    contents,
    header,
    debug=None,
    db_sync=False,
    *args,
    **kwargs
):
    pass

나도 예전부터 PEP8의 80자 제안은 잘 지키지 않는 편이었다. 한 줄에 완전한 문장이 다 들어올 때 가독성이 더 높은 것도 있고, 요즘 같이 고해상도 모니터에서 굳이 80자만 볼 필요가 있을까 싶었다. 하지만 Black의 88자 규칙도 나쁘지 않다고 생각하는 이유는 한 줄의 길이가 길면 Diff를 할 때 확실히 비효율적이긴 하기 때문이다.

만약 Flake8을 사용한다면 max-line-length 설정을 88자로 설정하거나 E501 에러를 제외시키고, flake8-bugbear 를 함께 사용해서 B950을 추가하는 것을 추천한다.

아래 .flake8 설정을 참고하자.

[flake8]
max-line-length = 80
...
select = C,E,F,W,B,B950
ignore = E203, E501, W503

튜플(tuple)의 경우 단 1개의 요소만 갖고 있다면 마지막 콤마를 제거하지 않는다. 그 이유는 데이터 타입이 변경될 위험이 있기 때문이다.

bar = (1,)
foo = (1)

print(type(bar))  # <class 'tuple'>
print(type(foo))  # <class 'int'>

기존에 작은따옴표를 컨벤션으로 정했는데 Black을 사용하려고 하면 어쩔 수 없이 큰 따옴표로 규칙으로 바꿔야 한다. 이 과정에서 의견이 분분할 수 있는데 이때는 다음과 같이 해보는 것을 제안하는 것도 좋을 것 같다.

키보드에서 작은따옴표를 입력하는 게 큰 따옴표를 입력하기 위해 Shift키를 함께 누르는 것보다 편하고 빠르기 때문에 코딩할 때는 작은따옴표를 사용하자. 그리고 IDE에 Black을 자동 실행하도록 설정하거나 Commit 전에 Black으로 자동 교정하는 것이다.

# 원본
income = (gross_wages +
          taxable_interest +
          (dividends - qualified_dividends) -
          ira_deduction -
          student_loan_interest)
# Black 실행 결과
income = (
    gross_wages
    + taxable_interest
    + (dividends - qualified_dividends)
    - ira_deduction
    - student_loan_interest
)
bar = [1, 2, 3, 4, 5]

foo = bar[2 - 1 : -1]
# 원본
manage = OrderManage.objects.select_related(
    'order_item_no', 'order_item_no__brand_no',
    'order_delivery_no', 'order_delivery_no__order_no', 'order_delivery_no__order_no__user_grade',
    'order_item_no__item_no', 'order_item_no__option_no', 'order_no'
).prefetch_related(
    Prefetch(
        'order_delivery_no__details',
        queryset=OrderDeliveryDetail.objects.select_related(
            'delivery_company_no'
        ).filter(is_deleted=const.FALSE, is_cancel=const.FALSE).all()
    )
).filter(is_deleted=False).order_by('-order_item_no__brand_no')
# Black 실행 결과
manage = (
    OrderManage.objects.select_related(
        "order_item_no",
        "order_item_no__brand_no",
        "order_delivery_no",
        "order_delivery_no__order_no",
        "order_delivery_no__order_no__user_grade",
        "order_item_no__item_no",
        "order_item_no__option_no",
        "order_no",
    )
    .prefetch_related(
        Prefetch(
            "order_delivery_no__details",
            queryset=OrderDeliveryDetail.objects.select_related("delivery_company_no")
            .filter(is_deleted=const.FALSE, is_cancel=const.FALSE)
            .all(),
        )
    )
    .filter(is_deleted=False)
    .order_by("-order_item_no__brand_no")
)

자신이 속한 그룹이나 개인마다 선호하는 코드 스타일 가이드를 정하는 것도 좋지만, 가장 중요하게 생각하는 가치는 **“가독성”**이다. 그렇기 때문에 보편적으로 따르는 관행적인 코드 스타일 규칙을 채택하는 것이 고민과 갈등을 줄이는 방법이 될 수 있다.


comments powered by Disqus
TAGS
On this page