Files
md_to_gost/tests/test_listing_highlight.py
T
Igor20264 e6b58efeb3
Example document / build (push) Has been cancelled
Python application / build (push) Has been cancelled
v0.5.3
- Мелкие исправления увеличивающие работу конвертера
2026-09-10 10:50:15 +03:00

164 lines
6.0 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.
"""Оформление листинга и флаг подсветки синтаксиса.
Методичка МИРЭА, табл. 4.1 (`_method_extract.txt`, строки 716-737), блок
«Содержание листинга»: шрифт Courier New, начертание Обычный, размер 10 пт,
цвет текста Авто (чёрный), выравнивание по левому краю, отступы слева/справа
0 см, первая строка 0 см, интервал перед и после 0 мм, междустрочный
одинарный.
Отсюда два независимых требования:
* с выключенной подсветкой (дефолт) содержимое листинга не должно нести
никакого прямого форматирования на уровне run'а;
* если пользователь всё же включил `--syntax-highlighting`, флаг обязан
работать честно — курсив из стиля Pygments должен доезжать до XML, а checker
обязан предупредить об отклонении от табл. 4.1.
"""
import os
import unittest
from contextlib import contextmanager
import docx
from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_LINE_SPACING
from docx.oxml.ns import qn
from docx.shared import Pt
from md2gost import package_dir
from md2gost.checker import check_markdown
from md2gost.renderable.caption import CaptionInfo
from md2gost.renderable.listing import Listing
from md2gost.styles import apply_mirea_styles
CODE = '# комментарий\nprint("hello")\n'
MD_WITH_LISTING = """# *СОДЕРЖАНИЕ
[TOC]
# Раздел
%code1 Пример кода
```python
print(1)
```
См. @Листинг:code1.
"""
@contextmanager
def _syntax_highlighting(enabled: bool):
previous = os.environ.get("SYNTAX_HIGHLIGHTING")
if enabled:
os.environ["SYNTAX_HIGHLIGHTING"] = "1"
else:
os.environ.pop("SYNTAX_HIGHLIGHTING", None)
try:
yield
finally:
if previous is None:
os.environ.pop("SYNTAX_HIGHLIGHTING", None)
else:
os.environ["SYNTAX_HIGHLIGHTING"] = previous
class _ListingTestCase(unittest.TestCase):
def setUp(self) -> None:
self._document = docx.Document(os.path.join(package_dir(), "Template.docx"))
self._document._body.clear_content()
apply_mirea_styles(self._document)
def _listing(self, highlighting: bool) -> Listing:
listing = Listing(self._document._body, "python", CaptionInfo("code1", "Пример"))
with _syntax_highlighting(highlighting):
listing.set_text(CODE)
return listing
@staticmethod
def _runs(listing: Listing):
for paragraph in listing.paragraphs:
for run in paragraph._docx_paragraph.runs:
yield run
class TestListingWithoutHighlighting(_ListingTestCase):
"""Табл. 4.1: содержимое листинга — Обычный, чёрный, Courier New 10 пт."""
def test_runs_carry_no_direct_formatting(self):
listing = self._listing(highlighting=False)
runs = list(self._runs(listing))
self.assertTrue(runs)
for run in runs:
rPr = run._element.find(qn("w:rPr"))
if rPr is None:
continue
self.assertIsNone(rPr.find(qn("w:b")), run.text)
self.assertIsNone(rPr.find(qn("w:i")), run.text)
self.assertIsNone(rPr.find(qn("w:color")), run.text)
def test_code_style_matches_table_4_1(self):
style = self._document.styles["Code"]
self.assertEqual("Courier New", style.font.name)
self.assertEqual(Pt(10), style.font.size)
self.assertFalse(style.font.bold)
self.assertFalse(style.font.italic)
pf = style.paragraph_format
self.assertEqual(WD_ALIGN_PARAGRAPH.LEFT, pf.alignment)
self.assertAlmostEqual(0, pf.first_line_indent.cm, places=3)
self.assertAlmostEqual(0, pf.left_indent.cm, places=3)
self.assertAlmostEqual(0, pf.space_before.mm, places=3)
self.assertAlmostEqual(0, pf.space_after.mm, places=3)
self.assertEqual(WD_LINE_SPACING.SINGLE, pf.line_spacing_rule)
class TestListingWithHighlighting(_ListingTestCase):
"""Включённый флаг обязан реально давать курсив там, где его задаёт Pygments."""
def test_italic_token_reaches_xml(self):
listing = self._listing(highlighting=True)
italic_runs = [run for run in self._runs(listing) if run.italic]
self.assertTrue(
italic_runs,
"ни один run не получил курсив, хотя стиль Pygments задаёт italic "
"для Token.Comment",
)
self.assertTrue(
any("комментарий" in run.text for run in italic_runs),
[run.text for run in italic_runs],
)
class TestSyntaxHighlightingChecker(unittest.TestCase):
"""Подсветка противоречит табл. 4.1 — checker обязан об этом сказать."""
def test_no_issue_when_flag_is_off(self):
issues = check_markdown(MD_WITH_LISTING, "coursework")
self.assertEqual(
[], [i for i in issues if i.id == "listing.syntax_highlighting"]
)
def test_warning_when_flag_is_on(self):
issues = check_markdown(
MD_WITH_LISTING, "coursework", syntax_highlighting=True
)
found = [i for i in issues if i.id == "listing.syntax_highlighting"]
self.assertEqual(1, len(found))
self.assertEqual("warning", found[0].severity)
def test_error_under_strict(self):
issues = check_markdown(
MD_WITH_LISTING, "coursework", syntax_highlighting=True, strict=True
)
found = [i for i in issues if i.id == "listing.syntax_highlighting"]
self.assertEqual(1, len(found))
self.assertEqual("error", found[0].severity)