programing

.py 파일을 구문 분석하고 AST를 읽고 수정한 다음 수정된 소스 코드를 다시 씁니다.

sourcetip 2022. 9. 12. 12:05
반응형

.py 파일을 구문 분석하고 AST를 읽고 수정한 다음 수정된 소스 코드를 다시 씁니다.

Python 소스 코드를 프로그래밍 방식으로 편집하고 싶습니다.는 기본적으로 ..py파일을 만들고 AST를 생성한 다음 수정된 python 소스 코드(즉, 다른 코드)를 다시 씁니다..py을 클릭합니다.

표준 python 모듈을 사용하여 python 소스 코드를 해석/컴파일하는 방법이 있습니다.그러나 그 중 어느 것도 소스 코드를 수정(예를 들어 이 함수 선언을 삭제)하고 수정한 python 소스 코드를 다시 쓰는 방법을 지원하지 않는다고 생각합니다.

업데이트: 이 작업을 수행하는 이유는 주로 문장/표현 삭제, 테스트 재실행, 고장 확인 등을 통해 파이썬용 변환 테스트 라이브러리를 작성하고 싶기 때문입니다.

Pythoscope는 python 2.6용 2to3 툴과 마찬가지로 자동으로 생성되는 테스트 케이스에 대해 이 작업을 수행합니다(python 2.x 소스를 python 3.x 소스로 변환).

이러한 툴은 모두 python parser/compiler 머신을 구현한 lib2to3 라이브러리를 사용합니다.이러한 라이브러리는 소스 -> AST -> 소스로부터의 라운드 트립 시 소스 내의 코멘트를 유지할 수 있습니다.

변환과 같은 리팩터링을 더 많이 수행하려는 경우 로프 프로젝트가 사용자의 요구를 충족할 수 있습니다.

ast 모듈은 다른 옵션이며 구문 트리를 다시 코드로 "비교 해제"하는 방법에 대한 오래된 예가 있습니다(파서 모듈 사용).근데...ast모듈은 코드 객체로 변환된 코드에서 AST 변환을 수행할 때 더 유용합니다.

Red Baron 프로젝트도 적합할 수 있습니다(ht Xavier Combelle).

내장 모듈에는 소스로 되돌릴 방법이 없는 것 같습니다.단, 이 코드젠 모듈은 Ast를 위한 예쁜 프린터를 제공합니다.

import ast
import codegen

expr="""
def foo():
   print("hello world")
"""
p=ast.parse(expr)

p.body[0].body = [ ast.parse("return 42").body[0] ] # Replace function body with "return 42"

print(codegen.to_source(p))

인쇄:

def foo():
    return 42

정확한 형식과 코멘트는 보존되지 않으므로 손실될 수 있습니다.

단, 그럴 필요는 없습니다.치환된 AST를 실행하는 것만이 필요한 경우 ast에서 compile()을 호출하여 결과 코드 객체를 실행하는 것만으로 실행할 수 있습니다.

시간이 좀 걸렸지만 Python 3.9에는 다음과 같은 기능이 있습니다.https://docs.python.org/3.9/whatsnew/3.9.html#ast https://docs.python.org/3.9/library/ast.html#ast.unparse

ast.unparse(ast_obj)

아스트의 파스를 풀다.AST를 오브젝트하고 그에 상응하는 AST를 생성하는 코드를 가진 문자열을 생성합니다.AST 오브젝트가 ast.parse()로 해석되는 경우.

으로, 제가 은 '먹다', '먹다', '먹다'였습니다.astor패키지입니다만, 그 후, 다음과 같은 최신 AST un-parsing 패키지를 발견했습니다.

>>> import ast
>>> import astunparse
>>> print(astunparse.unparse(ast.parse('def foo(x): return 2 * x')))


def foo(x):
    return (2 * x)

Python 3.5에서 테스트했습니다.

