Source code for dockerjudge.main

"dockerjudge main functions"

from concurrent.futures import ThreadPoolExecutor
from functools import partial
from pathlib import PurePosixPath

import docker

from . import processor as _processor
from . import test_case
from .dockerpy import exec_run, put_bin
from .status import Status


[docs]def judge(processor, source, tests, config=None, client=None): """Main function :param processor: Programming language processor :type processor: :class:`dockerjudge.processor.Processor`, `list` or `tuple` :param source: Source code :type source: str :param tests: Test cases :type tests: list :param config: Configuration +------------------------------+-----------------+---------+----------+ | Key | Description | Default |Value type| +================+=============+=================+=========+==========+ | ``callback`` | ``compile`` | Compilation | None |`function`| | | | callback | | | | +-------------+-----------------+ | | | | ``judge`` | Callback after | | | | | | judging | | | +----------------+-------------+-----------------+---------+----------+ | ``demux`` | ``compile`` | Return `stdout` |``False``| `bool` | | | | and `stderr` of | | | | | | compiler | | | | | | separately | | | +----------------+-------------+-----------------+---------+----------+ | ``iofilename`` | ``in`` | Input filename | `stdin` | `str` | | +-------------+-----------------+---------+ | | | ``out`` | Output filename | `stdout`| | +----------------+-------------+-----------------+---------+----------+ | ``limit`` | ``time`` | Time limit | ``1`` | `int` or | | | | | | `float` | +----------------+-------------+-----------------+---------+----------+ | ``network`` | Network enabled |``False``| `bool` | +------------------------------+-----------------+---------+----------+ | ``threads`` | Thread limit | None | `int` | +------------------------------+-----------------+---------+----------+ :type config: dict :param client: Docker client :type client: |DockerClient|_ .. |DockerClient| replace:: `docker.client.DockerClient` .. _DockerClient: https://docker-py.readthedocs.io/en/stable/client.html\ #docker.client.DockerClient :return: Result :rtype: `list` === ========== ======================== Key Value type Description === ========== ======================== `0` `list` Result of each test case `1` `byte` Compiler output === ========== ======================== Tese case === =================================== ===================== Key Value type Description === =================================== ===================== `0` :class:`~dockerjudge.status.Status` Status code `1` `tuple` `stdout` and `stderr` `2` `float` Time spent === =================================== ===================== """ config = config or {} client = client or docker.from_env(version="auto") try: processor = getattr(_processor, processor[0])(**processor[1]) except TypeError: try: processor = getattr(_processor, processor[0])(*processor[1]) except TypeError: pass container = client.containers.run( processor.image, detach=True, tty=True, network_disabled=not config.get("network"), ) try: return run(container, processor, source, tests, config) finally: container.remove(force=True)
def compile_source_code(container, processor, source, config): "Compile the source file" container.exec_run(f"mkdir -p {processor.workdir}/0") put_bin( container, PurePosixPath(f"{processor.workdir}/0/{processor.source}"), source, ) exec_run(container, processor.before_compile, f"{processor.workdir}/0") exec_result = container.exec_run( processor.compile, workdir=f"{processor.workdir}/0", demux=config["demux"].get("compile", False), ) if "compile" in config["callback"]: config["callback"]["compile"]( exec_result.exit_code, exec_result.output ) exec_run(container, processor.after_compile, f"{processor.workdir}/0") return exec_result def judge_test_cases(container, processor, tests, config): "Judge test cases" with ThreadPoolExecutor(max_workers=config.get("threads")) as executor: futures = [] for i, test in zip(range(len(tests)), tests): futures.append( executor.submit( test_case.__init__, container, processor, i + 1, test, config, ) ) futures[-1].add_done_callback(partial(done_callback, i, config)) return [ future.result() if not future.exception() else (Status.UE, (None, None), 0.0) for future in futures ] def done_callback(i, config, future): "Callback function for concurrent.futures.Future.add_done_callback" result = ( (Status.UE, (None, None), 0.0) if future.exception() else future.result() ) try: config["callback"].get("judge")(i, *result) except TypeError: pass def run(container, processor, source, tests, config=None): "Compile and judge" config.setdefault("callback", {}) config.setdefault("demux", {}) config.setdefault("iofilename", {}) exec_result = compile_source_code(container, processor, source, config) if exec_result.exit_code: return [ [(Status.CE, (None, None), 0.0)] * len(tests), exec_result.output, ] res = judge_test_cases(container, processor, tests, config) return [res, exec_result.output]