93204


Pythonスクリプトから型ヒントを自動で **除去** する方法はありますか．

  * 除去前（例）

[code]

    def hoge() -> None:
        print('Hello World')
    
    fuga: str | None = 'Guido'
    piyo: list[int] = [1, 9, 9, 1]
    
    
[/code]

  * 除去後（例）

[code]

    def hoge():
        print('Hello World')
    
    fuga = 'Guido'
    piyo = [1, 9, 9, 1]
    
    
[/code]


CC BY-SA 4.0
2022-12-30T06:08:29.690
93106
2023-01-06T21:47:39.407
51374
question
python python3
Pythonスクリプトから型ヒントを除去する


[LibCST](https://libcst.readthedocs.io/en/latest/index.html)を使ってType
Hintsの開始行桁、終了行桁を取得することができました。  
対象ファイルから`Type Hints`以外を出力するコード例です。

**コード例** ※修正版  
同一行に複数の`Type Hints`があるとき、２つ目以降の`Type Hints`が除去されないバグに対応しました。主な修正点は以下です。

  * 修正した行を対象としてさらに`Type Hints`を削除する。
  * `Type Hints`を削除すると桁位置が変わるので、Type Hintsの開始行桁、終了行桁を逆順に処理する。

[code]

    from typing import Optional
    import libcst as cst
    from libcst.metadata import WhitespaceInclusivePositionProvider
    
    
    class Printer(cst.CSTVisitor):  # Type Hintsの開始行桁、終了行桁を取得するVisitor
        METADATA_DEPENDENCIES = (cst.metadata.PositionProvider, WhitespaceInclusivePositionProvider)
        pos_annotations: list[(int, int, int, int)] = []    # Type Hintsの開始行、開始行桁、終了行、終了桁のリスト
    
        def __init__(self, pos_annotations: list[(int, int, int, int)]):
            self.pos_annotations = pos_annotations
    
        def visit_Annotation(self, node: "Annotation") -> Optional[bool]:
            pos_start = self.get_metadata(cst.metadata.WhitespaceInclusivePositionProvider, node).start
            pos_end = self.get_metadata(cst.metadata.WhitespaceInclusivePositionProvider, node).end
            self.pos_annotations.append((pos_start.line, pos_start.column, pos_end.line, pos_end.column))
    
    
    if __name__ == '__main__':
        file_path = "対象ファイル.py"
        tree = cst.parse_module(open(file_path, encoding="utf-8").read())   # 対象ファイルのCSTツリー
        wrapper = cst.metadata.MetadataWrapper(tree)
        pos_annotations: list[(int, int, int, int)] = []    # Type Hintsの開始行、開始行桁、終了行、終了桁のリスト
        result = wrapper.visit(Printer(pos_annotations))
    
        def print_no_annotation(pos_annotations: list[(int, int, int, int)], line_no: int, line: int):
            skip = False
            print_line = line + "\n"
            for item in reversed(pos_annotations):
                pos_start_line, pos_start_column, pos_end_line, pos_end_column = item
                if pos_start_line < line_no < pos_end_line:
                    skip = True
                    break
                elif pos_start_line == line_no == pos_end_line:
                    print_line = print_line[0:pos_start_column] + print_line[pos_end_column:]
                    continue
                elif pos_start_line == line_no:
                    print_line = print_line[0:pos_start_column]
                    continue
                elif pos_end_line == line_no:
                    print_line = print_line[pos_end_column:]
                    continue
            if skip:
                return
            print(print_line, end='')
    
    
        line_no = 1
        for line in tree.code.splitlines():
            print_no_annotation(pos_annotations, line_no, line)
            line_no += 1
    
[/code]

**バージョン**  
Python 3.10.5

**LibCSTのインストール**  
pip install libcst

* * *

当初はPythonの組み込みastモジュールを使用しようと考えたのですが、空白やコメントの情報が失われるようなので断念しました。※動作確認したわけではありません。  
LibCSTは空白やコメントもノードとして保持されることがわかり、`Type Hints`のノードを削除する方法を試したのですが、変数の`Type
Hints`の除去で発生する例外をどうしても解消することができませんでした。※関数の復帰値の`Type Hints`の除去はうまくいきました。

LibCSTを使うのであればこの方法が本来の方法だと思いますが残念です。

今回の回答は少し強引ですが`Type Hints`の開始行桁、終了行桁を求め、それ以外を出力する方法です。


CC BY-SA 4.0
2023-01-06T02:11:17.063
93204
2023-01-06T21:47:39.407
2023-01-06T21:47:39.407
35558
35558
93106
answer