파이썬 AST 파싱 및 사용자 정의 린팅
📚 콘텐츠 목차
- 소개
- Python 코드 내에서 Python 코드 탐색하기
- flake8을 사용하여 코드 품질 관리하기
- flake8 플러그인 작성하기
- 사용자 정의 규칙 추가하기
- 함수 내 로컬 임포트 금지하기
- 추상 구문 트리와 ast 모듈
- ast 모듈을 사용하여 소스 코드 분석하기
- NodeVisitor 클래스와 코드 트리 탐색
- flake8 플러그인 구현하기
- 플러그인 환경 설정하기
- 하나 이상의 체크 규칙 추가하기
- 대안 옵션 추가하기
- 플러그인 테스트 및 패키징하기
- 과제: 사용자 정의 규칙 구현하기
- 끝내며
🎯 소개
안녕하세요! 제임스 머피입니다. 이번 영상에서는 Python 코드 내에서 코드를 탐색하는 방법을 배워보겠습니다. 이 능력을 활용하여 코드 품질을 유지하고 일반적인 오류를 방지하기 위한 사용자 정의 체크를 작성할 것입니다. 주로, flake8을 이용한 사용자 정의 플러그인 작성에 초점을 맞출 것입니다. flake8은 소스 코드를 실행하고 잘못된 부분을 알려주는 린터(linter)입니다.
📝 Python 코드 내에서 Python 코드 탐색하기
추상 구문 트리와 ast 모듈
Python은 개념을 이해하는 데 도움이 되는 데이터 구조를 제공합니다. 이를 통해 우리는 Python 소스 코드를 분석할 수 있습니다. Python은 ast(추상 구문 트리) 모듈을 제공하여 코드의 추상 구문 트리 표현을 인쇄할 수 있습니다. 아래의 예시 코드를 실행하면 추상 구문 트리를 확인할 수 있습니다.
import ast
code = """
def hello():
x = 1
y = 2
return x + y
"""
tree = ast.parse(code)
print(tree)
이렇게 실행하면 코드의 추상 구문 트리를 확인할 수 있습니다. 트리는 모듈, 함수 정의, 변수 등의 객체로 구성됩니다. 이러한 정보를 활용하여 코드를 분석하고 수정할 수 있습니다.
NodeVisitor 클래스와 코드 트리 탐색
ast 모듈에는 NodeVisitor 클래스가 제공됩니다. 이 클래스를 사용하면 코드 트리를 전체적으로 탐색하고 각 노드를 방문할 수 있습니다. 아래의 예시 코드를 살펴보겠습니다.
import ast
class MyVisitor(ast.NodeVisitor):
def visit_FunctionDef(self, node):
print(f"함수 이름: {node.name}")
self.generic_visit(node)
code = """
def hello():
x = 1
y = 2
return x + y
"""
tree = ast.parse(code)
visitor = MyVisitor()
visitor.visit(tree)
위 코드를 실행하면 함수 이름을 출력하는 것을 확인할 수 있습니다. self.generic_visit(node)
를 호출함으로써 모든 자식 노드를 방문할 수 있습니다.
🔧 flake8을 사용하여 코드 품질 관리하기
flake8은 코드 품질을 분석하고 검사하는 도구입니다. 우리는 이를 활용하여 코드에 오류나 스타일 관련 문제가 있는지 확인할 수 있습니다. flake8은 이미 많은 규칙을 내장하고 있으며, 필요한 경우 일부 규칙을 비활성화할 수도 있습니다.
flake8 플러그인 작성하기
특정한 규칙을 추가하고 싶은 경우 어떻게 해야 할까요? 예를 들어, 함수 내 로컬 임포트를 금지하고 싶다고 가정해봅시다. 우리는 사용자 정의 flake8 플러그인을 작성하여 이 규칙을 추가할 수 있습니다.
사용자 정의 규칙 추가하기
플러그인을 작성하기 전에, 먼저 flake8에 사용자 정의 규칙을 추가해야 합니다. 이를 위해 플러그인 클래스를 만들고 이름과 버전을 지정해야 합니다. 플러그인 클래스는 flake8_plugin.py
라는 이름으로 저장합니다.
class CustomPlugin:
name = "custom-plugin"
version = "0.1"
def __init__(self, tree):
self.tree = tree
def run(self):
yield
또한, .flake8
이라는 이름의 flake8 설정 파일을 생성해야 합니다. 이 파일은 플러그인의 위치를 알려줄 것입니다.
[flake8]
plugins = custom-plugin
위와 같이 설정한 다음, 플러그인을 실행하면 실행 메시지가 출력됩니다.
함수 내 로컬 임포트 금지하기
추가한 규칙 중 하나는 함수 내 로컬 임포트를 금지하는 것입니다. 이를 위해 NodeVisitor를 사용하여 코드를 분석하고 로컬 임포트를 찾아내는 작업을 수행합니다. 예시 코드는 다음과 같습니다.
import ast
class LocalImportVisitor(ast.NodeVisitor):
def __init__(self):
self.errors = []
def visit_FunctionDef(self, node):
for child in node.body:
if isinstance(child, ast.Import) or isinstance(child, ast.ImportFrom):
self.errors.append({
"line_number": child.lineno,
"column_offset": child.col_offset,
"message": "함수 내에서 로컬 임포트는 허용되지 않습니다.",
"class": CustomPlugin,
})
self.generic_visit(node)
전체 함수 정의를 방문하고, 그 안에서 로컬 임포트를 검사합니다. 로컬 임포트가 발견되면 플러그인에 오류를 추가합니다. 모든 함수 정의를 방문한 후, 오류 목록이 self.errors
에 저장됩니다.
이제 플러그인에서 위 Visitor를 호출하고 오류를 반환하면 됩니다.
flake8 플러그인 구현하기
이제 플러그인을 구현하는 코드에 Visitor를 호출하고 오류를 반환하는 부분을 추가하겠습니다.
import ast
class CustomPlugin:
name = "custom-plugin"
version = "0.1"
def __init__(self, tree):
self.tree = tree
def run(self):
visitor = LocalImportVisitor()
visitor.visit(self.tree)
for error in visitor.errors:
yield (
error["line_number"],
error["column_offset"],
error["message"],
error["class"]
)
위와 같이 플러그인 실행에서 Visitor를 호출하고 오류를 반환하도록 변경하고, 오류의 세부 정보를 yield해야 합니다.
이제 flake8을 실행하면 로컬 임포트 사용을 금지하는 규칙이 적용되었음을 확인할 수 있습니다. 각 오류는 정확한 라인 번호와 컬럼 오프셋을 포함합니다.
✔️ 하나 이상의 체크 규칙 추가하기
하나 이상의 체크 규칙을 추가하려면 방문할 노드 메소드를 수정하여 특정 규칙에만 적용되도록 해야 합니다. 각 규칙에 대한 새로운 클래스를 만들고 메시지와 체크 함수를 포함해야 합니다. 필요한 경우 옵션을 추가할 수도 있습니다.
옵션을 추가하려면 @staticmethod add_options를 작성하고 option_manager를 인자로 받아야 합니다. 이를 통해 argparse를 사용하여 옵션을 지정할 수 있습니다.
값을 확인하려면 parse_options라는 다른 @staticmethod를 작성해야 합니다. 이 함수에는 options 매개변수가 필요하며, argparse.Namespace를 통해 전달됩니다. 여기에서 지정된 옵션 값을 사용하여 원하는 작업을 수행할 수 있습니다.
테스트와 패키징에 관한 더 자세한 내용은 비디오를 참고하십시오.
📝 과제: 사용자 정의 규칙 구현하기
다음 과제를 통해 규칙을 구현해보세요.
Eval
함수의 사용을 경고하는 플러그인을 작성하세요.
- 새로운 메타 클래스 생성을 금지하는 규칙을 적용하세요.
- 동일한 이름의 변수를 여러 번 사용하지 못하도록 제한하는 플러그인을 작성하세요.
위 문제들은 어렵지 않으며, 주어진 예시와 비슷한 방식으로 구현할 수 있습니다.
📚 콘텐츠 목차
- 소개
- Python 코드 내에서 Python 코드 탐색하기
- flake8을 사용하여 코드 품질 관리하기
- flake8 플러그인 작성하기
- 사용자 정의 규칙 추가하기
- 함수 내 로컬 임포트 금지하기
- 추상 구문 트리와 ast 모듈
- ast 모듈을 사용하여 소스 코드 분석하기
- NodeVisitor 클래스와 코드 트리 탐색
- flake8 플러그인 구현하기
- 플러그인 환경 설정하기
- 하나 이상의 체크 규칙 추가하기
- 대안 옵션 추가하기
- 플러그인 테스트 및 패키징하기
- 과제: 사용자 정의 규칙 구현하기
- 끝내며
🎯 소개
안녕하세요! 제임스 머피입니다. 이번 영상에서는 Python 코드 내에서 코드를 탐색하는 방법을 배워보겠습니다. 이 능력을 활용하여 코드 품질을 유지하고 일반적인 오류를 방지하기 위한 사용자 정의 체크를 작성할 것입니다. 주로, flake8을 이용한 사용자 정의 플러그인 작성에 초점을 맞출 것입니다. flake8은 소스 코드를 실행하고 잘못된 부분을 알려주는 린터(linter)입니다.
📝 Python 코드 내에서 Python 코드 탐색하기
추상 구문 트리와 ast 모듈
Python은 개념을 이해하는 데 도움이 되는 데이터 구조를 제공합니다. 이를 통해 우리는 Python 소스 코드를 분석할 수 있습니다. Python은 ast(추상 구문 트리) 모듈을 제공하여 코드의 추상 구문 트리 표현을 인쇄할 수 있습니다. 아래의 예시 코드를 실행하면 추상 구문 트리를 확인할 수 있습니다.
import ast
code = """
def hello():
x = 1
y = 2
return x + y
"""
tree = ast.parse(code)
print(tree)
이렇게 실행하면 코드의 추상 구문 트리를 확인할 수 있습니다. 트리는 모듈, 함수 정의, 변수 등의 객체로 구성됩니다. 이러한 정보를 활용하여 코드를 분석하고 수정할 수 있습니다.
NodeVisitor 클래스와 코드 트리 탐색
ast 모듈에는 NodeVisitor 클래스가 제공됩니다. 이 클래스를 사용하면 코드 트리를 전체적으로 탐색하고 각 노드를 방문할 수 있습니다. 아래의 예시 코드를 살펴보겠습니다.
import ast
class MyVisitor(ast.NodeVisitor):
def visit_FunctionDef(self, node):
print(f"함수 이름: {node.name}")
self.generic_visit(node)
code = """
def hello():
x = 1
y = 2
return x + y
"""
tree = ast.parse(code)
visitor = MyVisitor()
visitor.visit(tree)
위 코드를 실행하면 함수 이름을 출력하는 것을 확인할 수 있습니다. self.generic_visit(node)
를 호출함으로써 모든 자식 노드를 방문할 수 있습니다.
🔧 flake8을 사용하여 코드 품질 관리하기
flake8은 코드 품질을 분석하고 검사하는 도구입니다. 우리는 이를 활용하여 코드에 오류나 스타일 관련 문제가 있는지 확인할 수 있습니다. flake8은 이미 많은 규칙을 내장하고 있으며, 필요한 경우 일부 규칙을 비활성화할 수도 있습니다.
flake8 플러그인 작성하기
특정한 규칙을 추가하고 싶은 경우 어떻게 해야 할까요? 예를 들어, 함수 내 로컬 임포트를 금지하고 싶다고 가정해봅시다. 우리는 사용자 정의 flake8 플러그인을 작성하여 이 규칙을 추가할 수 있습니다.
사용자 정의 규칙 추가하기
플러그인을 작성하기 전에, 먼저 flake8에 사용자 정의 규칙을 추가해야 합니다. 이를 위해 플러그인 클래스를 만들고 이름과 버전을 지정해야 합니다. 플러그인 클래스는 flake8_plugin.py
라는 이름으로 저장합니다.
class CustomPlugin:
name = "custom-plugin"
version = "0.1"
def __init__(self, tree):
self.tree = tree
def run(self):
yield
또한, .flake8
이라는 이름의 flake8 설정 파일을 생성해야 합니다. 이 파일은 플러그인의 위치를 알려줄 것입니다.
[flake8]
plugins = custom-plugin
위와 같이 설정한 다음, 플러그인을 실행하면 실행 메시지가 출력됩니다.
함수 내 로컬 임포트 금지하기
추가한 규칙 중 하나는 함수 내 로컬 임포트를 금지하는 것입니다. 이를 위해 NodeVisitor를 사용하여 코드를 분석하고 로컬 임포트를 찾아내는 작업을 수행합니다. 예시 코드는 다음과 같습니다.
import ast
class LocalImportVisitor(ast.NodeVisitor):
def __init__(self):
self.errors = []
def visit_FunctionDef(self, node):
for child in node.body:
if isinstance(child, ast.Import) or isinstance(child, ast.ImportFrom):
self.errors.append({
"line_number": child.lineno,
"column_offset": child.col_offset,
"message": "함수 내에서 로컬 임포트는 허용되지 않습니다.",
"class": CustomPlugin,
})
self.generic_visit(node)
전체 함수 정의를 방문하고, 그 안에서 로컬 임포트를 검사합니다. 로컬 임포트가 발견되면 플러그인에 오류를 추가합니다. 모든 함수 정의를 방문한 후, 오류 목록이 self.errors
에 저장됩니다.
이제 플러그인에서 위 Visitor를 호출하고 오류를 반환하면 됩니다.
flake8 플러그인 구현하기
이제 플러그인을 구현하는 코드에 Visitor를 호출하고 오류를 반환하는 부분을 추가하겠습니다.
import ast
class CustomPlugin:
name = "custom-plugin"
version = "0.1"
def __init__(self, tree):
self.tree = tree
def run(self):
visitor = LocalImportVisitor()
visitor.visit(self.tree)
for error in visitor.errors:
yield (
error["line_number"],
error["column_offset"],
error["message"],
error["class"]
)
위와 같이 플러그인 실행에서 Visitor를 호출하고 오류를 반환하도록 변경하고, 오류의 세부 정보를 yield해야 합니다.
이제 flake8을 실행하면 로컬 임포트 사용을 금지하는 규칙이 적용되었음을 확인할 수 있습니다. 각 오류는 정확한 라인 번호와 컬럼 오프셋을 포함합니다.
✔️ 하나 이상의 체크 규칙 추가하기
하나 이상의 체크 규칙을 추가하려면 방문할 노드 메소드를 수정하여 특정 규칙에만 적용되도록 해야 합니다. 각 규칙에 대한 새로운 클래스를 만들고 메시지와 체크 함수를 포함해야 합니다. 필요한 경우 옵션을 추가할 수도 있습니다.
옵션을 추가하려면 @staticmethod add_options를 작성하고 option_manager를 인자로 받아야 합니다. 이를 통해 argparse를 사용하여 옵션을 지정할 수 있습니다.
값을 확인하려면 parse_options라는 다른 @staticmethod를 작성해야 합니다. 이 함수에는 options 매개변수가 필요하며, argparse.Namespace를 통해 전달됩니다. 여기에서 지정된 옵션 값을 사용하여 원하는 작업을 수행할 수 있습니다.
테스트와 패키징에 관한 더 자세한 내용은 비디오를 참고하십시오.
📝 과제: 사용자 정의 규칙 구현하기
다음 과제를 통해 규칙을 구현해보세요.
eval
함수의 사용을 경고하는 플러그인을 작성하세요.
- 새로운 메타 클래스 생성을 금지하는 규칙을 적용하세요.
- 동일한 이름의 변수를 여러 번 사용하지 못하도록 제한하는 플러그인을 작성하세요.
위 문제들은 어렵지 않으며, 주어진 예시와 비슷한 방식으로 구현할 수 있습니다.
🌟 하이라이트
- Python 코드 내에서 코드 탐색하기
- flake8을 사용하여 코드 품질 관리하기
- 함수 내 로컬 임포트 금지하기
- 추상 구문 트리와 ast 모듈을 사용하여 소스 코드 분석하기
- NodeVisitor 클래스와 코드 트리 탐색
- flake8 플러그인 구현하기
- 플러그인 환경 설정 및 옵션 추가하기
- 과제를 통해 사용자 정의 규칙 구현하기
- 플러그인 테스트 및 패키징하기