Сохранить русские комментарии и настройки проекта в UTF-8

Добавить пояснения к исходникам, заголовкам TI, настройкам CCS и существующим проверкам. Зафиксировать правила кодировки, обзор модулей и аудит комментариев.
This commit is contained in:
2026-10-12 12:24:48 +03:00
parent 378e06a281
commit f741741cf8
137 changed files with 21576 additions and 2134 deletions
+262
View File
@@ -0,0 +1,262 @@
# Проверка 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()