모든 Pythonista가 알고 싶은 import 순서를 프로젝트 내에서 통합하는 방법

이 기사 is 무엇? (TL; DR)


  • Python import 문 순서가 사람에 의해 흩어져 문제를 해결합니다

  • isort을 사용하여 가져 오기 정렬
  • 사전 커밋을 사용하여 가져 오기 순서를 준수하지 않는 코드를 커밋 할 수 없도록 설정

  • Python의 import 순서가 규약으로 정해지지 않은 문제



    Python의 import 순서는 PEP8에 다음과 같이 설명되어 있습니다.
    - Imports are always put at the top of the file, just after any module
      comments and docstrings, and before module globals and constants.
    
      Imports should be grouped in the following order:
    
      1. Standard library imports.
      2. Related third party imports.
      3. Local application/library specific imports.
    
      You should put a blank line between each group of imports.
    

    즉, 다음 순서로 공행을 끼워 쓰면 OK입니다!
    1. 표준 라이브러리
    2. 타사 라이브러리
    3. 자작 라이브러리

    ...에?
    표준 라이브러리끼리의 순서는 어떻게 하면 좋을까?from pathlib import Pathimport sys는 어느 쪽이 먼저인가?

    실은 Python 규약에서는, 표준 라이브러리끼리의 순서등에 대해서는 정해져 있지 않습니다!
    즉, 프로젝트 내에서 통일하는 기준을 정해주지 않으면, 사람에 의해 화려한 쓰는 방법을 해 버리게 됩니다!

    isort를 사용한 솔루션



    isort이라는 도구를 사용하여 프로젝트 내에서 통합 규칙을 결정하고 운영합니다.
  • pip install isort에서 isort 설치
  • (기본 설정 이외의 설정으로 작동하려면) ~/.isort.cfg 파일 편집
  • isort -rc .를 실행하여 프로젝트의 모든 파일을 정의한 규칙으로 정렬

  • 기본 설정(.isort.cfg 파일을 만들지 않는 경우)은 다음과 같이 정렬됩니다!
    기본적으로 PEP8을 따라 정렬하여 좋은 느낌입니다

    정렬 전
    import os
    import sys
    from pathlib import Path
    import numpy as np
    import pandas as pd
    import boto3
    import mylib1
    import mylib2
    

    정렬 후
    import os
    import sys
    from pathlib import Path
    
    import boto3
    import numpy as np
    import pandas as pd
    
    import mylib1
    import mylib2
    

    덤: 정렬되지 않은 파일을 커밋할 수 없게 합니다.



    사전 커밋을 사용하여 정렬되지 않은 파일을 커밋할 수 없도록 하려면 다음과 같이 파일을 편집합니다!
    참고: htps : // 기 st. 기주 b. 코 m/아 c데야/8717683
    1. cp .git/hooks/pre-commit.sample .git/hooks/pre-commit하여 사전 커밋 파일 만들기
    2. pre-commit 파일을 다음 내용으로 덮어쓰기

    ※프로젝트에 의해서 고유의 값이 될 수 있는 개소를 TODO로서 기재하고 있다(2개소) 때문에, 거기를 수정할 필요가 있습니다.
    which python 이나 which isort 의 결과의 패스를 지정해 올리면 OK입니다.

    사전 커밋
    #!/usr/bin/env PYTHONIOENCODING=utf-8 [TODO: プロジェクトのPython環境を記載する]
    # encoding: utf-8
    """Git pre-commit hook which lints Python, JavaScript, SASS and CSS"""
    
    from __future__ import absolute_import, print_function, unicode_literals
    
    import os
    import subprocess
    import sys
    
    FS_ENCODING = sys.getfilesystemencoding()
    
    
    def check_linter(cmd, files, **kwargs):
        if not files:
            return
        print('Running %s' % cmd[0])
        return subprocess.check_output(cmd + files, stderr=subprocess.STDOUT, **kwargs).decode(FS_ENCODING)
    
    
    def filter_ext(extension, files, exclude=None):
        files = [f for f in files if f.endswith(extension)]
        if exclude is not None:
            files = [i for i in files if exclude not in i]
        return files
    
    
    def lint_files(changed_files):
        changed_files = [i.strip() for i in changed_files.splitlines() if '/external/' not in i]
    
        changed_extensions = {ext for root, ext in map(os.path.splitext, changed_files)}
    
        if '.py' in changed_extensions:
            py_files = filter_ext('.py', changed_files)
            check_linter(['[TODO: プロジェクトのisortパスを記載する]', '-c'], py_files)
    
        if '.js' in changed_extensions:
            check_linter(['eslint'], filter_ext('.js', changed_files, exclude='.min.'))
    
        if '.scss' in changed_extensions:
            try:
                check_linter(['scss-lint'], filter_ext('.scss', changed_files))
            except subprocess.CalledProcessError as exc:
                if exc.returncode == 1:
                    # scss-lint rc=1 means no message more severe than a warning
                    pass
                else:
                    raise
    
        if '.css' in changed_extensions:
            check_linter(['csslint'], filter_ext('.css', changed_files, exclude='.min.'))
    
    
    if __name__ == "__main__":
        os.chdir(os.path.join(os.path.dirname(__file__), '..', '..'))
        changed_files = subprocess.check_output('git diff --cached --name-only --diff-filter=ACM'.split())
        changed_files = changed_files.decode(FS_ENCODING)
    
        try:
            lint_files(changed_files)
        except subprocess.CalledProcessError as exc:
            print('Quality check failed:', file=sys.stderr)
            print(' '.join(exc.cmd), file=sys.stderr)
            if exc.output:
                output = exc.output.decode(FS_ENCODING)
                print('\t', '\n\t'.join(output.splitlines()), sep='', file=sys.stderr)
            sys.exit(1)
    

    pre-commit 파일을 작성하고, isort 순서가 지정된 방법이 아닌 파일이 있는 경우, 다음과 같은 에러 메세지가 나와 commit에 실패합니다.
    コミットはエラーで失敗しました
    0 file committed, 1 file failed to commit: [invalid!!]test commit
    ERROR: /Users/xxx/PycharmProjects/isort_test/incorrect_isort.py Imports are incorrectly sorted.
    

    위의 오류로 커밋이 실패하면 isort [ソートしたいファイルパス]를 실행하여 import 순서를 정렬하면 commit 할 수 있습니다!

    덤: PyCharm 사용자용



    PyCharm에서 하나의 명령으로 isort를 실행하지 않으려면 명령을 외부 도구로 등록 할 수 있습니다!
    커뮤니티 버전에서도 사용할 수 있습니다!
    참고: htps : // 기주 b. 코 m / 치모 thyc 로 s ぇ y / 이소 rt / 이스에 s / 258
  • Pycharm > Preferences > External Tools > + 선택
  • 도구 편집 화면에서 다음 이미지와 같이 설정
  • 가져 오기를 정렬하려는 파일을 열고 마우스 오른쪽 버튼을 클릭> External Tools> isort current file

  • ※Pycharm을 일본어화하고 있으므로 일부 화상이 다를지도 모릅니다.




    요약



    The Zen of Python에 따라 단 하나의 즐거운 방법을 팀에서 모색해 봅시다!
    마사카리 환영합니다!

    There should be one-- and preferably only one --obvious way to do it.
    뭔가 좋은 방법이 있을 것이다. 누가 봐도 분명한, 단 하나의 방법이.
                         by The Zen of Python

    좋은 웹페이지 즐겨찾기