Skip to content

Commit

Permalink
executors: add basic autodetection from filename
Browse files Browse the repository at this point in the history
Ref #967
  • Loading branch information
Xyene committed Dec 6, 2021
1 parent 2871cb3 commit 6cb8e31
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 21 deletions.
15 changes: 4 additions & 11 deletions dmoj/commands/submit.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from typing import Optional

from dmoj import judgeenv
from dmoj import executors, judgeenv
from dmoj.commands.base_command import Command
from dmoj.error import InvalidCommandException
from dmoj.executors import executors
from dmoj.judge import Submission


Expand Down Expand Up @@ -45,24 +44,18 @@ def execute(self, line: str) -> None:
memory_limit: int = args.memory_limit
source_file: Optional[str] = args.source_file

if language_id not in executors:
if language_id not in executors.executors:
source_file = language_id
language_id = None # source file / language id optional

if problem_id not in judgeenv.get_supported_problems():
raise InvalidCommandException(f"unknown problem '{problem_id}'")
elif not language_id:
if source_file:
filename, dot, ext = source_file.partition('.')
if not ext:
raise InvalidCommandException('invalid file name')
else:
# TODO: this should be a proper lookup elsewhere
ext = ext.upper()
language_id = {'PY': 'PY2', 'CPP': 'CPP11', 'JAVA': 'JAVA8'}.get(ext, ext)
language_id = executors.from_filename(source_file).Executor.name
else:
raise InvalidCommandException('no language is selected')
elif language_id not in executors:
elif language_id not in executors.executors:
raise InvalidCommandException(f"unknown language '{language_id}'")
elif time_limit <= 0:
raise InvalidCommandException('--time-limit must be >= 0')
Expand Down
24 changes: 24 additions & 0 deletions dmoj/executors/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,30 @@
executors: Dict[str, Any] = {}


def by_ext(ext: str) -> Any:
ext = ext.lower()

for name, executor in executors.items():
if name.lower() == ext:
return executor

for executor in sorted(
executors.values(), key=lambda executor: executor.Executor.name
):
if executor.Executor.ext == ext:
return executor

raise KeyError('no executor for extension "%s"' % ext)


def from_filename(filename: str) -> Any:
_, _, ext = filename.partition('.')
if not ext:
raise KeyError('invalid file name')

return by_ext(ext)


def get_available():
return get_available_modules(
_reexecutor, os.path.dirname(__file__), only_executors, exclude_executors | _unsupported_executors
Expand Down
22 changes: 15 additions & 7 deletions dmoj/utils/helper_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def compile_with_auxiliary_files(
compiler_time_limit: Optional[int] = None,
unbuffered: bool = False,
) -> 'BaseExecutor':
from dmoj.executors import executors
from dmoj import executors
from dmoj.executors.compiled_executor import CompiledExecutor

sources = {}
Expand All @@ -37,21 +37,29 @@ def compile_with_auxiliary_files(

def find_runtime(languages):
for grader in languages:
if grader in executors:
if grader in executors.executors:
return grader
return None

use_cpp = any(map(lambda name: os.path.splitext(name)[1] in ['.cpp', '.cc'], filenames))
use_c = any(map(lambda name: os.path.splitext(name)[1] in ['.c'], filenames))
if lang is None:
if not lang:
best_choices = ('CPP20', 'CPP17', 'CPP14', 'CPP11', 'CPP03') if use_cpp else ('C11', 'C')
lang = find_runtime(best_choices)

executor = executors.get(lang)
if not executor:
raise IOError('could not find an appropriate C++ executor')
# TODO: remove above code once `from_filename` is smart enough to
# prioritize newer versions of runtimes
if not lang:
for filename in filenames:
try:
lang = executors.from_filename(filename).Executor.name
except KeyError:
continue

executor = executor.Executor
if not lang:
raise IOError('could not find an appropriate executor')

executor = executors.executors[lang].Executor

kwargs = {'fs': executor.fs + [RecursiveDir(tempfile.gettempdir())]}

Expand Down
4 changes: 1 addition & 3 deletions testsuite/generator_python/init.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
generator:
language: PY2
source: gen.py
generator: gen.py2
test_cases:
- {generator_args: [1, 2], points: 1}
- {generator_args: [10, 20], points: 1}
Expand Down

0 comments on commit 6cb8e31

Please sign in to comment.