Удалить программные подсказки типа Python
У меня есть исходный код, написанный для Python 3.5, который я хочу сделать исполняемым в Python 3.4. Единственная функция из 3.5, которую я использую, которая недоступна в 3.4, - это подсказки типа, поэтому я хотел бы написать script, чтобы полностью удалить их.
На первый взгляд это казалось достаточно простым, и я решил написать некоторые регулярные выражения, чтобы сделать это, но потом я подумал о нескольких случаях с краями, и я не был уверен, как решить проблему для более сложной функции:/p >
def foo(bar: Dict[T, List[T]],
baz: Callable[[T], int] = lambda x: (x+3)/7,
**kwargs) -> List[T]:
В принципе, мне придется разбирать все это и перестраивать список аргументов без аннотаций типа. Как бы я даже приблизился к этому?
Ответы
Ответ 1
ОК, я понял: D
Используйте Python встроенный ast модуль для анализа исходного кода, а затем отличный astunparse, чтобы снова генерировать исходный код из проанализированного ast. Затем все, что осталось, - удалить аннотации типа:
import ast
import astunparse
source="""
import typing
from typing import Dict, T, Callable
from typing import List
def foo(bar: Dict[T, List[T]],
baz: Callable[[T], int] = lambda x: (x+3)/7,
**kwargs) -> List[T]:
pass
"""
class TypeHintRemover(ast.NodeTransformer):
def visit_FunctionDef(self, node):
# remove the return type defintion
node.returns = None
# remove all argument annotations
if node.args.args:
for arg in node.args.args:
arg.annotation = None
return node
def visit_Import(self, node):
node.names = [n for n in node.names if n.name != 'typing']
return node if node.names else None
def visit_ImportFrom(self, node):
return node if node.module != 'typing' else None
# parse the source code into an AST
parsed_source = ast.parse(source)
# remove all type annotations, function return type definitions
# and import statements from 'typing'
transformed = TypeHintRemover().visit(parsed_source)
# convert the AST back to source code
print(astunparse.unparse(transformed))
TypeHintRemover посещает все Узлы в AST и удаляет все типы подсказок в аргументах функции, определения типа возвращаемого значения для каждой функции и всех операторов импорта, которые относятся к модулю ввода.
Результат:
def foo(bar, baz=(lambda x: ((x + 3) / 7)), **kwargs):
pass
Ответ 2
Вы можете создать подкласс lib2to3.refactor.RefactoringTool
для рефакторинга кода с использованием фиксатора, который является подклассом lib2to3.fixer_base.BaseFix
с шаблоном, который ищет либо типизированный аргумент, объявление функции с возвращаемым значением с комментариями, либо простой оператор, который импортирует или импортирует из typing
и метод transform
, который удаляет индексы аннотаций из дочерних узлов или заменяет узел оператора пустым узлом:
from lib2to3 import fixer_base, refactor, fixer_util
class FixParameterAnnotations(fixer_base.BaseFix):
PATTERN = r'''
name=tname
|
func=funcdef< any+ '->' any+ >
|
simple_stmt<
(
import_name< 'import' 'typing' >
|
import_from< 'from' 'typing' 'import' any+ >
) '\n'
>
'''
def transform(self, node, results):
if 'name' in results:
del node.children[1:] # delete annotation to typed argument
elif 'func' in results:
del node.children[-4:-2] # delete annotation to function declaration
else:
return fixer_util.BlankLine() # delete statement that imports typing
return node
class Refactor(refactor.RefactoringTool):
def __init__(self, fixers):
self._fixers= [cls(None, None) for cls in fixers]
super().__init__(None)
def get_fixers(self):
return self._fixers, []
так что:
source = """
import typing
from typing import Dict, T, Callable
from typing import List
def foo(bar: Dict[T, List[T]],
baz: Callable[[T], int] = lambda x: (x+3)/7,
**kwargs) -> List[T]:
pass # comments and white spaces are preserved
"""
print(Refactor([FixParameterAnnotations]).refactor_string(source, ''))
выходы:
def foo(bar,
baz = lambda x: (x+3)/7,
**kwargs):
pass # comments and white spaces are preserved
Демонстрация: https://repl.it/@blhsing/BurlywoodFeistyTrials
В качестве бонуса, lib2to3
также сохраняет все комментарии и пробелы после преобразования. Вы можете найти определение грамматики Python в Grammar.txt
модуля lib2to3
.