소스 코드를 다시 생성할 필요가 없을 수 있습니다.물론 코드 가득한 .py 파일을 생성할 필요가 있다고 생각하는 이유를 실제로 설명하지 않았기 때문에 조금 위험합니다만,

  • 사람들이 실제로 사용하는 .py 파일을 생성하고 폼을 작성하여 프로젝트에 삽입할 유용한 .py 파일을 가져오려면 AST로 변경하고 싶지 않습니다.그러면 손실되기 때문입니다. all 포맷(관련된 일련의 행을 그룹화하여 Python을 읽기 쉽게 만드는 빈 행을 생각해 보십시오) (ast 노드에는 및 Attribute가 있습니다) 코멘트.대신 템플릿 엔진(예를 들어 Django 템플릿 언어)을 사용하여 .py 파일을 커스터마이즈하거나 Rick Copeland의 MetaPython 확장자를 사용하는 것이 좋습니다.

  • 모듈을 컴파일하는 동안 변경할 경우 텍스트로 돌아갈 필요가 없습니다.AST를 .py 파일로 되돌리는 대신 직접 컴파일하면 됩니다.

  • 그러나 거의 모든 경우, 새로운 .py 파일을 쓰지 않고 Python과 같은 언어를 사용하면 실제로 매우 쉽게 할 수 있는 역동적인 작업을 시도하고 있을 것입니다.질문을 확장하여 실제로 무엇을 달성하고 싶은지 알려주시면 새로운 .py 파일은 답변에 전혀 관여하지 않을 것입니다. 수백 개의 Python 프로젝트가 실제로 수백 개의 작업을 수행하는 것을 보았습니다. 단 하나도 .py 파일을 작성하지 않아도 됩니다.그래서 저는 당신이 첫 번째 좋은 사용 사례를 발견했다는 것에 조금 회의적입니다. :-)

업데이트: 이제 당신이 하려는 일에 대해 설명했으니, 어쨌든 AST에서 수술하고 싶습니다.파일의 행이 아니라 문장 전체를 삭제하여 변환하고 싶을 것입니다.AST보다 더 좋은 장소가 있을까요?

은 물론 구조를 하고 수정할 수 .ast이치는 ,, 경, 경, 변, 변, 습, 습, 습, 습, 습 으로 회신할 수 없습니다.ast모듈만.이 작업에 사용할 수 있는 다른 모듈이 있습니다(예: 여기).

할 수 .ast하십시오.ast모듈은 Green Tree snaks 튜토리얼 및 모듈에 대한 공식 문서에서 제공됩니다.

의요의 ast:

>>> import ast
>>> tree = ast.parse("print 'Hello Python!!'")
>>> exec(compile(tree, filename="<ast>", mode="exec"))
Hello Python!!

만 하면 python 코드(문자열로를할 수 .ast.parse(). Tree) AST(Abstract Syntax Tree)를 사용합니다.흥미롭게도 이 구조를 컴파일하여 위와 같이 실행할 수 있습니다.

하나의 매우 는 '하다' API입니다.ast.dump()AST 전체를 문자열 형식으로 덤프합니다.트리 구조를 검사하는 데 사용할 수 있으며 디버깅에 매우 유용합니다.를 들어 '예'라고 하면,

Python 2.7의 경우:

>>> import ast
>>> tree = ast.parse("print 'Hello Python!!'")
>>> ast.dump(tree)
"Module(body=[Print(dest=None, values=[Str(s='Hello Python!!')], nl=True)])"

Python 3.5의 경우:

>>> import ast
>>> tree = ast.parse("print ('Hello Python!!')")
>>> ast.dump(tree)
"Module(body=[Expr(value=Call(func=Name(id='print', ctx=Load()), args=[Str(s='Hello Python!!')], keywords=[]))])"

Python 2.7과 Python 3.5의 인쇄문 구문 차이와 각 트리의 AST 노드 유형 차이점에 주목하십시오.


를 사용하여 ast:

이번에는 이 비단뱀 를 보겠습니다.ast 구조하는 주요 AST입니다.ast.NodeTransformerAST를 수정해야 할 때마다 AST에서 서브클래스를 하여 노드 변환(들)을 작성해야 합니다.

이 예에서는 Python 2의 print 문을 Python 3 함수 호출로 변환하는 간단한 유틸리티를 작성해 보겠습니다.

Fun call converter 유틸리티 print2 to 3 에 스테이트먼트를 인쇄합니다.py:

#!/usr/bin/env python
'''
This utility converts the python (2.7) statements to Python 3 alike function calls before running the code.

USAGE:
     python print2to3.py <filename>
'''
import ast
import sys

class P2to3(ast.NodeTransformer):
    def visit_Print(self, node):
        new_node = ast.Expr(value=ast.Call(func=ast.Name(id='print', ctx=ast.Load()),
            args=node.values,
            keywords=[], starargs=None, kwargs=None))
        ast.copy_location(new_node, node)
        return new_node

