반응형
yapf
YAPF is a Python formatter based on clang-format (developed by Daniel Jasper).
아무리해도 기본은 마음에 안들어서 조금씩 변경해서 사용하고 싶었다. yapf 가 좋다길래 해봤다.
설정한 값.
해당값으로 할때 sqlalchemy로 query를 작성하면 너무 이상하게 줄바꿈이 되어서 전처리 및 주석처리를 해주고있다.
.style.yapf
[style]
based_on_style = pep8
indent_width = 2
column_limit = 200
space_between_ending_comma_and_closing_bracket = false
# enter only trailing comma
SPLIT_ARGUMENTS_WHEN_COMMA_TERMINATED = true
SPLIT_ALL_COMMA_SEPARATED_VALUES = false
SPLIT_BEFORE_FIRST_ARGUMENT = true
# SPLIT_BEFORE_NAMED_ASSIGNS = false
# enter
EACH_DICT_ENTRY_ON_SEPARATE_LINE = true
ALLOW_SPLIT_BEFORE_DICT_VALUE = true
COALESCE_BRACKETS = false
DEDENT_CLOSING_BRACKETS = true
SPLIT_BEFORE_LOGICAL_OPERATOR = true
sqlalchemy 용
# format_sqlalchamy.py
import re
from pathlib import Path
# 후행 연산자 감지
OPERATOR_PATTERN = re.compile(r"\)\s*(or|and|\+|\-|\*|/|if|is|not|in)\b.*")
# 대입문 감지
ASSIGN_PATTERN = re.compile(r"^\s*\w[\w\d_]*\s*=")
def should_refactor(block: str) -> bool:
return (block.count(".filter") >= 2 or ".join" in block) and len(block.strip()) > 100
def extract_query_block(lines, start_index):
block = []
follow = ""
parens = 0
i = start_index
while i < len(lines):
line = lines[i]
if i > start_index and ASSIGN_PATTERN.match(line): # 다음 대입문이 나오면 stop
break
block.append(line)
parens += line.count("(") - line.count(")")
i += 1
if parens <= 0 and line.strip().endswith(")"):
break
if i < len(lines) and lines[i].strip().startswith("."):
follow = lines[i].strip()
i += 1
return block, follow, i
def split_final_operator(block_lines):
last_line = block_lines[-1]
match = OPERATOR_PATTERN.search(last_line)
if match:
split_pos = match.start()
return block_lines[:-1] + [last_line[:split_pos].rstrip()], last_line[split_pos:].strip()
return block_lines, ""
def reformat_block(assign_indent: str, var_name: str, query_block: str) -> list[str]:
lines = query_block.strip().split("\n")
if len(query_block.strip()) <= 100:
return [f"{assign_indent}{var_name} = {query_block.strip()}"]
first_line = lines[0].strip()
base_line = f"{assign_indent}{var_name} = ({first_line}"
opening_paren_col = base_line.find("(")
dot_index = base_line.find(".") if "." in base_line else opening_paren_col + 2
chain_indent = " " * dot_index
closing_indent = " " * opening_paren_col
body_lines, trailing_op = split_final_operator(lines[1:])
rest_lines = [f"{chain_indent}{line.strip()}" for line in body_lines]
closing = f"{closing_indent})"
if trailing_op:
closing += f" {trailing_op}"
return [base_line, *rest_lines, closing]
def process_code(code: str) -> str:
lines = code.split("\n")
output = []
i = 0
while i < len(lines):
line = lines[i]
if "db.query" in line:
assign_match = re.match(r"^(\s*)(\w[\w\d_]*)\s*=\s*(db\.query\(.*)", line)
if assign_match:
assign_indent = assign_match.group(1)
var_name = assign_match.group(2)
query_start = assign_match.group(3)
lines[i] = assign_indent + query_start
block, follow, next_i = extract_query_block(lines, i)
full_query = "\n".join(block)
if not should_refactor(full_query):
output.append(f"{assign_indent}{var_name} = {full_query.strip()}")
i = next_i
continue
formatted = re.sub(r"\)\.(filter|join|order_by|limit)", r")\n" + assign_indent + r".\1", full_query)
output.append(f"{assign_indent}# yapf: disable")
output.extend(reformat_block(assign_indent, var_name, formatted))
output.append(f"{assign_indent}# yapf: enable")
if follow:
output.append(f"{assign_indent}{follow}")
i = next_i
continue
output.append(line)
i += 1
return "\n".join(output)
def run_on_target_dir(target_dir: str, in_place: bool = False, only_inside="databases"):
base = Path(target_dir)
py_files = [p for p in base.rglob("*.py") if only_inside in p.parts]
print(f"\n📁 '{only_inside}/' 폴더 내 .py 파일 {len(py_files)}개 처리 중...\n")
for path in py_files:
try:
original = path.read_text(encoding="utf-8")
updated = process_code(original)
if updated != original:
if in_place:
path.write_text(updated, encoding="utf-8")
print(f"✅ 수정됨: {path}")
else:
new_path = path.with_name(path.stem + "_yapf_disabled" + path.suffix)
new_path.write_text(updated, encoding="utf-8")
print(f"💾 저장됨: {new_path}")
else:
print(f"⏭ 변경 없음: {path}")
except Exception as e:
print(f"❌ 오류: {path} → {e}")
if __name__ == "__main__":
import sys
if len(sys.argv) < 2:
print("❗ 사용법: python fix_queries.py <project_dir> [--in-place]")
else:
run_on_target_dir(target_dir=sys.argv[1], in_place="--in-place" in sys.argv)
반응형
'Python' 카테고리의 다른 글
[Python] Python으로 Slack 메시지 보내기 (채널 & DM) (0) | 2025.04.22 |
---|---|
[Python] logging.handlers - TimedRotatingFileHandler (0) | 2025.04.09 |
[Python] 데크 deque 대해 아십니까? (feat. 자료구조) (0) | 2025.02.28 |
[Python] Meta classes 메타 클레스 란? (펌 / 링크) (0) | 2024.11.22 |
[python] vscode 들여쓰기 (0) | 2024.07.09 |