added function isolation patch
This commit is contained in:
@@ -15,3 +15,6 @@ grdr = "auto_grader.cli:main"
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["src"]
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
"auto_grader" = ["data/*"]
|
||||
|
||||
@@ -7,6 +7,7 @@ import subprocess
|
||||
import shlex
|
||||
import tomllib
|
||||
import re
|
||||
import difflib
|
||||
from unidecode import unidecode
|
||||
|
||||
SUCCESS_BOX = "[\033[0;92mSUCCESS\033[0m]"
|
||||
@@ -65,6 +66,29 @@ def _weaken_file(filename, functions):
|
||||
f.write(new_code)
|
||||
|
||||
|
||||
def _add_static_attribute(match):
|
||||
prefix = match.group(1)
|
||||
func_name = match.group(2)
|
||||
suffix = match.group(3)
|
||||
if "static" in prefix:
|
||||
return match.group(0)
|
||||
return f"{prefix} static {func_name}{suffix}"
|
||||
|
||||
|
||||
def _staticize_file(filename, functions):
|
||||
pattern = re.compile(
|
||||
r"^(\s*(?:(?:\w+[\s\*]+)+))"
|
||||
r"(" + "|".join(functions) + r")"
|
||||
r"(\s*\([^)]*\)\s*\{)",
|
||||
re.MULTILINE
|
||||
)
|
||||
with open(filename, "r") as f:
|
||||
code = f.read()
|
||||
new_code = pattern.sub(_add_static_attribute, code)
|
||||
with open(filename, "w") as f:
|
||||
f.write(new_code)
|
||||
|
||||
|
||||
def _remove_static_attribute(match):
|
||||
prefix = match.group(1)
|
||||
func_name = match.group(2)
|
||||
@@ -87,17 +111,43 @@ def _unstaticize_file(filename, functions):
|
||||
f.write(new_code)
|
||||
|
||||
|
||||
def _extract_prototypes_to_header(filename, functions, header_filename):
|
||||
func_pattern = re.compile(
|
||||
r"^(\s*(?:(?:\w+[\s\*]+)+))"
|
||||
r"(" + "|".join(functions) + r")"
|
||||
r"(\s*\([^)]*\)\s*\{)",
|
||||
re.MULTILINE
|
||||
)
|
||||
with open(filename, "r") as f:
|
||||
code = f.read()
|
||||
|
||||
matches = func_pattern.findall(code)
|
||||
if not matches:
|
||||
print(f"No functions found in {filename}")
|
||||
return
|
||||
|
||||
prototypes = []
|
||||
for prefix, func_name, args in matches:
|
||||
proto = f"{prefix.strip()} static {func_name}{args.strip()[:-1]};"
|
||||
prototypes.append(proto)
|
||||
prototypes = list(dict.fromkeys(prototypes)) # preserve order
|
||||
header_exists = os.path.exists(header_filename)
|
||||
with open(header_filename, "w" if header_exists else "w") as header:
|
||||
header.write('#include "solver.h"'+"\n")
|
||||
for proto in prototypes:
|
||||
header.write(proto + "\n")
|
||||
|
||||
include_line = f'#include "{os.path.basename(header_filename)}"\n'
|
||||
if include_line not in code:
|
||||
new_code = include_line + code
|
||||
with open(filename, "w") as f:
|
||||
f.write(new_code)
|
||||
|
||||
|
||||
def is_submission_dir(dir_path):
|
||||
return os.path.isdir(dir_path) and "_assignsubmission_file" in dir_path
|
||||
|
||||
|
||||
# def _make_names(dir_path):
|
||||
# student_names = [unidecode(name[:name.find('_')].lower().replace(
|
||||
# ' ', '_')) for name in os.listdir(
|
||||
# dir_path) if is_submission_dir(os.path.join(dir_path, name))]
|
||||
# return student_names
|
||||
|
||||
|
||||
def _make_names_pairs(dir_path):
|
||||
student_pairs = [(unidecode(name[:name.find('_')].lower().replace(
|
||||
' ', '_')), os.path.join(dir_path, name)) for name in os.listdir(
|
||||
@@ -356,8 +406,6 @@ def compile(number, path, command, verbose):
|
||||
@click.argument('number', required=True, type=click.INT)
|
||||
@click.option('-s', '--solution-path', default='./solutions', type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True))
|
||||
@click.option('-p', '--path', default='./roots', type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True))
|
||||
# @click.option('-c', '--command', default='make', type=click.STRING)
|
||||
# @click.option('-v', '--verbose', is_flag=True, default=False)
|
||||
def dissect(number, solution_path, path):
|
||||
sol_as_path = os.path.join(solution_path, f"assignment_{number}")
|
||||
config_path = os.path.join(sol_as_path, "test_config.toml")
|
||||
@@ -384,6 +432,11 @@ def dissect(number, solution_path, path):
|
||||
f"{ERROR_BOX}: No function dissect config found for assignment_{number}.", err=True)
|
||||
return
|
||||
all_functions = config['dissect']['all_functions']
|
||||
if not 'build_cmd' in config["dissect"]:
|
||||
click.echo(
|
||||
f"{ERROR_BOX}: No build config found for assignment_{number}.", err=True)
|
||||
return
|
||||
static_functions = config['dissect']['static_functions']
|
||||
if not 'build_cmd' in config["dissect"]:
|
||||
click.echo(
|
||||
f"{ERROR_BOX}: No build config found for assignment_{number}.", err=True)
|
||||
@@ -428,11 +481,17 @@ def dissect(number, solution_path, path):
|
||||
click.echo(
|
||||
f"\t[{os.path.basename(root)}]:{ERROR_BOX}: Missing source {os.path.basename(src)}")
|
||||
shutil.copyfile(src, dest)
|
||||
|
||||
_extract_prototypes_to_header(
|
||||
dest, static_functions, os.path.join(sol_as_path, 'src', 'aid.h'))
|
||||
|
||||
_unstaticize_file(dest, all_functions)
|
||||
_weaken_file(dest, all_functions)
|
||||
# BUILD TEST
|
||||
_staticize_file(dest, static_functions)
|
||||
|
||||
result = 0
|
||||
os.chdir(sol_as_path)
|
||||
|
||||
for function, flag in zip(test_functions, flags):
|
||||
command = build_cmd + f" CFLAGS+={flag}"
|
||||
result = subprocess.run(
|
||||
@@ -446,13 +505,9 @@ def dissect(number, solution_path, path):
|
||||
else:
|
||||
click.echo(
|
||||
f"\t[{os.path.basename(root)}]:[BUILD]:[{function}]:{ERROR_BOX}")
|
||||
click.echo(f"stderr: {result.stderr[:69]:69s}...")
|
||||
click.echo(f"\t\tstderr: {result.stderr[:61]:61s}...")
|
||||
os.chdir(current_wd)
|
||||
|
||||
# for file in solution_files:
|
||||
# _unstaticize_file(file, test_functions)
|
||||
# _weaken_file(file, all_functions)
|
||||
|
||||
|
||||
cli.add_command(init)
|
||||
cli.add_command(archives)
|
||||
|
||||
Reference in New Issue
Block a user