Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions problemtools/checks/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,9 @@ def collect_flags(group: TestDataGroup, flags: set[str]) -> None:

collect_flags(testdata, all_flags)

fd, file_name = tempfile.mkstemp()
fd, tmp_name = tempfile.mkstemp()
os.close(fd)
file_name = Path(tmp_name)
for desc, case in _JUNK_CASES:
with open(file_name, 'wb') as f:
f.write(case)
Expand Down Expand Up @@ -189,7 +190,7 @@ def _run_input_validators(validators: InputValidators, testcase: TestCase, work_
continue

with tempfile.NamedTemporaryFile() as outfile, tempfile.NamedTemporaryFile() as errfile:
status, _ = val.run(str(testcase.infile), outfile.name, errfile.name, args=flags, work_dir=work_dir)
status, _ = val.run(testcase.infile, Path(outfile.name), Path(errfile.name), args=flags, work_dir=work_dir)
if not os.WIFEXITED(status):
emsg = f'Input format validator {val} crashed on input {testcase.infile}'
elif os.WEXITSTATUS(status) != 42:
Expand Down
8 changes: 4 additions & 4 deletions problemtools/judge/execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ def _run_normal(
errfile = execution_dir / 'submission_stderr'
sub_path = sub.compile(base_dir).path
status, runtime = sub.run(
infile=str(infile),
outfile=str(outfile),
errfile=str(errfile),
infile=infile,
outfile=outfile,
errfile=errfile,
timelim=math.ceil(timelim) + 1,
memlim=metadata.limits.memory,
work_dir=sub_path,
Expand Down Expand Up @@ -120,7 +120,7 @@ def _run_interactive(
interactive_out = execution_dir / 'interactive_output'

i_status, _ = interactive.run(
outfile=str(interactive_out),
outfile=interactive_out,
args=(
['1', str(math.ceil(2 * timelim))]
+ output_validator.get_runcmd(memlim=metadata.limits.validation_memory)
Expand Down
2 changes: 1 addition & 1 deletion problemtools/judge/grade.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def grade_group(
errfile = Path(tmpdir) / 'grader_err'
infile.write_text(grader_input)

status, _runtime = grader.run(str(infile), str(outfile), str(errfile), args=grader_flags)
status, _runtime = grader.run(infile, outfile, errfile, args=grader_flags)

grader_output = outfile.read_text(errors='replace') if outfile.exists() else ''
stderr_content = errfile.read_text(errors='replace') if errfile.exists() else ''
Expand Down
6 changes: 3 additions & 3 deletions problemtools/judge/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,12 @@ def _validate_output(
val_stdout = execution_dir / 'val_stdout'
val_stderr = execution_dir / 'val_stderr'
status, _ = output_validator.run(
infile=str(submission_output),
infile=submission_output,
args=[str(effective_infile), str(testcase.ansfile), str(feedback_dir) + os.sep] + flags,
timelim=val_timelim,
memlim=val_memlim,
outfile=str(val_stdout),
errfile=str(val_stderr),
outfile=val_stdout,
errfile=val_stderr,
)
for label, path in [('stdout', val_stdout), ('stderr', val_stderr)]:
try:
Expand Down
2 changes: 1 addition & 1 deletion problemtools/model/graders.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ def grader(self) -> SourceCode | BuildRun | None:

def load_graders(probdir: Path, language_config: Languages) -> Graders:
probdir = resolve(probdir)
graders = as_source_or_buildrun(find_programs(str(probdir / 'graders'), language_config=language_config))
graders = as_source_or_buildrun(find_programs(probdir / 'graders', language_config=language_config))
return Graders(graders=graders)
2 changes: 1 addition & 1 deletion problemtools/model/submissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,6 @@ def load_submissions(probdir: Path, language_config: Languages, includes: Includ
submissions = []
for entry in sorted(subs_root.iterdir()):
if entry.is_dir():
for program in find_programs(str(entry), language_config=language_config, includes=includes):
for program in find_programs(entry, language_config=language_config, includes=includes):
submissions.append(Submission(program=program, path=relpath(Path(entry.name) / program.name)))
return Submissions(submissions=submissions)
4 changes: 2 additions & 2 deletions problemtools/model/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def load_input_validators(probdir: Path, language_config: Languages) -> InputVal
validators = [
program
for directory in ('input_format_validators', 'input_validators')
for program in find_programs(str(probdir / directory), language_config=language_config, allow_validation_script=True)
for program in find_programs(probdir / directory, language_config=language_config, allow_validation_script=True)
]
return InputValidators(validators=validators)

Expand Down Expand Up @@ -55,6 +55,6 @@ def select(self, format_version: FormatVersion, metadata: Metadata) -> Program |
def load_output_validators(probdir: Path, format_version: FormatVersion, language_config: Languages) -> OutputValidators:
probdir = resolve(probdir)
validators = as_source_or_buildrun(
find_programs(str(probdir / format_version.output_validator_directory), language_config=language_config)
find_programs(probdir / format_version.output_validator_directory, language_config=language_config)
)
return OutputValidators(validators=validators)
21 changes: 10 additions & 11 deletions problemtools/run/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""

import os
from pathlib import Path
from typing import TYPE_CHECKING

from ..languages import Languages
Expand All @@ -22,7 +23,7 @@


def find_programs(
path: str,
path: Path,
language_config: Languages,
includes: 'Includes | None' = None,
allow_validation_script: bool = False,
Expand All @@ -47,11 +48,10 @@ def find_programs(
list of Program instances, all programs found in path.

"""
if not os.path.isdir(path):
if not path.is_dir():
return []
ret = []
for name in sorted(os.listdir(path)):
fullpath = os.path.join(path, name)
for fullpath in sorted(path.iterdir()):
run = get_program(
fullpath,
language_config=language_config,
Expand All @@ -64,7 +64,7 @@ def find_programs(


def get_program(
path: str,
path: Path,
language_config: Languages,
includes: 'Includes | None' = None,
allow_validation_script: bool = False,
Expand Down Expand Up @@ -98,17 +98,16 @@ def get_program(

includes = Includes()

if os.path.isfile(path):
if path.is_file():
if allow_validation_script:
ext = os.path.splitext(path)[1]
if ext == '.viva':
if path.suffix == '.viva':
return Viva(path)
if ext == '.ctd':
if path.suffix == '.ctd':
return Checktestdata(path)
files = [path]
else:
build = os.path.join(path, 'build')
if os.path.isfile(build) and os.access(build, os.X_OK):
build = path / 'build'
if build.is_file() and os.access(build, os.X_OK):
return BuildRun(path)
files = rutil.list_files_recursive(path)

Expand Down
27 changes: 12 additions & 15 deletions problemtools/run/buildrun.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,36 +15,33 @@
class BuildRun(Program):
"""Class for build/run-script program."""

def __init__(self, path: str) -> None:
def __init__(self, path: Path) -> None:
"""Instantiate BuildRun object.

Args:
path: directory containing the build script.
"""
if not os.path.isdir(path):
if not path.is_dir():
raise ProgramError(f'{path} is not a directory')

if path[-1] == '/':
path = path[:-1]
name = os.path.basename(path)
super().__init__(name=name)
super().__init__(name=path.name)
self._source_path = path

def do_compile(self, work_dir: Path) -> CompileResult:
"""Set up the compile work-space (copying the build script and friends into
work_dir) and run the build script."""
name = self.name
run_path = work_dir / name
if os.path.exists(run_path):
if run_path.exists():
run_path = Path(tempfile.mkdtemp(prefix=f'{name}-', dir=work_dir))
else:
os.makedirs(run_path)
run_path.mkdir(parents=True)
self._path = run_path

rutil.add_files(self._source_path, self.path)

build = os.path.join(self.path, 'build')
if not os.path.isfile(build):
build = self.path / 'build'
if not build.is_file():
raise ProgramError(f'{self._source_path} does not have a build script')
if not os.access(build, os.X_OK):
raise ProgramError(f'{self._source_path}/build is not executable')
Expand All @@ -54,12 +51,12 @@ def do_compile(self, work_dir: Path) -> CompileResult:
except subprocess.CalledProcessError as err:
return CompileResult(False, err.output.decode('utf8', 'replace'), self.path)

run = os.path.join(self.path, 'run')
if not os.path.isfile(run) or not os.access(run, os.X_OK):
run = self.path / 'run'
if not run.is_file() or not os.access(run, os.X_OK):
return CompileResult(False, 'build script did not produce an executable called "run"', self.path)
return CompileResult(True, None, self.path)

def get_runcmd(self, cwd: str | None = None, memlim: int = 1024) -> list[str]:
def get_runcmd(self, cwd: Path | None = None, memlim: int = 1024) -> list[str]:
"""Run command for the program.

Must not be called until compile() has been called.
Expand All @@ -68,8 +65,8 @@ def get_runcmd(self, cwd: str | None = None, memlim: int = 1024) -> list[str]:
cwd: if not None, the run command is provided
relative to cwd (otherwise absolute paths are given).
"""
path = self.path if cwd is None else os.path.relpath(self.path, cwd)
return [os.path.join(path, 'run')]
path = self.path if cwd is None else Path(os.path.relpath(self.path, cwd))
return [str(path / 'run')]

def should_skip_memory_rlimit(self) -> bool:
"""Ugly hack (see program.py for details)."""
Expand Down
18 changes: 9 additions & 9 deletions problemtools/run/checktestdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@
from pathlib import Path

from .executable import Executable
from .program import CompileResult
from .program import DEV_NULL, CompileResult


class Checktestdata(Executable):
"""Wrapper class for running Checktestdata scripts."""

def __init__(self, path: str) -> None:
def __init__(self, path: Path) -> None:
"""Create a Checktestdata wrapper.

Args:
path: path to .ctd source file
"""
super().__init__(sys.executable, args=['-m', 'checktestdata', path], name=os.path.basename(path))
super().__init__(Path(sys.executable), args=['-m', 'checktestdata', str(path)], name=path.name)

def do_compile(self, work_dir: Path) -> CompileResult:
"""Syntax-check the Checktestdata script"""
Expand All @@ -29,9 +29,9 @@ def do_compile(self, work_dir: Path) -> CompileResult:

def run(
self,
infile: str = '/dev/null',
outfile: str = '/dev/null',
errfile: str = '/dev/null',
infile: Path = DEV_NULL,
outfile: Path = DEV_NULL,
errfile: Path = DEV_NULL,
args: list[str] | None = None,
timelim: int = 1000,
memlim: int = 1024,
Expand All @@ -40,9 +40,9 @@ def run(
"""Run the Checktestdata script to validate an input file.

Args:
infile: name of input file to validate
outfile: file name to save stdout of Checktestdata in
errfile: file name to save stderr of Checktestdata in
infile: input file to validate
outfile: file to save stdout of Checktestdata in
errfile: file to save stderr of Checktestdata in
args: additional command-line arguments to pass to Checktestdata
timelim: time limit for the Checktestdata process in seconds

Expand Down
8 changes: 4 additions & 4 deletions problemtools/run/executable.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
class Executable(Program):
"""Class for executable files."""

def __init__(self, path: str, args: list[str] | None = None, name: str | None = None) -> None:
def __init__(self, path: Path, args: list[str] | None = None, name: str | None = None) -> None:
"""Instantiate executable object.

Args:
Expand All @@ -22,12 +22,12 @@ def __init__(self, path: str, args: list[str] | None = None, name: str | None =
should be passed to the program every time it is executed.
name: name to use for the program. Defaults to the basename of path.
"""
if not os.path.isfile(path) or not os.access(path, os.X_OK):
if not path.is_file() or not os.access(path, os.X_OK):
raise ProgramError(f'{path} is not an executable program')
super().__init__(name=name if name is not None else os.path.basename(path), path=Path(path))
super().__init__(name=name if name is not None else path.name, path=path)
self.args = args if args is not None else []

def get_runcmd(self, cwd: str | None = None, memlim: int = 1024) -> list[str]:
def get_runcmd(self, cwd: Path | None = None, memlim: int = 1024) -> list[str]:
"""Command to run the program."""
return [str(self.path)] + self.args

Expand Down
24 changes: 13 additions & 11 deletions problemtools/run/program.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

log = logging.getLogger(__name__)

DEV_NULL = Path(os.devnull)


@dataclasses.dataclass(frozen=True)
class CompileResult:
Expand Down Expand Up @@ -58,14 +60,14 @@ def __str__(self) -> str:
return self.name

@abstractmethod
def get_runcmd(self, cwd: str | None = None, memlim: int = 1024) -> list[str]:
def get_runcmd(self, cwd: Path | None = None, memlim: int = 1024) -> list[str]:
pass

def run(
self,
infile: str = '/dev/null',
outfile: str = '/dev/null',
errfile: str = '/dev/null',
infile: Path = DEV_NULL,
outfile: Path = DEV_NULL,
errfile: Path = DEV_NULL,
args: list[str] | None = None,
timelim: int = 1000,
memlim: int = 1024,
Expand All @@ -74,9 +76,9 @@ def run(
"""Run the program.

Args:
infile: name of file to pass on stdin
outfile: name of file to send stdout to
errfile: name of file to send stderr to
infile: file to pass on stdin
outfile: file to send stdout to
errfile: file to send stderr to
args: additional command-line arguments to pass to the program
timelim: CPU time limit in seconds
memlim: memory limit in MiB
Expand Down Expand Up @@ -136,9 +138,9 @@ def should_skip_memory_rlimit(self) -> bool:
def __run_wait(
self,
argv: list[str],
infile: str,
outfile: str,
errfile: str,
infile: Path,
outfile: Path,
errfile: Path,
timelim: int,
memlim: int,
work_dir: Path | None,
Expand Down Expand Up @@ -185,7 +187,7 @@ def __run_wait(
return status, rusage.ru_utime + rusage.ru_stime

@staticmethod
def __setfd(fd: int, filename: str, flag: int) -> None:
def __setfd(fd: int, filename: Path, flag: int) -> None:
tmpfd = os.open(filename, flag)
os.dup2(tmpfd, fd)
os.close(tmpfd)
Loading
Loading