def main(filename=None):
    if not filename:
        return

    with open(filename, 'r') as fp:
        data = fp.readlines()
    data = ''.join(data)
    tree = ast.parse(data)

    print "Converting python 2 print statements to Python 3 function calls"
    print "-" * 35
    P2to3().visit(tree)
    ast.fix_missing_locations(tree)
    # print ast.dump(tree)

    exec(compile(tree, filename="p23", mode="exec"))

if __name__ == '__main__':
    if len(sys.argv) <=1:
        print ("\nUSAGE:\n\t print2to3.py <filename>")
        sys.exit(1)
    else:
        main(sys.argv[1])

이 유틸리티는 다음과 같은 작은 예제 파일에서 사용할 수 있으며 정상적으로 작동합니다.

테스트 입력 파일:py2.py

class A(object):
    def __init__(self):
        pass

def good():
    print "I am good"

main = good

if __name__ == '__main__':
    print "I am in main"
    main()

위의 변환은 다음 경우에만 가능합니다.ast의 경우 .print " x is %s" % ("Hello Python").

안정적이고 한 코드를 는 정말 잘 ).ast트리 : https://github.com/paluh/code-formatter

저는 프로젝트를 작은 vim 플러그인의 베이스로 사용하고 있기 때문에(매일 사용하고 있습니다), 매우 멋지고 읽기 쉬운 python 코드를 생성하는 것이 목표입니다.

신추를 . 제가 좀 더 연장하려고 노력했습니다.codegen는 '아키텍처'를 으로 하고 .ast.NodeVisitor so formatters(visitor_메서드)는 기능일 뿐입니다.이 구조는 매우 제한적이고 최적화하기 어렵다는 것을 알았습니다(길고 중첩된 표현식의 경우 개체를 트리로 유지하고 일부 결과를 캐시하는 것이 더 쉽습니다. 그렇지 않으면 최상의 레이아웃을 검색하려면 기하급수적으로 복잡해질 수 있습니다).하지만 codegen제가 읽은 미츠히코의 작품은 모두 잘 쓰여져 있고 간결하기 때문입니다.

2019년에 이것을 본다면 이 libcst 패키지를 사용해도 좋습니다.이것은 ast와 유사한 구문을 가지고 있습니다.이것은 마법처럼 작동하며 코드 구조를 보존합니다.기본적으로 댓글, 공백, 줄바꿈 등을 보존해야 하는 프로젝트에 도움이 됩니다.

코멘트, 공백 등의 보존에 신경 쓸 필요가 없는 경우는, 아스트와 아스터의 조합이 효과적입니다.

다른 답변하나는 다음과 같습니다.codegen에 의해 대체된 것 같습니다.PyPI의 버전(이 글에서는 버전 0.5)도 약간 오래된 것 같기 때문에, 의 개발 버전을 인스톨 할 수 있습니다.astor다음과 같이 합니다.

pip install git+https://github.com/berkerpeksag/astor.git#egg=astor

ㄴ, ㄴ, ㄴ, ㄴ, ㄴ데.astor.to_sourcePython AST:

>>> import ast
>>> import astor
>>> print(astor.to_source(ast.parse('def foo(x): return 2 * x')))
def foo(x):
    return 2 * x

Python 3.5에서 테스트했습니다.

우리는 비슷한 욕구를 가졌지만, 여기 있는 다른 대답으로는 해결되지 않았다.그래서 저희는 AST토켄스라는 라이브러리를 만들었습니다.아스트 또는 아스트로이드 모듈로 생성된 AST 트리를 원본 소스 코드의 텍스트 범위로 표시합니다.

코드를 직접 수정하지는 않지만 수정해야 할 텍스트의 범위를 알려주기 때문에 추가하기가 어렵지 않습니다.

를 들어, 호출은 " " ", " "로 .WRAP(...)을 사용하다

example = """
def foo(): # Test
  '''My func'''
  log("hello world")  # Print
"""

import ast, asttokens
atok = asttokens.ASTTokens(example, parse=True)

call = next(n for n in ast.walk(atok.tree) if isinstance(n, ast.Call))
start, end = atok.get_text_range(call)
print(atok.text[:start] + ('WRAP(%s)' % atok.text[start:end])  + atok.text[end:])

작성:

