Files
Balsam_167/tests/comments/run_test.py
T
Andrey f741741cf8 Сохранить русские комментарии и настройки проекта в UTF-8
Добавить пояснения к исходникам, заголовкам TI, настройкам CCS и существующим проверкам. Зафиксировать правила кодировки, обзор модулей и аудит комментариев.
2026-10-12 12:24:48 +03:00

263 lines
26 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Проверка comments на модели пк. Комментарии и исходный текст в UTF-8.
# Продолжает текущий вызов, список или выражение: """Проверка UTF-8 и русских пояснений к каждой строке исходного кода."""; порядок аргументов сохраняется.
"""Проверка UTF-8 и русских пояснений к каждой строке исходного кода."""
# Подключает модуль argparse для этой проверки или подготовки сборки.
import argparse
# Подключает модуль io для этой проверки или подготовки сборки.
import io
# Импортирует необходимые имена: from pathlib import Path.
from pathlib import Path
# Подключает модуль re для этой проверки или подготовки сборки.
import re
# Подключает модуль subprocess для этой проверки или подготовки сборки.
import subprocess
# Подключает модуль tokenize для этой проверки или подготовки сборки.
import tokenize
# Задаёт ROOT по выражению справа; значение используется последующими операциями.
ROOT = Path(__file__).resolve().parents[2]
# Задаёт RUSSIAN по выражению справа; значение используется последующими операциями.
RUSSIAN = re.compile("[А-Яа-яЁё]")
# Задаёт C_SUFFIXES по выражению справа; значение используется последующими операциями.
C_SUFFIXES = {".c", ".h", ".cmd", ".inc"}
# Задаёт TEXT_SUFFIXES по выражению справа; значение используется последующими операциями.
TEXT_SUFFIXES = C_SUFFIXES | {".asm", ".py", ".md", ".bat", ".old", ".prefs", ".json", ".pjt", ".lkf"}
# Задаёт TEXT_NAMES по выражению справа; значение используется последующими операциями.
TEXT_NAMES = {".project", ".cproject", ".ccsproject", ".gitignore", ".editorconfig"}
# Объявляет функцию c_code_lines и её входные параметры.
def c_code_lines(text):
# Задаёт block по выражению справа; значение используется последующими операциями.
block = False
# Перебирает line in text.splitlines() в указанном порядке.
for line in text.splitlines():
# Задаёт out по выражению справа; значение используется последующими операциями.
out = []
# Задаёт i по выражению справа; значение используется последующими операциями.
i = 0
# Задаёт quote по выражению справа; значение используется последующими операциями.
quote = None
# Повторяет блок при выполнении i < len(line).
while i < len(line):
# Проверяет условие if block перед выполнением следующего блока.
if block:
# Задаёт end по выражению справа; значение используется последующими операциями.
end = line.find("*/", i)
# Проверяет условие if end < 0 перед выполнением следующего блока.
if end < 0:
# Продолжает текущий вызов, список или выражение: break; порядок аргументов сохраняется.
break
# Задаёт block по выражению справа; значение используется последующими операциями.
block = False
# Задаёт i по выражению справа; значение используется последующими операциями.
i = end + 2
# Продолжает текущий вызов, список или выражение: out.append(" "); порядок аргументов сохраняется.
out.append(" ")
# Продолжает текущий вызов, список или выражение: continue; порядок аргументов сохраняется.
continue
# Задаёт c по выражению справа; значение используется последующими операциями.
c = line[i]
# Проверяет условие if quote перед выполнением следующего блока.
if quote:
# Продолжает текущий вызов, список или выражение: out.append(c); порядок аргументов сохраняется.
out.append(c)
# Проверяет условие if c == "\\" and i + 1 < len(line) перед выполнением следующего блока.
if c == "\\" and i + 1 < len(line):
# Задаёт i + по выражению справа; значение используется последующими операциями.
i += 1
# Продолжает текущий вызов, список или выражение: out.append(line[i]); порядок аргументов сохраняется.
out.append(line[i])
# Проверяет условие elif c == quote перед выполнением следующего блока.
elif c == quote:
# Задаёт quote по выражению справа; значение используется последующими операциями.
quote = None
# Задаёт i + по выражению справа; значение используется последующими операциями.
i += 1
# Продолжает текущий вызов, список или выражение: continue; порядок аргументов сохраняется.
continue
# Проверяет условие if line.startswith("//", i) перед выполнением следующего блока.
if line.startswith("//", i):
# Продолжает текущий вызов, список или выражение: break; порядок аргументов сохраняется.
break
# Проверяет условие if line.startswith("/*", i) перед выполнением следующего блока.
if line.startswith("/*", i):
# Задаёт block по выражению справа; значение используется последующими операциями.
block = True
# Задаёт i + по выражению справа; значение используется последующими операциями.
i += 2
# Продолжает текущий вызов, список или выражение: out.append(" "); порядок аргументов сохраняется.
out.append(" ")
# Продолжает текущий вызов, список или выражение: continue; порядок аргументов сохраняется.
continue
# Проверяет условие if c in "\"'" перед выполнением следующего блока.
if c in "\"'":
# Задаёт quote по выражению справа; значение используется последующими операциями.
quote = c
# Продолжает текущий вызов, список или выражение: out.append(c); порядок аргументов сохраняется.
out.append(c)
# Задаёт i + по выражению справа; значение используется последующими операциями.
i += 1
# Продолжает текущий вызов, список или выражение: yield "".join(out).strip(); порядок аргументов сохраняется.
yield "".join(out).strip()
# Объявляет функцию python_code_lines и её входные параметры.
def python_code_lines(text):
# Задаёт active по выражению справа; значение используется последующими операциями.
active = set()
# Задаёт strings по выражению справа; значение используется последующими операциями.
strings = set()
# Перебирает token in tokenize.generate_tokens(io.StringIO(text).readline) в указанном порядке.
for token in tokenize.generate_tokens(io.StringIO(text).readline):
# Проверяет условие if token.type == tokenize.STRING and token.end[0] > token.start[0] перед выполнением следующего блока.
if token.type == tokenize.STRING and token.end[0] > token.start[0]:
# Продолжает текущий вызов, список или выражение: strings.update(range(token.start[0] + 1, token.end[0] + 1)); порядок аргументов сохраняется.
strings.update(range(token.start[0] + 1, token.end[0] + 1))
# Проверяет условие if token.type not in {tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE, tokenize.INDENT, tokenize.DEDENT, tokenize.ENDMARKER} перед выполнением следующего блока.
if token.type not in {tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE, tokenize.INDENT, tokenize.DEDENT, tokenize.ENDMARKER}:
# Продолжает текущий вызов, список или выражение: active.add(token.start[0]); порядок аргументов сохраняется.
active.add(token.start[0])
# Задаёт lines по выражению справа; значение используется последующими операциями.
lines = text.splitlines()
# Возвращает [line.strip() if n in active and n not in strings else "" for n, line in enumerate(lines, 1)] вызывающему коду и завершает функцию.
return [line.strip() if n in active and n not in strings else "" for n, line in enumerate(lines, 1)]
# Объявляет функцию audit и её входные параметры.
def audit(path, text):
# Задаёт lines по выражению справа; значение используется последующими операциями.
lines = text.splitlines()
# Проверяет условие if path.suffix in C_SUFFIXES перед выполнением следующего блока.
if path.suffix in C_SUFFIXES:
# Задаёт code по выражению справа; значение используется последующими операциями.
code = list(c_code_lines(text))
# Проверяет условие elif path.suffix == ".asm" перед выполнением следующего блока.
elif path.suffix == ".asm":
# Задаёт code по выражению справа; значение используется последующими операциями.
code = [line.split(";")[0].strip() for line in lines]
# Проверяет условие elif path.suffix == ".py" перед выполнением следующего блока.
elif path.suffix == ".py":
# Задаёт code по выражению справа; значение используется последующими операциями.
code = python_code_lines(text)
# Обрабатывает альтернативу предыдущего условия.
else:
# Возвращает 0, [] вызывающему коду и завершает функцию.
return 0, []
# Задаёт missing по выражению справа; значение используется последующими операциями.
missing = []
# Задаёт count по выражению справа; значение используется последующими операциями.
count = 0
# Перебирает i, value in enumerate(code) в указанном порядке.
for i, value in enumerate(code):
# Проверяет условие if not value перед выполнением следующего блока.
if not value:
# Продолжает текущий вызов, список или выражение: continue; порядок аргументов сохраняется.
continue
# Задаёт count + по выражению справа; значение используется последующими операциями.
count += 1
# Проверяет условие if RUSSIAN.search(lines[i]) or (i and RUSSIAN.search(lines[i-1])) перед выполнением следующего блока.
if RUSSIAN.search(lines[i]) or (i and RUSSIAN.search(lines[i-1])):
# Продолжает текущий вызов, список или выражение: continue; порядок аргументов сохраняется.
continue
# Явное продолжение Python-выражения относится к пояснённой начальной строке.
# Проверяет условие if path.suffix == ".py" and i and lines[i-1].rstrip().endswith("\\") перед выполнением следующего блока.
if path.suffix == ".py" and i and lines[i-1].rstrip().endswith("\\"):
# Продолжает текущий вызов, список или выражение: continue; порядок аргументов сохраняется.
continue
# Продолжает текущий вызов, список или выражение: missing.append(i+1); порядок аргументов сохраняется.
missing.append(i+1)
# Возвращает count, missing вызывающему коду и завершает функцию.
return count, missing
# Объявляет функцию main и её входные параметры.
def main():
# Задаёт parser по выражению справа; значение используется последующими операциями.
parser = argparse.ArgumentParser(description=__doc__)
# Задаёт parser.add_argument("--report", type по выражению справа; значение используется последующими операциями.
parser.add_argument("--report", type=Path, help="Сохранить таблицу покрытия в Markdown")
# Задаёт args по выражению справа; значение используется последующими операциями.
args = parser.parse_args()
# Задаёт names по выражению справа; значение используется последующими операциями.
names = subprocess.check_output(["git", "ls-files", "-z"], cwd=ROOT).decode("utf-8").split("\0")
# Задаёт names по выражению справа; значение используется последующими операциями.
names = sorted(set(filter(None, names)) | {"tests/comments/run_test.py", ".editorconfig"})
# Задаёт rows по выражению справа; значение используется последующими операциями.
rows = []
# Задаёт errors по выражению справа; значение используется последующими операциями.
errors = []
# Задаёт checked по выражению справа; значение используется последующими операциями.
checked = 0
# Перебирает name in names в указанном порядке.
for name in names:
# Задаёт path по выражению справа; значение используется последующими операциями.
path = ROOT / name
# Проверяет условие if path.suffix.lower() not in TEXT_SUFFIXES and path.name not in TEXT_NAMES перед выполнением следующего блока.
if path.suffix.lower() not in TEXT_SUFFIXES and path.name not in TEXT_NAMES:
# Продолжает текущий вызов, список или выражение: continue; порядок аргументов сохраняется.
continue
# Начинает блок обработки возможной ошибки.
try:
# Читает исходный файл или образ для последующей проверки и разбора.
text = path.read_text(encoding="utf-8")
# Перехватывает указанный тип ошибки и выполняет обработку.
except UnicodeError as error:
# Продолжает текущий вызов, список или выражение: errors.append(f"{name}: неверная кодировка UTF-8: {error}"); порядок аргументов сохраняется.
errors.append(f"{name}: неверная кодировка UTF-8: {error}")
# Продолжает текущий вызов, список или выражение: continue; порядок аргументов сохраняется.
continue
# Задаёт checked + по выражению справа; значение используется последующими операциями.
checked += 1
# Задаёт count, missing по выражению справа; значение используется последующими операциями.
count, missing = audit(path, text)
# Проверяет условие if missing перед выполнением следующего блока.
if missing:
# Продолжает текущий вызов, список или выражение: errors.append(f"{name}: нет русского пояснения, строки {missing[:20]}"); порядок аргументов сохраняется.
errors.append(f"{name}: нет русского пояснения, строки {missing[:20]}")
# Проверяет условие if path.suffix in C_SUFFIXES or path.suffix in {".asm", ".py"} перед выполнением следующего блока.
if path.suffix in C_SUFFIXES or path.suffix in {".asm", ".py"}:
# Продолжает текущий вызов, список или выражение: rows.append((name, count, count-len(missing))); порядок аргументов сохраняется.
rows.append((name, count, count-len(missing)))
# Проверяет условие if args.report перед выполнением следующего блока.
if args.report:
# Задаёт report по выражению справа; значение используется последующими операциями.
report = [
# Продолжает текущий вызов, список или выражение: "# Проверка комментариев и кодировки",; порядок аргументов сохраняется.
"# Проверка комментариев и кодировки",
# Продолжает текущий вызов, список или выражение: "",; порядок аргументов сохраняется.
"",
# Продолжает текущий вызов, список или выражение: "Проверка относится к редактируемым исходникам C/H, ASM, CMD, тестовым INC и Python.",; порядок аргументов сохраняется.
"Проверка относится к редактируемым исходникам C/H, ASM, CMD, тестовым INC и Python.",
# Продолжает текущий вызов, список или выражение: "Каждая непустая строка кода имеет русское пояснение перед ней или на ней. Пустые строки,",; порядок аргументов сохраняется.
"Каждая непустая строка кода имеет русское пояснение перед ней или на ней. Пустые строки,",
# Продолжает текущий вызов, список или выражение: "текст существующих комментариев и содержимое многострочных строк данных не считаются кодом.",; порядок аргументов сохраняется.
"текст существующих комментариев и содержимое многострочных строк данных не считаются кодом.",
# Продолжает текущий вызов, список или выражение: "Исходные уведомления TI сохранены. JSON-паспорта, индексы IDE, таблицы XLS и двоичные",; порядок аргументов сохраняется.
"Исходные уведомления TI сохранены. JSON-паспорта, индексы IDE, таблицы XLS и двоичные",
# Продолжает текущий вызов, список или выражение: "результаты сборки не допускают исходных комментариев; их назначение описано в документации.",; порядок аргументов сохраняется.
"результаты сборки не допускают исходных комментариев; их назначение описано в документации.",
# Продолжает текущий вызов, список или выражение: "",; порядок аргументов сохраняется.
"",
# Продолжает текущий вызов, список или выражение: f"UTF-8 проверен в {checked} текстовых файлах. Строк кода: {sum(r[1] for r in rows)}.",; порядок аргументов сохраняется.
f"UTF-8 проверен в {checked} текстовых файлах. Строк кода: {sum(r[1] for r in rows)}.",
# Продолжает текущий вызов, список или выражение: "",; порядок аргументов сохраняется.
"",
# Продолжает текущий вызов, список или выражение: "| Файл | Строк кода | С русским пояснением |",; порядок аргументов сохраняется.
"| Файл | Строк кода | С русским пояснением |",
# Продолжает текущий вызов, список или выражение: "| --- | ---: | ---: |",; порядок аргументов сохраняется.
"| --- | ---: | ---: |",
# Продолжает текущий вызов, список или выражение: ]; порядок аргументов сохраняется.
]
# Задаёт report + по выражению справа; значение используется последующими операциями.
report += [f"| {name} | {count} | {covered} |" for name,count,covered in rows]
# Сохраняет подготовленный файл в указанном формате для следующего шага.
args.report.write_text("\n".join(report)+"\n", encoding="utf-8")
# Проверяет условие if errors перед выполнением следующего блока.
if errors:
# Останавливает текущую операцию с исключением SystemExit("\n".join(errors)).
raise SystemExit("\n".join(errors))
# Продолжает текущий вызов, список или выражение: print(f"PASS UTF-8: {checked} files; Russian comments: {len(rows)} source files, {sum(r[1] for r in rows)} code lines"); порядок аргументов сохраняется.
print(f"PASS UTF-8: {checked} files; Russian comments: {len(rows)} source files, {sum(r[1] for r in rows)} code lines")
# Проверяет условие if __name__ == "__main__" перед выполнением следующего блока.
if __name__ == "__main__":
# Продолжает текущий вызов, список или выражение: main(); порядок аргументов сохраняется.
main()