forked from microsoft/agent-framework
- Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcheck_md_code_blocks.py
More file actions
Latest commit
119 lines (95 loc) · 5.05 KB
/
Copy pathcheck_md_code_blocks.py
File metadata and controls
119 lines (95 loc) · 5.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# Copyright (c) Microsoft. All rights reserved.
"""Check code blocks in Markdown files for syntax errors."""
importargparse
fromenumimportEnum
importlogging
importtempfile
importsubprocess# nosec
frompygmentsimporthighlight# type: ignore
frompygments.formattersimportTerminalFormatter
frompygments.lexersimportPythonLexer
logger=logging.getLogger(__name__)
logger.addHandler(logging.StreamHandler())
logger.setLevel(logging.INFO)
classColors(str, Enum):
CEND="\33[0m"
CRED="\33[31m"
CREDBG="\33[41m"
CGREEN="\33[32m"
CGREENBG="\33[42m"
CVIOLET="\33[35m"
CGREY="\33[90m"
defwith_color(text: str, color: Colors) ->str:
"""Prints a string with the specified color."""
returnf"{color.value}{text}{Colors.CEND.value}"
defextract_python_code_blocks(markdown_file_path: str) ->list[tuple[str, int]]:
"""Extract Python code blocks from a Markdown file."""
withopen(markdown_file_path, encoding="utf-8") asfile:
lines=file.readlines()
code_blocks: list[tuple[str, int]] = []
in_code_block=False
current_block: list[str] = []
fori, lineinenumerate(lines):
ifline.strip().startswith("```python"):
in_code_block=True
current_block= []
elifline.strip().startswith("```"):
in_code_block=False
code_blocks.append(("\n".join(current_block), i-len(current_block) +1))
elifin_code_block:
current_block.append(line)
returncode_blocks
defcheck_code_blocks(markdown_file_paths: list[str], exclude_patterns: list[str] |None=None) ->None:
"""Check Python code blocks in a Markdown file for syntax errors."""
files_with_errors: list[str] = []
exclude_patterns=exclude_patternsor []
formarkdown_file_pathinmarkdown_file_paths:
# Skip files that match any exclude pattern
ifany(patterninmarkdown_file_pathforpatterninexclude_patterns):
logger.info(f"Skipping {markdown_file_path} (matches exclude pattern)")
continue
code_blocks=extract_python_code_blocks(markdown_file_path)
had_errors=False
forcode_block, line_noincode_blocks:
markdown_file_path_with_line_no=f"{markdown_file_path}:{line_no}"
logger.info("Checking a code block in %s...", markdown_file_path_with_line_no)
# Skip blocks that don't import agent_framework modules or import lab modules
if (all(
all(import_codenotincode_blockforimport_codein [f"import {module}", f"from {module}"])
formodulein ["agent_framework"]
) or"agent_framework.lab"incode_block):
logger.info(f' {with_color("OK[ignored]", Colors.CGREENBG)}')
continue
withtempfile.NamedTemporaryFile(suffix=".py", delete=False) astemp_file:
temp_file.write(code_block.encode("utf-8"))
temp_file.flush()
# Run pyright on the temporary file using subprocess.run
result=subprocess.run(["uv", "run", "pyright", temp_file.name], capture_output=True, text=True, cwd=".") # nosec
ifresult.returncode!=0:
highlighted_code=highlight(code_block, PythonLexer(), TerminalFormatter()) # type: ignore
logger.info(
f" {with_color('FAIL', Colors.CREDBG)}\n"
f"{with_color('========================================================', Colors.CGREY)}\n"
f"{with_color('Error', Colors.CRED)}: Pyright found issues in {with_color(markdown_file_path_with_line_no, Colors.CVIOLET)}:\n"
f"{with_color('--------------------------------------------------------', Colors.CGREY)}\n"
f"{highlighted_code}\n"
f"{with_color('--------------------------------------------------------', Colors.CGREY)}\n"
"\n"
f"{with_color('pyright output:', Colors.CVIOLET)}\n"
f"{with_color(result.stdout, Colors.CRED)}"
f"{with_color('========================================================', Colors.CGREY)}\n"
)
had_errors=True
else:
logger.info(f" {with_color('OK', Colors.CGREENBG)}")
ifhad_errors:
files_with_errors.append(markdown_file_path)
iffiles_with_errors:
raiseRuntimeError("Syntax errors found in the following files:\n"+"\n".join(files_with_errors))
if__name__=="__main__":
parser=argparse.ArgumentParser(description="Check code blocks in Markdown files for syntax errors.")
# Argument is a list of markdown files containing glob patterns
parser.add_argument("markdown_files", nargs="+", help="Markdown files to check.")
parser.add_argument("--exclude", action="append", help="Exclude files containing this pattern.")
args=parser.parse_args()
check_code_blocks(args.markdown_files, args.exclude)