def foo(): # Test
  '''My func'''
  WRAP(log("hello world"))  # Print

이게 도움이 됐으면 좋겠네요!

유감스럽게도 위의 답변 중 실제로 이 두 가지 조건을 모두 충족하는 것은 없습니다.

  • 주변 소스 코드의 구문 무결성을 유지합니다(예를 들어 코드의 나머지 부분에 대한 코멘트 유지, 기타 형식 지정).
  • 실제로는 (CST가 아닌) AST를 사용합니다.

저는 최근에 리팩터라고 불리는 순수 AST 기반의 리팩터링을 수행하기 위한 작은 툴킷을 썼습니다.예를 들어, 모두 치환하는 경우placeholder을 가지고 있다42다음과 같은 규칙을 간단하게 작성할 수 있습니다.

class Replace(Rule):
    
    def match(self, node):
        assert isinstance(node, ast.Name)
        assert node.id == 'placeholder'
        
        replacement = ast.Constant(42)
        return ReplacementAction(node, replacement)

그리고 모든 허용 가능한 노드를 찾아 새 노드로 교체하고 최종 폼을 생성합니다.

--- test_file.py
+++ test_file.py

@@ -1,11 +1,11 @@

 def main():
-    print(placeholder * 3 + 2)
-    print(2 +               placeholder      + 3)
+    print(42 * 3 + 2)
+    print(2 +               42      + 3)
     # some commments
-    placeholder # maybe other comments
+    42 # maybe other comments
     if something:
         other_thing
-    print(placeholder)
+    print(42)
 
 if __name__ == "__main__":
     main()

프로그램 변환 시스템은 소스 텍스트를 구문 분석하고 AST를 구축하며 소스 간 변환을 사용하여 수정할 수 있는 도구입니다("이 패턴이 보이면 해당 패턴으로 대체하십시오.").이러한 도구는 기존 소스 코드를 변환하는 데 이상적입니다. 즉, "이 패턴이 보이면 패턴 변형으로 대체하십시오."

물론 관심 언어를 해석하면서도 패턴 지향 변환을 수행할 수 있는 프로그램 변환 엔진이 필요합니다.DMS Software Reengineering Toolkit은 Python 및 기타 다양한 언어를 처리할 수 있는 시스템입니다.

DMS 구문 분석된 Python용 AST가 정확하게 주석을 캡처하는 예는 SO 답변을 참조하십시오.DMS는 AST를 변경하고 주석을 포함한 유효한 텍스트를 재생성할 수 있습니다.자체 포맷 규칙을 사용하여 AST를 예쁘게 인쇄하도록 요청할 수 있습니다(이러한 규칙을 변경할 수 있음). 또는 원래 라인 및 열 정보를 사용하여 원래 레이아웃을 최대한 보존하는 "충실도 인쇄"를 수행하도록 요청할 수 있습니다(새 코드가 삽입되는 레이아웃의 일부 변경은 피할 수 없습니다.

DMS를 사용하는 Python에 대해 "변환" 규칙을 구현하려면 다음을 작성할 수 있습니다.

rule mutate_addition(s:sum, p:product):sum->sum =
  " \s + \p " -> " \s - \p"
 if mutate_this_place(s);

이 규칙은 구문적으로 올바른 방법으로 "+"를 "-"로 대체합니다. AST에서 작동하므로 올바르게 보이는 문자열이나 코멘트는 건드리지 않습니다."mutate_this_place"의 추가 조건은 프로그램 내의 모든 위치를 변환하는 것이 아니라 이러한 빈도를 제어할 수 있도록 하는 것입니다.

다양한 코드 구조를 검출하여 변환된 버전으로 대체하는 이와 같은 규칙이 더 많이 필요합니다.DMS는 일련의 규칙을 적용할 수 있습니다.변이된 AST는 그 후 예쁘게 인쇄됩니다.

예전에는 baron을 사용했지만, 지금은 parso를 사용하고 있습니다.그것은 현대의 python에 대응하고 있기 때문입니다.아주 잘 작동한다.

돌연변이 검사기에도 이게 필요했어요parso로 만드는 것은 매우 간단합니다.https://github.com/boxed/mutmut에서 제 코드를 확인하세요.

언급URL : https://stackoverflow.com/questions/768634/parse-a-py-file-read-the-ast-modify-it-then-write-back-the-modified-source-c

반응형