Files
Balsam_167/tests/comments/run_test.py
Andrey 1e911e5250 Обновить логгер УМП и исправить запуск UART и EEPROM
Перейти на компактные 12-словные записи v4 и сохранять 10 процентов банка после отключения. Добавить трёхсекундное ожидание роста ЗПТ и настраиваемый лимит пусков. Явно инициализировать состояние UART и гарантировать публикацию параметров EEPROM перед запуском Timer2. Сохранить воспроизводимые сборки, регрессионные проверки, документацию и обновлённые результаты сборки; исключить временные артефакты.
2026-10-12 12:25:41 +03:00

267 lines
27 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])):
# Продолжает текущий вызов, список или выражение: 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.",
# Описывает фактический критерий покрытия, не выдавая наличие кириллицы за оценку качества пояснения.
"Для каждой непустой строки кода проверяется русский текст на ней или непосредственно перед ней.",
# Уточняет исключения счётчика, чтобы комментарии и многострочные данные не завышали объём кода.
"Пустые строки, текст комментариев и содержимое многострочных строк данных не считаются кодом.",
# Предупреждает о главном ограничении: шаблонный текст или русский строковый литерал тоже проходит эвристику.
"Качество и достоверность пояснений этой проверкой не устанавливаются; совпадение возможно и в строковом литерале.",
# Отделяет исходники репозитория от подключаемого и генерируемого кода, отсутствующего в Git-индексе.
"Охват: исходники из Git-индекса. Сгенерированные файлы и подключённые соседние библиотеки проверяются отдельно.",
# Продолжает текущий вызов, список или выражение: "Исходные уведомления 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()