Ответ 1
Это всего лишь вероятные решения, так как я не очень склонен к статическому анализу.
С tokenize
:
Недавно я искал с токенизацией кода python, и я считаю, что у него есть вся информация, необходимая для выполнения таких проверок, когда достаточная логика добавлено. Для данного списка токены, сгенерированные с помощью python -m tokenize list1.py
, выглядят следующим образом:
python -m tokenize list1.py
1,0-1,4: NAME 'test'
1,5-1,6: OP '='
1,7-1,8: OP '['
1,8-1,9: NL '\n'
2,1-2,8: STRING '"item1"'
2,8-2,9: NL '\n'
3,1-3,8: STRING '"item2"'
3,8-3,9: NL '\n'
4,0-4,1: OP ']'
4,1-4,2: NEWLINE '\n'
5,0-5,0: ENDMARKER ''
Это, конечно, "проблемный" случай, когда содержимое собирается конкатенироваться. В случае, когда a ,
присутствует, результат слегка изменяется, чтобы отразить это (я добавил токены только для тела списка):
1,7-1,8: OP '['
1,8-1,9: NL '\n'
2,1-2,8: STRING '"item1"'
2,8-2,9: OP ','
2,9-2,10: NL '\n'
3,1-3,8: STRING '"item2"'
3,8-3,9: NL '\n'
4,0-4,1: OP ']'
Теперь у нас есть дополнительный знак OP ','
, обозначающий наличие второго элемента, разделенного запятой.
Учитывая эту информацию, мы могли бы использовать действительно удобный метод generate_tokens
в модуле tokenize
. Метод tokenize.generate_tokens()
, tokenize.tokenize()
в Py3
, имеет один аргумент readline
, метод для файловых объектов, который по существу возвращает следующую строку для такого файла, как объект (соответствующий ответ). Он возвращает именованный кортеж с 5 элементами в общей сложности с информацией о типе токена, символьной строкой вместе с номером строки и позицией в строке.
Используя эту информацию, теоретически можно прокрутить файл и когда OP ','
отсутствует внутри инициализации списка (начало которого определяется путем проверки того, что токены NAME
, OP '='
и OP '['
существуют на номер той же строки) можно выдать предупреждение о строках, на которых оно было обнаружено.
Хорошо, что этот подход заключается в том, что он довольно прямолинейный для обобщения. Чтобы соответствовать всем случаям, когда имеет место конкатенация строковых литералов (а именно, внутри операторов группировки (), {}, []
), вы проверяете, что токен имеет type = 51
(или 53 для Python 3) или что значение в любом из (, [, {
существует в одной строке (это грубые, верхняя часть головы предложения atm).
Теперь я не совсем уверен, как другие люди занимаются такими проблемами , но кажется, что это может быть то, что вы можете изучить. Вся необходимая информация предоставляется tokenize
, логика обнаружения - это единственное, что отсутствует.
Замечание по реализации: Эти значения (например, для type
) различаются между версиями и могут быть изменены, поэтому об этом нужно знать. Можно было бы использовать этот только работая с константами для токенов.
С parser
и ast
:
Еще одно вероятное решение, которое, вероятно, более утомительно, может включать parser
и ast
. Конкатенация строк фактически выполняется во время создания абстрактного дерева синтаксиса, чтобы вы могли альтернативно обнаружить его там.
Я действительно не хочу выгружать полный вывод методов для parser
и ast
, что я собираюсь упомянуть, но, чтобы убедиться, что мы на одной странице, я будет использоваться следующий оператор инициализации списка:
l_init = """
test = [
"item1"
"item2",
"item3"
]
"""
Чтобы получить генерируемое дерево разбора, используйте p = parser.suite(l_init)
. После этого вы можете просмотреть его с помощью p.tolist()
(слишком большой вывод, чтобы добавить его). Вы заметите, что будут три записи для трех разных объектов str
item1
, item2
, item3
.
С другой стороны, когда AST создается с помощью node = ast.parse(l_init)
и просматривается с помощью ast.dump(node)
есть только две записи: одна для конкатенированного str
item1item2
и другая для другой записи item3
.
Итак, это еще один вероятный способ сделать это, но, как я уже упоминал ранее, это еще более утомительно. Я не уверен, доступна ли линейная информация, и вы имеете дело с двумя разными модулями. Просто подумайте, если вы, возможно, захотите поиграть с внутренними объектами выше в цепочке компилятора.
Закрытие комментариев. Как заключительная записка, подход tokenize
представляется наиболее логичным в этом случае. Наоборот, похоже, что pylint
работает с astroid
python lib, которая упрощает анализ абстрактных синтаксических деревьев для кода python. Итак, в идеале нужно взглянуть на него и как он используется внутри pylint.
Примечание. Конечно, я мог бы полностью переубедить его, и более простое решение для решения "белого пространства" или новой строки, как вам было предложено, было бы достаточно.: -)