├── .github ├── linters │ ├── .python-lint │ ├── .python-black │ └── .yaml-lint.yml ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── node.js.yml │ ├── transifex.yml │ ├── python-publish.yml │ ├── linter.yml │ ├── python-package.yml │ ├── docker-publish.yml │ └── codeql-analysis.yml ├── requirements.txt ├── setup.py ├── docs ├── requirements.txt ├── setup.py ├── status.rst ├── Makefile ├── make.bat ├── processor.rst ├── main.rst ├── installation.rst ├── conf.py └── index.rst ├── .yamllint.yaml ├── pylintrc ├── .vscode └── settings.json ├── pyproject.toml ├── .coveragerc ├── docker-compose.test.yml ├── dockerjudge ├── __init__.py ├── status.py ├── dockerpy.py ├── __main__.py ├── test_case.py ├── main.py └── processor.py ├── .eslintrc.yml ├── Dockerfile ├── .codeclimate.yml ├── tox.ini ├── index.js ├── appveyor.yml ├── docker-pull.sh ├── .readthedocs.yml ├── test_websocket.js ├── package.json ├── .tx └── config ├── Makefile ├── azure-pipelines.yml ├── setup.cfg ├── .gitlab-ci.yml ├── .gitignore ├── LICENSE ├── README.zh_Hans_CN.md ├── test_dockerjudge.py └── README.md /.github/linters/.python-lint: -------------------------------------------------------------------------------- 1 | ../../pylintrc -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | docker>=3.7 2 | websockets 3 | -------------------------------------------------------------------------------- /.github/linters/.python-black: -------------------------------------------------------------------------------- 1 | ../../pyproject.toml -------------------------------------------------------------------------------- /.github/linters/.yaml-lint.yml: -------------------------------------------------------------------------------- 1 | ../../.yamllint.yaml -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | "setup.py" 2 | 3 | import setuptools 4 | 5 | setuptools.setup() 6 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx>=2 2 | sphinx-rtd-theme>=0.5 3 | transifex-client 4 | -------------------------------------------------------------------------------- /.yamllint.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | extends: default 4 | 5 | rules: 6 | truthy: 7 | check-keys: false 8 | -------------------------------------------------------------------------------- /pylintrc: -------------------------------------------------------------------------------- 1 | [MESSAGES CONTROL] 2 | disable = C0103, C0330, C0326, E0401 3 | 4 | [format] 5 | max-line-length = 79 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.linting.flake8Enabled": true, 3 | "python.linting.pylintEnabled": true 4 | } -------------------------------------------------------------------------------- /docs/setup.py: -------------------------------------------------------------------------------- 1 | "Run `tx pull --all`" 2 | 3 | import subprocess 4 | 5 | subprocess.run(["tx", "pull", "--all"], check=True) 6 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | line-length = 79 3 | 4 | [tool.isort] 5 | profile = "black" 6 | skip = "build,.tox,venv" 7 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = */site-packages/* 3 | setup.py 4 | 5 | [report] 6 | exclude_lines = if __name__ == (?:'|")__main__(?:'|") 7 | -------------------------------------------------------------------------------- /docker-compose.test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | sut: 4 | build: . 5 | volumes: 6 | - /var/run/docker.sock:/var/run/docker.sock 7 | command: make test 8 | -------------------------------------------------------------------------------- /dockerjudge/__init__.py: -------------------------------------------------------------------------------- 1 | "dockerjudge - A Docker Based Online Judge Engine" 2 | 3 | from .main import judge # noqa 4 | 5 | __version__ = "1.4.0" 6 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | env: 2 | commonjs: true 3 | es2020: true 4 | node: true 5 | extends: 6 | - airbnb-base 7 | parserOptions: 8 | ecmaVersion: 11 9 | rules: {} 10 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 4 | ['https://afdian.net/@wangxinhe', 'https://afdian.net/@bohan'] 5 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3 2 | 3 | LABEL maintainer="汪心禾 " 4 | 5 | 6 | WORKDIR / 7 | 8 | COPY . . 9 | RUN make pip && make install && rm -rf dockerjudge 10 | -------------------------------------------------------------------------------- /docs/status.rst: -------------------------------------------------------------------------------- 1 | :mod:`dockerjudge.status` - Statuses 2 | ==================================== 3 | 4 | .. automodule:: dockerjudge.status 5 | 6 | .. autoclass:: dockerjudge.status.Status 7 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | version: "2" 4 | 5 | checks: 6 | argument-count: 7 | config: 8 | threshold: 5 9 | 10 | exclude_patterns: 11 | - "test_*.py" 12 | - "*_test.py" 13 | 14 | plugins: 15 | pep8: 16 | enabled: true 17 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py{36,37,38,39,3},pypy3,flake8,pylint,pytest 3 | skip_missing_interpreters = true 4 | 5 | [testenv] 6 | allowlist_externals = make 7 | deps = -rrequirements.txt 8 | 9 | [testenv:flake8] 10 | deps = -rrequirements.txt 11 | flake8 12 | commands = make flake8 13 | 14 | [testenv:pylint] 15 | deps = -rrequirements.txt 16 | pylint 17 | commands = make pylint 18 | 19 | [testenv:pytest] 20 | parallel_show_output = true 21 | deps = -rrequirements.txt 22 | pytest-cov 23 | commands = make pytest 24 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const EventEmitter = require('events'); 2 | 3 | const WebSocket = require('ws'); 4 | 5 | module.exports = (url, args) => { 6 | const emitter = new EventEmitter(); 7 | const ws = new WebSocket(url); 8 | emitter.ws = ws; 9 | ws.on('error', (err) => { 10 | emitter.emit('error', err); 11 | }); 12 | ws.on('open', () => { 13 | ws.send(JSON.stringify(args)); 14 | ws.on('message', (json) => { 15 | const data = JSON.parse(json); 16 | emitter.emit(...data); 17 | }); 18 | }); 19 | return emitter; 20 | }; 21 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | --- 2 | environment: 3 | matrix: 4 | - TOXENV: py36 5 | - TOXENV: py37 6 | - TOXENV: py38 7 | - TOXENV: py3 8 | 9 | build: false 10 | 11 | install: 12 | - "SET PATH=C:\\Python37-x64;C:\\Python37-x64\\Scripts;C:\\MinGW\\bin;%PATH%" 13 | - "mklink C:\\MinGW\\bin\\make.exe C:\\MinGW\\bin\\mingw32-make.exe" 14 | - pip install tox 15 | # - pip install codecov 16 | # - sh docker-pull.sh 17 | 18 | test_script: 19 | - "SET TOXENV=%TOXENV%,flake8,pylint" 20 | - make tox 21 | 22 | # after_test: 23 | # - codecov 24 | -------------------------------------------------------------------------------- /docker-pull.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -v 3 | docker pull bash # For bash 4 | docker pull clangbuiltlinux/ubuntu:llvm10-latest # For clang-10 & clang++-10 5 | docker pull clangbuiltlinux/ubuntu:llvm11-latest # For clang-11 & clang++-11 6 | docker pull gcc:4.8 # For gcc & g++ 7 | docker pull gcc:4.9 # For gccgo 8 | docker pull golang:1 # For go 9 | docker pull mono # For csc, vbnc & mono 10 | docker pull node:12 # For node 11 | docker pull openjdk # For javac and java 12 | docker pull php # For php 13 | docker pull pypy:2 # For pypy2 14 | docker pull pypy:3 # For pypy3 15 | docker pull python:2 # For python2 16 | docker pull python:3 # For python3 17 | docker pull ruby # For ruby 18 | docker pull swift # For swiftc 19 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # .readthedocs.yml 4 | # Read the Docs configuration file 5 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 6 | 7 | # Required 8 | version: 2 9 | 10 | # Build documentation in the docs/ directory with Sphinx 11 | sphinx: 12 | configuration: docs/conf.py 13 | 14 | # Build documentation with MkDocs 15 | # mkdocs: 16 | # configuration: mkdocs.yml 17 | 18 | # Optionally build your docs in additional formats such as PDF 19 | formats: all 20 | 21 | # Set the version of Python and requirements required to build your docs 22 | python: 23 | version: 3.7 24 | install: 25 | - requirements: requirements.txt 26 | - requirements: docs/requirements.txt 27 | - method: setuptools 28 | path: docs 29 | -------------------------------------------------------------------------------- /dockerjudge/status.py: -------------------------------------------------------------------------------- 1 | "The collection of judge statuses" 2 | 3 | from enum import Enum 4 | 5 | 6 | class Status(Enum): 7 | """Enumeration of judge statuses 8 | 9 | ======= ===================== 10 | Name Value 11 | ======= ===================== 12 | ``AC`` `Accepted` 13 | ``WA`` `Wrong Answer` 14 | ``ONF`` `Output Not Found` 15 | ``RE`` `Runtime Error` 16 | ``TLE`` `Time Limit Exceeded` 17 | ``UE`` `Unknown Error` 18 | ``CE`` `Compilation Error` 19 | ======= ===================== 20 | """ 21 | 22 | AC = "Accepted" 23 | WA = "Wrong Answer" 24 | ONF = "Output Not Found" 25 | RE = "Runtime Error" 26 | TLE = "Time Limit Exceeded" 27 | UE = "Unknown Error" 28 | CE = "Compilation Error" 29 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # To get started with Dependabot version updates, you'll need to specify which 4 | # package ecosystems to update and where the package manifests are located. 5 | # Please see the documentation for all configuration options: 6 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 7 | 8 | version: 2 9 | updates: 10 | - package-ecosystem: "pip" # See documentation for possible values 11 | directory: "/" # Location of package manifests 12 | schedule: 13 | interval: "daily" 14 | 15 | # Enable version updates for Docker 16 | - package-ecosystem: "docker" 17 | # Look for a `Dockerfile` in the `root` directory 18 | directory: "/" 19 | # Check for updates once a week 20 | schedule: 21 | interval: "weekly" 22 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # This workflow will do a clean install of node dependencies, 4 | # build the source code and run tests across different versions of node 5 | # For more information see: 6 | # https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 7 | 8 | name: Node.js CI 9 | 10 | on: [push, pull_request] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [10.x, 12.x, 14.x, 15.x] 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Use Node.js ${{ matrix.node-version }} 24 | uses: actions/setup-node@v1 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | - run: npm ci 28 | - run: npm run build --if-present 29 | - run: npm run lint 30 | -------------------------------------------------------------------------------- /test_websocket.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert').strict; 2 | const { spawn } = require('child_process'); 3 | 4 | const dockerjudge = require('.'); 5 | 6 | const HOST = process.env.HOST || '127.0.0.1'; 7 | const PORT = process.env.PORT || 8765; 8 | const ADDRESS = `${HOST}:${PORT}`; 9 | 10 | const server = spawn('coverage', ['run', '-a', '-m', 'dockerjudge', ADDRESS]); 11 | 12 | process.on('exit', () => { 13 | server.kill('SIGINT'); 14 | }); 15 | 16 | const judge = () => { 17 | dockerjudge(`ws://${ADDRESS}/`, { 18 | processor: ['Python', ['3']], 19 | source: "print('Hello, world!')", 20 | tests: [['', 'Hello, world!']], 21 | config: {}, 22 | }).on('error', (err) => { 23 | if (err.code === 'ECONNREFUSED') { 24 | judge(); 25 | } else { 26 | throw err; 27 | } 28 | }).on('done', (args) => { 29 | assert.equal(args[0][0][0], 'AC'); 30 | process.exit(); 31 | }); 32 | }; 33 | 34 | judge(); 35 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /.github/workflows/transifex.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | name: Transifex 4 | 5 | on: 6 | push: 7 | branches: [master] 8 | 9 | jobs: 10 | deploy: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | 15 | - name: Set up Python 16 | uses: actions/setup-python@v2 17 | with: 18 | python-version: '3.x' 19 | 20 | - name: Install dependencies 21 | run: | 22 | python -m pip install --upgrade pip 23 | pip install -U setuptools wheel 24 | if [ -f requirements.txt ] 25 | then pip install -r requirements.txt 26 | fi 27 | if [ -f docs/requirements.txt ] 28 | then pip install -r docs/requirements.txt 29 | fi 30 | 31 | - name: Build docs 32 | run: make -C docs gettext 33 | 34 | - name: Push 35 | env: 36 | TX_TOKEN: ${{ secrets.TX_TOKEN }} 37 | run: python -m txclib.cmdline push -s 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dockerjudge", 3 | "version": "0.0.1", 4 | "description": "A Docker Based Online Judge Engine", 5 | "main": "index.js", 6 | "directories": { 7 | "doc": "docs" 8 | }, 9 | "scripts": { 10 | "lint": "eslint .", 11 | "test": "node test_websocket.js" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/wxh06/dockerjudge.git" 16 | }, 17 | "keywords": [ 18 | "docker", 19 | "lxc", 20 | "oj", 21 | "online-judge", 22 | "online-judge-engine" 23 | ], 24 | "author": "汪心禾", 25 | "license": "Apache-2.0", 26 | "bugs": { 27 | "url": "https://github.com/wxh06/dockerjudge/issues" 28 | }, 29 | "homepage": "https://github.com/wxh06/dockerjudge#readme", 30 | "dependencies": { 31 | "ws": "^7.3.1" 32 | }, 33 | "devDependencies": { 34 | "eslint": "^5.16.0 || ^6.8.0 || ^7.2.0", 35 | "eslint-config-airbnb-base": "latest", 36 | "eslint-plugin-import": "^2.21.2" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.tx/config: -------------------------------------------------------------------------------- 1 | [main] 2 | host = https://www.transifex.com 3 | 4 | [dockerjudge.docs__build_gettext_main] 5 | file_filter = docs/locales//LC_MESSAGES/main.po 6 | source_file = docs/_build/gettext/main.pot 7 | source_lang = en 8 | type = PO 9 | 10 | [dockerjudge.docs__build_gettext_index] 11 | file_filter = docs/locales//LC_MESSAGES/index.po 12 | source_file = docs/_build/gettext/index.pot 13 | source_lang = en 14 | type = PO 15 | 16 | [dockerjudge.docs__build_gettext_status] 17 | file_filter = docs/locales//LC_MESSAGES/status.po 18 | source_file = docs/_build/gettext/status.pot 19 | source_lang = en 20 | type = PO 21 | 22 | [dockerjudge.docs__build_gettext_installation] 23 | file_filter = docs/locales//LC_MESSAGES/installation.po 24 | source_file = docs/_build/gettext/installation.pot 25 | source_lang = en 26 | type = PO 27 | 28 | [dockerjudge.docs__build_gettext_processor] 29 | file_filter = docs/locales//LC_MESSAGES/processor.po 30 | source_file = docs/_build/gettext/processor.pot 31 | source_lang = en 32 | type = PO 33 | 34 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: clean-build 2 | python3 setup.py build 3 | 4 | dist: clean 5 | python3 setup.py sdist bdist_wheel 6 | 7 | install: build 8 | python3 setup.py install 9 | 10 | compileall: clean-pycache 11 | python3 -m compileall dockerjudge 12 | 13 | 14 | clean: clean-build clean-dist clean-egg-info 15 | 16 | clean-pycache: 17 | rm -rf dockerjudge/__pycache__ 18 | 19 | clean-build: 20 | rm -rf build 21 | 22 | clean-dist: 23 | rm -rf dist 24 | 25 | clean-egg-info: 26 | rm -rf dockerjudge.egg-info 27 | 28 | 29 | pip: 30 | python3 -m pip install -Ur requirements.txt 31 | 32 | 33 | tox: 34 | tox -p all 35 | 36 | tox-lint: 37 | tox -e py3,flake8,pylint -p all 38 | 39 | 40 | test: 41 | python3 -W ignore test_*.py 42 | 43 | pytest: 44 | pytest --cov=dockerjudge --cov-report term-missing --cov-report xml 45 | 46 | 47 | docker-pull: 48 | chmod +x docker-pull.sh 49 | ./docker-pull.sh 50 | 51 | 52 | lint: black flake8 pylint 53 | 54 | black: 55 | black --check dockerjudge test_*.py 56 | 57 | flake8: 58 | flake8 dockerjudge test_*.py 59 | 60 | pylint: 61 | pylint dockerjudge test_*.py 62 | -------------------------------------------------------------------------------- /docs/processor.rst: -------------------------------------------------------------------------------- 1 | ========================================= 2 | :mod:`dockerjudge.processor` - Processors 3 | ========================================= 4 | 5 | Available built-in processors 6 | ============================= 7 | 8 | .. automodule:: dockerjudge.processor 9 | 10 | .. autoclass:: dockerjudge.processor.Bash 11 | .. autoclass:: dockerjudge.processor.Clang 12 | .. autoclass:: dockerjudge.processor.Clang.Language 13 | .. autoclass:: dockerjudge.processor.GCC 14 | .. autoclass:: dockerjudge.processor.GCC.Language 15 | .. autoclass:: dockerjudge.processor.Go 16 | .. autoclass:: dockerjudge.processor.Mono 17 | .. autoclass:: dockerjudge.processor.Mono.Language 18 | .. autoclass:: dockerjudge.processor.Node 19 | .. autoclass:: dockerjudge.processor.OpenJDK 20 | .. autoclass:: dockerjudge.processor.PHP 21 | .. autoclass:: dockerjudge.processor.PyPy 22 | .. autoclass:: dockerjudge.processor.Python 23 | .. autoclass:: dockerjudge.processor.Ruby 24 | .. autoclass:: dockerjudge.processor.Swift 25 | 26 | Customize 27 | ========= 28 | 29 | .. autoclass:: dockerjudge.processor.Processor 30 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # Upload a Python Package using Twine when a release is created 4 | # For more information see: 5 | # https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 6 | 7 | name: Upload Python Package 8 | 9 | on: 10 | release: 11 | types: [created] 12 | 13 | jobs: 14 | deploy: 15 | 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Set up Python 21 | uses: actions/setup-python@v2 22 | with: 23 | python-version: '3.x' 24 | - name: Install dependencies 25 | run: | 26 | python -m pip install --upgrade pip 27 | pip install -U setuptools wheel twine 28 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 29 | - name: Build and publish 30 | env: 31 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} 32 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 33 | run: | 34 | python setup.py sdist bdist_wheel 35 | twine upload dist/* 36 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # Python package 4 | # Create and test a Python package on multiple Python versions. 5 | # Add steps that analyze code, save the dist with the build record, 6 | # publish to a PyPI-compatible index, and more: 7 | # https://docs.microsoft.com/azure/devops/pipelines/languages/python 8 | 9 | trigger: 10 | - '*' 11 | 12 | pool: 13 | vmImage: 'ubuntu-latest' 14 | strategy: 15 | matrix: 16 | Python36: 17 | python.version: '3.6' 18 | Python37: 19 | python.version: '3.7' 20 | Python38: 21 | python.version: '3.8' 22 | 23 | steps: 24 | - task: UsePythonVersion@0 25 | inputs: 26 | versionSpec: '$(python.version)' 27 | displayName: 'Use Python $(python.version)' 28 | 29 | - task: NodeTool@0 30 | inputs: 31 | versionSpec: '14.x' 32 | 33 | - script: | 34 | python -m pip install --upgrade pip 35 | pip install -r requirements.txt 36 | npm ci 37 | make docker-pull 38 | displayName: 'Install dependencies' 39 | 40 | - script: | 41 | pip install pytest pytest-azurepipelines pytest-cov 42 | make pytest 43 | npm test 44 | coverage xml 45 | bash <(curl -s https://codecov.io/bash) 46 | displayName: 'pytest' 47 | -------------------------------------------------------------------------------- /.github/workflows/linter.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ########################### 3 | ########################### 4 | ## Linter GitHub Actions ## 5 | ########################### 6 | ########################### 7 | name: Lint Code Base 8 | 9 | # 10 | # Documentation: 11 | # https://help.github.com/en/articles/workflow-syntax-for-github-actions 12 | # 13 | 14 | ############################# 15 | # Start the job on all push # 16 | ############################# 17 | on: [push, pull_request] 18 | 19 | ############### 20 | # Set the Job # 21 | ############### 22 | jobs: 23 | build: 24 | # Name the Job 25 | name: Lint Code Base 26 | # Set the agent to run on 27 | runs-on: ubuntu-latest 28 | 29 | ################## 30 | # Load all steps # 31 | ################## 32 | steps: 33 | ########################## 34 | # Checkout the code base # 35 | ########################## 36 | - name: Checkout Code 37 | uses: actions/checkout@v2 38 | 39 | ################################ 40 | # Run Linter against code base # 41 | ################################ 42 | - name: Lint Code Base 43 | uses: github/super-linter@v3 44 | env: 45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 46 | VALIDATE_JAVASCRIPT_STANDARD: false 47 | VALIDATE_JSCPD: false 48 | -------------------------------------------------------------------------------- /docs/main.rst: -------------------------------------------------------------------------------- 1 | ============================== 2 | :mod:`dockerjudge.main` - Main 3 | ============================== 4 | 5 | .. automodule:: dockerjudge.main 6 | 7 | Judge 8 | ===== 9 | 10 | .. autofunction:: dockerjudge.main.judge 11 | 12 | Callback 13 | ======== 14 | 15 | Compile 16 | ------- 17 | 18 | ========= ================= ================================================= 19 | Parameter Type Description 20 | ========= ================= ================================================= 21 | `0` `int` Return value of the compiler 22 | `1` `byte` or `tuple` Output of compiler, value type depends on `demux` 23 | ========= ================= ================================================= 24 | 25 | Judge 26 | ----- 27 | 28 | ========= =================================== =============================== 29 | Parameter Type Description 30 | ========= =================================== =============================== 31 | `0` `int` Test case id, starting from `0` 32 | `1` :class:`~dockerjudge.status.Status` Status 33 | `2` `tuple` Output `(stdout, stderr)` 34 | `3` `float` Time used 35 | ========= =================================== =============================== 36 | -------------------------------------------------------------------------------- /dockerjudge/dockerpy.py: -------------------------------------------------------------------------------- 1 | "Extentions of Docker SDK for Python" 2 | 3 | import io 4 | import shlex 5 | import tarfile 6 | 7 | 8 | def tar_bin(filename, data): 9 | "Write the binary file `filename` into a tarfile" 10 | bytes_io = io.BytesIO() 11 | with tarfile.open(mode="w", fileobj=bytes_io) as tar: 12 | tarinfo = tarfile.TarInfo(filename) 13 | tarinfo.size = len(data) 14 | tar.addfile(tarinfo, io.BytesIO(data)) 15 | bytes_io.seek(0) 16 | return bytes_io.read() 17 | 18 | 19 | def put_bin(container, path, data): 20 | "Extends docker.models.containers.Container.put_archive" 21 | return container.put_archive(path.parent, tar_bin(path.name, data)) 22 | 23 | 24 | def get_bin(container, path): 25 | "Extends docker.models.containers.Container.get_archive" 26 | bytes_io = io.BytesIO() 27 | for chunk in container.get_archive(path)[0]: 28 | bytes_io.write(chunk) 29 | bytes_io.seek(0) 30 | with tarfile.open(mode="r", fileobj=bytes_io) as tar: 31 | return tar.extractfile(path.name).read() 32 | 33 | 34 | def exec_run(container, command, workdir): 35 | "Extends docker.models.containers.Container.exec_run" 36 | if command: 37 | return container.exec_run( 38 | f"sh -c {shlex.quote(command)}" 39 | if isinstance(command, str) 40 | else command, 41 | workdir=workdir, 42 | ) 43 | return None 44 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = dockerjudge 3 | version = attr: dockerjudge.__version__ 4 | author = 汪心禾 5 | author_email = wangxinhe06@gmail.com 6 | description = A Docker Based Online Judge Engine 7 | long_description = file: README.md 8 | long_description_content_type = text/markdown 9 | url = https://github.com/wxh06/dockerjudge 10 | download_url = https://pypi.org/simple/dockerjudge/ 11 | project_urls = 12 | Bug Tracker = https://github.com/wxh06/dockerjudge/issues 13 | Documentation = https://dockerjudge.readthedocs.io 14 | Release notes = https://github.com/wxh06/dockerjudge/releases 15 | Source Code = https://github.com/wxh06/dockerjudge/tree/master 16 | keywords = docker, online-judge, online-judge-engine 17 | license = Apache-2.0 18 | license_file = LICENSE 19 | classifiers = 20 | License :: OSI Approved :: Apache Software License 21 | Operating System :: OS Independent 22 | Programming Language :: Python :: 3 23 | Programming Language :: Python :: 3.6 24 | Programming Language :: Python :: 3.7 25 | Programming Language :: Python :: 3.8 26 | Programming Language :: Python :: 3.9 27 | Programming Language :: Python :: Implementation :: CPython 28 | Programming Language :: Python :: Implementation :: PyPy 29 | 30 | [options] 31 | packages = dockerjudge 32 | python_requires = >=3.6 33 | install_requires = docker>=3.7 34 | 35 | [options.extras_require] 36 | tls = docker[tls]>=3.7 37 | websocket = websockets 38 | -------------------------------------------------------------------------------- /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # This workflow will install Python dependencies, 4 | # run tests and lint with a variety of Python versions 5 | # For more information see: 6 | # https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 7 | 8 | name: Python package 9 | 10 | on: [push, pull_request] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | strategy: 17 | matrix: 18 | python-version: ['3.6', '3.7', '3.8', 'pypy3'] 19 | node-version: [10.x, 12.x, 14.x, 15.x] 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Set up Python ${{ matrix.python-version }} 24 | uses: actions/setup-python@v2 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | - name: Use Node.js ${{ matrix.node-version }} 28 | uses: actions/setup-node@v2 29 | with: 30 | node-version: ${{ matrix.node-version }} 31 | - name: Install dependencies 32 | run: | 33 | python -m pip install --upgrade pip 34 | pip install flake8 pytest-cov codecov 35 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 36 | npm ci 37 | npm run build --if-present 38 | if [ -f Makefile ]; then make docker-pull; fi 39 | - name: Lint with flake8 40 | run: flake8 . --count --show-source --statistics 41 | - name: Test with pytest 42 | run: | 43 | make pytest 44 | npm test 45 | codecov 46 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # This file is a template, 4 | # and might need editing before it works on your project. 5 | # Official language image. Look for the different tagged releases at: 6 | # https://hub.docker.com/r/library/python/tags/ 7 | image: docker:latest 8 | 9 | services: 10 | - docker:dind 11 | 12 | # Change pip's cache directory to be inside the project directory since we can 13 | # only cache local items. 14 | variables: 15 | PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip" 16 | 17 | # Pip's cache doesn't store the python packages 18 | # https://pip.pypa.io/en/stable/reference/pip_install/#caching 19 | # 20 | # If you want to also cache the installed packages, you have to install 21 | # them in a virtualenv and cache it as well. 22 | cache: 23 | paths: 24 | - .cache/pip 25 | - venv/ 26 | 27 | before_script: 28 | - apk add make py3-pip 29 | - python3 -V # Print out python version for debugging 30 | - pip install --ignore-installed virtualenv 31 | - virtualenv venv 32 | - source venv/bin/activate 33 | - pip install -U pip setuptools wheel 34 | 35 | test: 36 | script: 37 | - make docker-pull 38 | - pip install -U tox flake8 pylint pytest-cov # you can also use tox 39 | - make tox-lint 40 | - make pytest 41 | - coverage xml 42 | artifacts: 43 | reports: 44 | cobertura: coverage.xml 45 | 46 | run: 47 | script: 48 | - python setup.py bdist_wheel 49 | # an alternative approach is to install and run: 50 | - pip install dist/* 51 | # run the command here 52 | artifacts: 53 | paths: 54 | - dist/*.whl 55 | 56 | pages: 57 | script: 58 | - pip install -U sphinx sphinx-rtd-theme 59 | - cd docs ; make html 60 | - mv _build/html/ ../public/ 61 | artifacts: 62 | paths: 63 | - public 64 | only: 65 | - master 66 | -------------------------------------------------------------------------------- /dockerjudge/__main__.py: -------------------------------------------------------------------------------- 1 | "WebSocket server" 2 | 3 | from asyncio import get_event_loop, new_event_loop, set_event_loop 4 | from concurrent.futures import ThreadPoolExecutor 5 | from functools import partial 6 | from json import JSONEncoder as _JSONEncoder 7 | from json import dumps, loads 8 | from sys import argv 9 | 10 | import websockets 11 | 12 | from .main import judge 13 | from .status import Status 14 | 15 | executor = ThreadPoolExecutor() # pylint: disable = E0012, consider-using-with 16 | 17 | 18 | class JSONEncoder(_JSONEncoder): 19 | "TypeError: Object is not JSON serializable" 20 | 21 | def default(self, o): 22 | "bytes and dockerjudge.status.Status" 23 | if isinstance(o, bytes): 24 | return o.decode() 25 | if isinstance(o, Status): 26 | return o.name 27 | return _JSONEncoder.default(self, o) 28 | 29 | 30 | async def server(websocket, path): # pylint: disable = W0613 31 | "WebSocket server" 32 | loop = get_event_loop() 33 | kwargs = loads(await websocket.recv()) 34 | await websocket.send(dumps(["judge", kwargs])) 35 | kwargs["source"] = kwargs["source"].encode() 36 | kwargs["tests"] = [(i.encode(), o.encode()) for i, o in kwargs["tests"]] 37 | kwargs["config"]["callback"] = { 38 | "compile": lambda *args: loop.create_task( 39 | websocket.send(dumps(["compile", args], cls=JSONEncoder)) 40 | ) 41 | } 42 | res = await loop.run_in_executor(executor, partial(judge, **kwargs)) 43 | await websocket.send(dumps(["done", res], cls=JSONEncoder)) 44 | 45 | 46 | def main(*args): 47 | "if __name__ == '__main__'" 48 | set_event_loop(new_event_loop()) 49 | 50 | start_server = websockets.serve(server, *args) 51 | 52 | get_event_loop().run_until_complete(start_server) 53 | get_event_loop().run_forever() 54 | 55 | 56 | if __name__ == "__main__": 57 | try: 58 | main(*argv[1].split(":")) 59 | except KeyboardInterrupt: 60 | print() 61 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Installation 3 | ============ 4 | 5 | Docker 6 | ====== 7 | 8 | To run :mod:`dockerjudge`, `Docker Engine `_ is required. 9 | 10 | Install using the convenience script (for servers) 11 | -------------------------------------------------- 12 | 13 | .. code:: shell 14 | 15 | curl -fsSL https://get.docker.com -o get-docker.sh 16 | sudo sh get-docker.sh 17 | 18 | See `Install Docker Engine | Docker Documentation `_ for more information. 19 | 20 | Package :mod:`dockerjudge` 21 | ========================== 22 | 23 | From the `Python Package Index (PyPI) `_ 24 | ----------------------------------------------------------- 25 | 26 | `dockerjudge · PyPI `_ 27 | 28 | - `PyPI `_ 29 | - `Alibaba Open Source Mirror `_ 30 | - `Tsinghua Open Source Mirror `_ 31 | 32 | Via `pip `_ 33 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 34 | 35 | .. code:: shell 36 | 37 | pip install dockerjudge 38 | 39 | Via `Easy install `_ (deprecated) 40 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 41 | 42 | .. code:: shell 43 | 44 | easy_install dockerjudge 45 | 46 | 47 | From `source `_ on `GitHub `_ 48 | --------------------------------------------------------------------------------------- 49 | 50 | - HTTPS: `https://github.com/wxh06/dockerjudge.git` 51 | - SSH: `git@github.com:wxh06/dockerjudge.git` 52 | 53 | .. code:: shell 54 | 55 | git clone https://github.com/wxh06/dockerjudge.git 56 | cd dockerjudge 57 | 58 | make pip && make # python3 -m pip install -Ur requirements.txt && python3 setup.py build 59 | sudo make install # python3 setup.py install 60 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # pylint: disable = C0103, C0114, W0622 8 | 9 | # -- Path setup -------------------------------------------------------------- 10 | 11 | # If extensions (or modules to document with autodoc) are in another directory, 12 | # add these directories to sys.path here. If the directory is relative to the 13 | # documentation root, use os.path.abspath to make it absolute, like shown here. 14 | 15 | import os 16 | import sys 17 | 18 | sys.path.insert(0, os.path.abspath("..")) 19 | 20 | 21 | # -- Project information ----------------------------------------------------- 22 | 23 | project = "dockerjudge" 24 | copyright = "2020, 汪心禾" 25 | author = "汪心禾" 26 | 27 | 28 | # -- General configuration --------------------------------------------------- 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | extensions = [ 34 | "sphinx.ext.autodoc", 35 | "sphinx.ext.viewcode", 36 | ] 37 | 38 | master_doc = "index" 39 | 40 | # Add any paths that contain templates here, relative to this directory. 41 | templates_path = ["_templates"] 42 | 43 | # List of patterns, relative to source directory, that match files and 44 | # directories to ignore when looking for source files. 45 | # This pattern also affects html_static_path and html_extra_path. 46 | exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] 47 | 48 | 49 | # -- Options for HTML output ------------------------------------------------- 50 | 51 | # The theme to use for HTML and HTML Help pages. See the documentation for 52 | # a list of builtin themes. 53 | # 54 | html_theme = "sphinx_rtd_theme" 55 | 56 | # Add any paths that contain custom static files (such as style sheets) here, 57 | # relative to this directory. They are copied after the builtin static files, 58 | # so a file named "default.css" will overwrite the builtin "default.css". 59 | html_static_path = ["_static"] 60 | 61 | 62 | # -- Options for internationalization ---------------------------------------- 63 | 64 | locale_dirs = ["locales"] 65 | -------------------------------------------------------------------------------- /.github/workflows/docker-publish.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | name: Docker 4 | 5 | on: 6 | push: 7 | # Publish `master` as Docker `latest` image. 8 | branches: 9 | - master 10 | 11 | # Publish `v1.2.3` tags as releases. 12 | tags: 13 | - v* 14 | 15 | # Run tests for any PRs. 16 | pull_request: 17 | 18 | env: 19 | # TODO: Change variable to your image's name. 20 | IMAGE_NAME: dockerjudge 21 | 22 | jobs: 23 | # Run tests. 24 | # See also https://docs.docker.com/docker-hub/builds/automated-testing/ 25 | test: 26 | runs-on: ubuntu-latest 27 | 28 | steps: 29 | - uses: actions/checkout@v2 30 | 31 | - name: Run tests 32 | run: | 33 | if [ -f Makefile ]; then make docker-pull; fi 34 | if [ -f docker-compose.test.yml ]; then 35 | docker-compose --file docker-compose.test.yml build 36 | docker-compose --file docker-compose.test.yml run sut 37 | else 38 | docker build . --file Dockerfile 39 | fi 40 | 41 | # Push image to GitHub Packages. 42 | # See also https://docs.docker.com/docker-hub/builds/ 43 | push: 44 | # Ensure test job passes before pushing image. 45 | needs: test 46 | 47 | runs-on: ubuntu-latest 48 | if: github.event_name == 'push' 49 | 50 | steps: 51 | - uses: actions/checkout@v2 52 | 53 | - name: Build image 54 | run: docker build . --file Dockerfile --tag $IMAGE_NAME 55 | 56 | - name: Log into registry 57 | run: >- 58 | echo "${{ secrets.GITHUB_TOKEN }}" | docker login 59 | docker.pkg.github.com -u ${{ github.actor }} --password-stdin 60 | 61 | - name: Push image 62 | run: | 63 | IMAGE_ID=docker.pkg.github.com/${{ github.repository }}/$IMAGE_NAME 64 | 65 | # Change all uppercase to lowercase 66 | IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]') 67 | 68 | # Strip git ref prefix from version 69 | VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,') 70 | 71 | # Strip "v" prefix from tag name 72 | [[ "${{ github.ref }}" == "refs/tags/"* ]] && 73 | VERSION=$(echo $VERSION | sed -e 's/^v//') 74 | 75 | # Use Docker `latest` tag convention 76 | [ "$VERSION" == "master" ] && VERSION=latest 77 | 78 | echo IMAGE_ID=$IMAGE_ID 79 | echo VERSION=$VERSION 80 | 81 | docker tag $IMAGE_NAME $IMAGE_ID:$VERSION 82 | docker push $IMAGE_ID:$VERSION 83 | -------------------------------------------------------------------------------- /dockerjudge/test_case.py: -------------------------------------------------------------------------------- 1 | "Test case operations" 2 | 3 | import re 4 | import shlex 5 | from pathlib import PurePosixPath 6 | 7 | from docker.errors import NotFound 8 | 9 | from .dockerpy import exec_run, get_bin, put_bin 10 | from .status import Status 11 | 12 | 13 | def __init__(container, processor, i, ioput, config): 14 | "Copy binary files to `i` and judge" 15 | container.exec_run(f"cp -r 0 {i}", workdir=str(processor.workdir)) 16 | exec_run(container, processor.before_judge, f"{processor.workdir}/{i}") 17 | res = judge(container, processor, i, ioput, config) 18 | exec_run(container, processor.after_judge, f"{processor.workdir}/{i}") 19 | return res 20 | 21 | 22 | def _get_io_file_path(ioro, processor, i, config): 23 | "Get the absolute path of input or output file" 24 | return PurePosixPath( 25 | f"{processor.workdir}/{i}/{config['iofilename'][ioro]}" 26 | if config["iofilename"].get(ioro) 27 | else f"{processor.workdir}/{i}.{ioro}" 28 | ) 29 | 30 | 31 | def judge(container, processor, i, ioput, config): 32 | "Judge one of the test cases" 33 | put_bin(container, _get_io_file_path("in", processor, i, config), ioput[0]) 34 | res = container.exec_run( 35 | "bash -c " 36 | + shlex.quote( 37 | "TIMEFORMAT=$'\\n%3lR' && time timeout -sKILL " 38 | + str(config.get("limit", {}).get("time", 1)) 39 | + " sh -c " 40 | + shlex.quote(processor.judge) 41 | + " > " 42 | + f"{processor.workdir}/{i}.out" 43 | + ( 44 | " < " + f"{processor.workdir}/{i}.in" 45 | if not config["iofilename"].get("in") 46 | else "" 47 | ) 48 | ), 49 | workdir=f"{processor.workdir}/{i}", 50 | demux=True, 51 | ) 52 | duration = re.search( 53 | "\n([0-9]+)m([0-9]+\\.[0-9]{3})s\n$", res.output[1].decode() 54 | ) 55 | stderr = res.output[1][: duration.span()[0]] 56 | duration = int(duration.group(1)) * 60 + float(duration.group(2)) 57 | if res.exit_code == 137: 58 | return Status.TLE, (None, stderr), duration 59 | if res.exit_code: 60 | return Status.RE, (None, stderr), duration 61 | try: 62 | output = get_bin( 63 | container, _get_io_file_path("out", processor, i, config) 64 | ) 65 | except NotFound: 66 | return Status.ONF, (None, stderr), duration 67 | return ( 68 | Status.AC if output.rstrip() == ioput[1].rstrip() else Status.WA, 69 | (output, stderr), 70 | duration, 71 | ) 72 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # For most projects, this workflow file will not need changing; you simply need 4 | # to commit it to your repository. 5 | # 6 | # You may wish to alter this file to override the set of languages analyzed, 7 | # or to provide custom queries or build logic. 8 | # 9 | # ******** NOTE ******** 10 | # We have attempted to detect the languages in your repository. Please check 11 | # the `language` matrix defined below to confirm you have the correct set of 12 | # supported CodeQL languages. 13 | # 14 | name: "CodeQL" 15 | 16 | on: [push, pull_request] 17 | 18 | jobs: 19 | analyze: 20 | name: Analyze 21 | runs-on: ubuntu-latest 22 | permissions: 23 | actions: read 24 | contents: read 25 | security-events: write 26 | 27 | strategy: 28 | fail-fast: false 29 | matrix: 30 | language: ['javascript', 'python'] 31 | # CodeQL supports 32 | # ['cpp', 'csharp', 'go', 'java', 'javascript', 'python'] 33 | # Learn more: 34 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 35 | 36 | steps: 37 | - name: Checkout repository 38 | uses: actions/checkout@v2 39 | 40 | # Initializes the CodeQL tools for scanning. 41 | - name: Initialize CodeQL 42 | uses: github/codeql-action/init@v1 43 | with: 44 | languages: ${{ matrix.language }} 45 | # If you wish to specify custom queries, 46 | # you can do so here or in a config file. 47 | # By default, 48 | # queries listed here will override any specified in a config file. 49 | # Prefix the list here with "+" 50 | # to use these queries and those in the config file. 51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 52 | 53 | # Autobuild attempts to build any compiled languages 54 | # (C/C++, C#, or Java). 55 | # If this step fails, 56 | # then you should remove it and run the build manually (see below) 57 | - name: Autobuild 58 | uses: github/codeql-action/autobuild@v1 59 | 60 | # ℹ️ Command-line programs to run using the OS shell. 61 | # 📚 https://git.io/JvXDl 62 | 63 | # ✏️ If the Autobuild fails above, 64 | # remove it and uncomment the following three lines 65 | # and modify them (or add more) to build your code if your project 66 | # uses a compiled language 67 | 68 | # - run: | 69 | # make bootstrap 70 | # make release 71 | 72 | - name: Perform CodeQL Analysis 73 | uses: github/codeql-action/analyze@v1 74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python.gitignore 2 | # https://github.com/github/gitignore/blob/master/Python.gitignore 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | wheels/ 26 | share/python-wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .nox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *.cover 52 | *.py,cover 53 | .hypothesis/ 54 | .pytest_cache/ 55 | cover/ 56 | 57 | # Translations 58 | *.mo 59 | *.pot 60 | LC_MESSAGES/ 61 | 62 | # Django stuff: 63 | *.log 64 | local_settings.py 65 | db.sqlite3 66 | db.sqlite3-journal 67 | 68 | # Flask stuff: 69 | instance/ 70 | .webassets-cache 71 | 72 | # Scrapy stuff: 73 | .scrapy 74 | 75 | # Sphinx documentation 76 | docs/_build/ 77 | 78 | # PyBuilder 79 | .pybuilder/ 80 | target/ 81 | 82 | # Jupyter Notebook 83 | .ipynb_checkpoints 84 | 85 | # IPython 86 | profile_default/ 87 | ipython_config.py 88 | 89 | # pyenv 90 | # For a library or package, you might want to ignore these files since the code is 91 | # intended to run in multiple environments; otherwise, check them in: 92 | # .python-version 93 | 94 | # pipenv 95 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 96 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 97 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 98 | # install all needed dependencies. 99 | #Pipfile.lock 100 | 101 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 102 | __pypackages__/ 103 | 104 | # Celery stuff 105 | celerybeat-schedule 106 | celerybeat.pid 107 | 108 | # SageMath parsed files 109 | *.sage.py 110 | 111 | # Environments 112 | .env 113 | .venv 114 | env/ 115 | venv/ 116 | ENV/ 117 | env.bak/ 118 | venv.bak/ 119 | 120 | # Spyder project settings 121 | .spyderproject 122 | .spyproject 123 | 124 | # Rope project settings 125 | .ropeproject 126 | 127 | # mkdocs documentation 128 | /site 129 | 130 | # mypy 131 | .mypy_cache/ 132 | .dmypy.json 133 | dmypy.json 134 | 135 | # Pyre type checker 136 | .pyre/ 137 | 138 | # pytype static type analyzer 139 | .pytype/ 140 | 141 | # Cython debug symbols 142 | cython_debug/ 143 | 144 | 145 | # Node.gitignore 146 | # https://github.com/github/gitignore/blob/master/Node.gitignore 147 | 148 | # Logs 149 | logs 150 | *.log 151 | npm-debug.log* 152 | yarn-debug.log* 153 | yarn-error.log* 154 | lerna-debug.log* 155 | 156 | # Diagnostic reports (https://nodejs.org/api/report.html) 157 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 158 | 159 | # Runtime data 160 | pids 161 | *.pid 162 | *.seed 163 | *.pid.lock 164 | 165 | # Directory for instrumented libs generated by jscoverage/JSCover 166 | lib-cov 167 | 168 | # Coverage directory used by tools like istanbul 169 | coverage 170 | *.lcov 171 | 172 | # nyc test coverage 173 | .nyc_output 174 | 175 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 176 | .grunt 177 | 178 | # Bower dependency directory (https://bower.io/) 179 | bower_components 180 | 181 | # node-waf configuration 182 | .lock-wscript 183 | 184 | # Compiled binary addons (https://nodejs.org/api/addons.html) 185 | build/Release 186 | 187 | # Dependency directories 188 | node_modules/ 189 | jspm_packages/ 190 | 191 | # Snowpack dependency directory (https://snowpack.dev/) 192 | web_modules/ 193 | 194 | # TypeScript cache 195 | *.tsbuildinfo 196 | 197 | # Optional npm cache directory 198 | .npm 199 | 200 | # Optional eslint cache 201 | .eslintcache 202 | 203 | # Microbundle cache 204 | .rpt2_cache/ 205 | .rts2_cache_cjs/ 206 | .rts2_cache_es/ 207 | .rts2_cache_umd/ 208 | 209 | # Optional REPL history 210 | .node_repl_history 211 | 212 | # Output of 'npm pack' 213 | *.tgz 214 | 215 | # Yarn Integrity file 216 | .yarn-integrity 217 | 218 | # dotenv environment variables file 219 | .env 220 | .env.test 221 | 222 | # parcel-bundler cache (https://parceljs.org/) 223 | .cache 224 | .parcel-cache 225 | 226 | # Next.js build output 227 | .next 228 | out 229 | 230 | # Nuxt.js build / generate output 231 | .nuxt 232 | dist 233 | 234 | # Gatsby files 235 | .cache/ 236 | # Comment in the public line in if your project uses Gatsby and not Next.js 237 | # https://nextjs.org/blog/next-9-1#public-directory-support 238 | # public 239 | 240 | # vuepress build output 241 | .vuepress/dist 242 | 243 | # Serverless directories 244 | .serverless/ 245 | 246 | # FuseBox cache 247 | .fusebox/ 248 | 249 | # DynamoDB Local files 250 | .dynamodb/ 251 | 252 | # TernJS port file 253 | .tern-port 254 | 255 | # Stores VSCode versions used for testing VSCode extensions 256 | .vscode-test 257 | 258 | # yarn v2 259 | .yarn/cache 260 | .yarn/unplugged 261 | .yarn/build-state.yml 262 | .yarn/install-state.gz 263 | .pnp.* 264 | -------------------------------------------------------------------------------- /dockerjudge/main.py: -------------------------------------------------------------------------------- 1 | "dockerjudge main functions" 2 | 3 | from concurrent.futures import ThreadPoolExecutor 4 | from functools import partial 5 | from pathlib import PurePosixPath 6 | 7 | import docker 8 | 9 | from . import processor as _processor 10 | from . import test_case 11 | from .dockerpy import exec_run, put_bin 12 | from .status import Status 13 | 14 | 15 | def judge(processor, source, tests, config=None, client=None): 16 | """Main function 17 | 18 | :param processor: Programming language processor 19 | :type processor: 20 | :class:`dockerjudge.processor.Processor`, `list` or `tuple` 21 | :param source: Source code 22 | :type source: str 23 | :param tests: Test cases 24 | :type tests: list 25 | :param config: Configuration 26 | 27 | +------------------------------+-----------------+---------+----------+ 28 | | Key | Description | Default |Value type| 29 | +================+=============+=================+=========+==========+ 30 | | ``callback`` | ``compile`` | Compilation | None |`function`| 31 | | | | callback | | | 32 | | +-------------+-----------------+ | | 33 | | | ``judge`` | Callback after | | | 34 | | | | judging | | | 35 | +----------------+-------------+-----------------+---------+----------+ 36 | | ``demux`` | ``compile`` | Return `stdout` |``False``| `bool` | 37 | | | | and `stderr` of | | | 38 | | | | compiler | | | 39 | | | | separately | | | 40 | +----------------+-------------+-----------------+---------+----------+ 41 | | ``iofilename`` | ``in`` | Input filename | `stdin` | `str` | 42 | | +-------------+-----------------+---------+ | 43 | | | ``out`` | Output filename | `stdout`| | 44 | +----------------+-------------+-----------------+---------+----------+ 45 | | ``limit`` | ``time`` | Time limit | ``1`` | `int` or | 46 | | | | | | `float` | 47 | +----------------+-------------+-----------------+---------+----------+ 48 | | ``network`` | Network enabled |``False``| `bool` | 49 | +------------------------------+-----------------+---------+----------+ 50 | | ``threads`` | Thread limit | None | `int` | 51 | +------------------------------+-----------------+---------+----------+ 52 | :type config: dict 53 | :param client: Docker client 54 | :type client: |DockerClient|_ 55 | 56 | .. |DockerClient| replace:: `docker.client.DockerClient` 57 | .. _DockerClient: https://docker-py.readthedocs.io/en/stable/client.html\ 58 | #docker.client.DockerClient 59 | 60 | :return: Result 61 | :rtype: `list` 62 | 63 | === ========== ======================== 64 | Key Value type Description 65 | === ========== ======================== 66 | `0` `list` Result of each test case 67 | `1` `byte` Compiler output 68 | === ========== ======================== 69 | 70 | Tese case 71 | 72 | === =================================== ===================== 73 | Key Value type Description 74 | === =================================== ===================== 75 | `0` :class:`~dockerjudge.status.Status` Status code 76 | `1` `tuple` `stdout` and `stderr` 77 | `2` `float` Time spent 78 | === =================================== ===================== 79 | """ 80 | config = config or {} 81 | client = client or docker.from_env(version="auto") 82 | try: 83 | processor = getattr(_processor, processor[0])(**processor[1]) 84 | except TypeError: 85 | try: 86 | processor = getattr(_processor, processor[0])(*processor[1]) 87 | except TypeError: 88 | pass 89 | container = client.containers.run( 90 | processor.image, 91 | detach=True, 92 | tty=True, 93 | network_disabled=not config.get("network"), 94 | ) 95 | try: 96 | return run(container, processor, source, tests, config) 97 | finally: 98 | container.remove(force=True) 99 | 100 | 101 | def compile_source_code(container, processor, source, config): 102 | "Compile the source file" 103 | container.exec_run(f"mkdir -p {processor.workdir}/0") 104 | put_bin( 105 | container, 106 | PurePosixPath(f"{processor.workdir}/0/{processor.source}"), 107 | source, 108 | ) 109 | 110 | exec_run(container, processor.before_compile, f"{processor.workdir}/0") 111 | exec_result = container.exec_run( 112 | processor.compile, 113 | workdir=f"{processor.workdir}/0", 114 | demux=config["demux"].get("compile", False), 115 | ) 116 | if "compile" in config["callback"]: 117 | config["callback"]["compile"]( 118 | exec_result.exit_code, exec_result.output 119 | ) 120 | exec_run(container, processor.after_compile, f"{processor.workdir}/0") 121 | return exec_result 122 | 123 | 124 | def judge_test_cases(container, processor, tests, config): 125 | "Judge test cases" 126 | with ThreadPoolExecutor(max_workers=config.get("threads")) as executor: 127 | futures = [] 128 | for i, test in zip(range(len(tests)), tests): 129 | futures.append( 130 | executor.submit( 131 | test_case.__init__, 132 | container, 133 | processor, 134 | i + 1, 135 | test, 136 | config, 137 | ) 138 | ) 139 | futures[-1].add_done_callback(partial(done_callback, i, config)) 140 | return [ 141 | future.result() 142 | if not future.exception() 143 | else (Status.UE, (None, None), 0.0) 144 | for future in futures 145 | ] 146 | 147 | 148 | def done_callback(i, config, future): 149 | "Callback function for concurrent.futures.Future.add_done_callback" 150 | result = ( 151 | (Status.UE, (None, None), 0.0) 152 | if future.exception() 153 | else future.result() 154 | ) 155 | try: 156 | config["callback"].get("judge")(i, *result) 157 | except TypeError: 158 | pass 159 | 160 | 161 | def run(container, processor, source, tests, config=None): 162 | "Compile and judge" 163 | config.setdefault("callback", {}) 164 | config.setdefault("demux", {}) 165 | config.setdefault("iofilename", {}) 166 | exec_result = compile_source_code(container, processor, source, config) 167 | if exec_result.exit_code: 168 | return [ 169 | [(Status.CE, (None, None), 0.0)] * len(tests), 170 | exec_result.output, 171 | ] 172 | 173 | res = judge_test_cases(container, processor, tests, config) 174 | return [res, exec_result.output] 175 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. dockerjudge documentation master file, created by 2 | sphinx-quickstart on Thu Jul 9 19:16:09 2020. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to dockerjudge's documentation! 7 | ======================================= 8 | 9 | 10 | Badges 11 | ====== 12 | 13 | +-----------------+-------------------------------------------------------------------------------------------------------+ 14 | | Service | Status | 15 | +=================+=======================================================================================================+ 16 | | AppVeyor | .. image:: https://ci.appveyor.com/api/projects/status/qq5br83e4hp7utpo?svg=true | 17 | | | :alt: Build status | 18 | | | :target: https://ci.appveyor.com/project/t9n81yiq9d/dockerjudge | 19 | +-----------------+-------------------------------------------------------------------------------------------------------+ 20 | | Azure Pipelines | .. image:: https://dev.azure.com/wangxinhe/wangxinhe/_apis/build/status/wxh06.dockerjudge | 21 | | | :alt: Build Status | 22 | | | :target: https://dev.azure.com/wangxinhe/wangxinhe/_build/latest?definitionId=1 | 23 | | +-------------------------------------------------------------------------------------------------------+ 24 | | | .. image:: https://img.shields.io/azure-devops/tests/wangxinhe/wangxinhe/1 | 25 | | | :alt: Azure DevOps tests | 26 | | | :target: https://dev.azure.com/wangxinhe/wangxinhe/_test/analytics?definitionId=1 | 27 | +-----------------+-------------------------------------------------------------------------------------------------------+ 28 | | Code Climate | .. image:: https://api.codeclimate.com/v1/badges/dfe666a2140cd3390e56/maintainability | 29 | | | :alt: Maintainability | 30 | | | :target: https://codeclimate.com/github/wxh06/dockerjudge/maintainability | 31 | | +-------------------------------------------------------------------------------------------------------+ 32 | | | .. image:: https://api.codeclimate.com/v1/badges/dfe666a2140cd3390e56/test_coverage | 33 | | | :alt: Test Coverage | 34 | | | :target: https://codeclimate.com/github/wxh06/dockerjudge/test_coverage | 35 | +-----------------+-------------------------------------------------------------------------------------------------------+ 36 | | FOSSA | .. image:: https://app.fossa.com/api/projects/git%2Bgithub.com%2Fwxh06%2Fdockerjudge.svg?type=shield | 37 | | | :alt: FOSSA Status | 38 | | | :target: https://app.fossa.com/projects/git%2Bgithub.com%2Fwxh06%2Fdockerjudge | 39 | +-----------------+-------------------------------------------------------------------------------------------------------+ 40 | | GitHub Actions | .. image:: https://github.com/wxh06/dockerjudge/workflows/Docker/badge.svg | 41 | | | :alt: Docker | 42 | | | :target: https://github.com/wxh06/dockerjudge/actions?query=workflow%3ADocker | 43 | | +-------------------------------------------------------------------------------------------------------+ 44 | | | .. image:: https://github.com/wxh06/dockerjudge/workflows/Lint%20Code%20Base/badge.svg | 45 | | | :alt: Lint Code Base | 46 | | | :target: https://github.com/wxh06/dockerjudge/actions?query=workflow%3A%22Lint+Code+Base%22 | 47 | | +-------------------------------------------------------------------------------------------------------+ 48 | | | .. image:: https://github.com/wxh06/dockerjudge/workflows/Python%20package/badge.svg | 49 | | | :alt: Python package | 50 | | | :target: https://github.com/wxh06/dockerjudge/actions?query=workflow%3A%22Python+package%22 | 51 | | +-------------------------------------------------------------------------------------------------------+ 52 | | | .. image:: https://github.com/wxh06/dockerjudge/workflows/Transifex/badge.svg | 53 | | | :alt: Trnsifex | 54 | | | :target: https://github.com/wxh06/dockerjudge/actions?query=workflow%3ATransifex | 55 | | +-------------------------------------------------------------------------------------------------------+ 56 | | | .. image:: https://github.com/wxh06/dockerjudge/workflows/Upload%20Python%20Package/badge.svg | 57 | | | :alt: Upload Python Package | 58 | | | :target: https://github.com/wxh06/dockerjudge/actions?query=workflow%3A%22Upload+Python+Package%22 | 59 | +-----------------+-------------------------------------------------------------------------------------------------------+ 60 | | GitLab CI/CD | .. image:: https://gitlab.com/wangxinhe/dockerjudge/badges/master/pipeline.svg | 61 | | | :alt: pipeline | 62 | | | :target: https://gitlab.com/wangxinhe/dockerjudge/pipelines | 63 | | +-------------------------------------------------------------------------------------------------------+ 64 | | | .. image:: https://gitlab.com/wangxinhe/dockerjudge/badges/master/coverage.svg | 65 | | | :alt: coverage | 66 | | | :target: https://gitlab.com/wangxinhe/dockerjudge/-/jobs | 67 | +-----------------+-------------------------------------------------------------------------------------------------------+ 68 | | Read the Docs | .. image:: https://readthedocs.org/projects/dockerjudge/badge/ | 69 | | | :alt: Documentation Status | 70 | | | :target: https://readthedocs.org/projects/dockerjudge/builds/ | 71 | +-----------------+-------------------------------------------------------------------------------------------------------+ 72 | | Travis CI | .. image:: https://travis-ci.com/wxh06/dockerjudge.svg | 73 | | | :alt: Build Status | 74 | | | :target: https://travis-ci.com/wxh06/dockerjudge | 75 | | +-------------------------------------------------------------------------------------------------------+ 76 | | | .. image:: https://codecov.io/gh/wxh06/dockerjudge/branch/master/graph/badge.svg | 77 | | | :alt: Codecov | 78 | | | :target: https://codecov.io/gh/wxh06/dockerjudge | 79 | +-----------------+-------------------------------------------------------------------------------------------------------+ 80 | 81 | 82 | Project links 83 | ============= 84 | 85 | - `GitHub Repository `_ 86 | - `GitLab Repository `_ 87 | - `PyPI Project `_ 88 | - `Transifex Translation `_ 89 | 90 | .. toctree:: 91 | :maxdepth: 2 92 | :caption: Contents: 93 | 94 | installation.rst 95 | main.rst 96 | processor.rst 97 | status.rst 98 | 99 | 100 | 101 | Indices and tables 102 | ================== 103 | 104 | * :ref:`genindex` 105 | * :ref:`modindex` 106 | * :ref:`search` 107 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2019 汪心禾 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /dockerjudge/processor.py: -------------------------------------------------------------------------------- 1 | """Processors 2 | 3 | +---------------------------------------+--------------+-----------------+ 4 | | Processor | Language(s) |Required `Docker | 5 | | | [*]_ |image `_| 7 | +=======================================+==============+=================+ 8 | | :class:`~dockerjudge.processor.Bash` | Shell | |bash|_ | 9 | +---------------------------------------+--------------+-----------------+ 10 | | :class:`~dockerjudge.processor.Clang` | - C (``c``) | |clang|_ | 11 | | | - **C++** | | 12 | | | (``cpp``) | | 13 | +---------------------------------------+--------------+-----------------+ 14 | | :class:`~dockerjudge.processor.GCC` | - C | |gcc|_ | 15 | | | (``c``) | | 16 | | | - **C++** | | 17 | | | (``cpp``) | | 18 | | | - Go | | 19 | | | (``go``) | | 20 | +---------------------------------------+--------------+-----------------+ 21 | | :class:`~dockerjudge.processor.Go` | Go | |golang|_ | 22 | +---------------------------------------+--------------+-----------------+ 23 | | :class:`~dockerjudge.processor.Mono` |- Visual Basic| |mono|_ | 24 | | | (``vb``) | | 25 | | |- **C#** | | 26 | | | (``csharp``)| | 27 | +---------------------------------------+--------------+-----------------+ 28 | | :class:`~dockerjudge.processor.Node` | Node.js | |node|_ | 29 | +---------------------------------------+--------------+-----------------+ 30 | |:class:`~dockerjudge.processor.OpenJDK`| Java | |openjdk|_ | 31 | +---------------------------------------+--------------+-----------------+ 32 | | :class:`~dockerjudge.processor.PHP` | PHP | |php|_ | 33 | +---------------------------------------+--------------+-----------------+ 34 | | :class:`~dockerjudge.processor.PyPy` | Python | |pypy|_ | 35 | +---------------------------------------+--------------+-----------------+ 36 | | :class:`~dockerjudge.processor.Python`| Python | |python|_ | 37 | +---------------------------------------+--------------+-----------------+ 38 | | :class:`~dockerjudge.processor.Ruby` | Ruby | |ruby|_ | 39 | +---------------------------------------+--------------+-----------------+ 40 | | :class:`~dockerjudge.processor.Swift` | Swift | |swift|_ | 41 | +---------------------------------------+--------------+-----------------+ 42 | 43 | .. https://docutils.sourceforge.io/FAQ.html#is-nested-inline-markup-possible 44 | .. |node| replace:: `node` 45 | .. _node: https://hub.docker.com/_/node 46 | .. |openjdk| replace:: `openjdk` 47 | .. _openjdk: https://hub.docker.com/_/openjdk 48 | .. |php| replace:: `php` 49 | .. _php: https://hub.docker.com/_/php 50 | .. |pypy| replace:: `pypy` 51 | .. _pypy: https://hub.docker.com/_/pypy 52 | .. |python| replace:: `python` 53 | .. _python: https://hub.docker.com/_/python 54 | .. |ruby| replace:: `ruby` 55 | .. _ruby: https://hub.docker.com/_/ruby 56 | .. |swift| replace:: `swift` 57 | .. _swift: https://hub.docker.com/_/swift 58 | 59 | .. [*] Emboldened language by default. 60 | """ 61 | # pylint: disable = missing-function-docstring, too-few-public-methods 62 | 63 | import shlex 64 | from enum import Enum 65 | from pathlib import PurePosixPath 66 | 67 | 68 | class Processor: 69 | """Defines the operations of a multi-version programming language processor 70 | 71 | ================== ======================================== 72 | Data Type 73 | ================== ======================================== 74 | ``image`` `str` 75 | ``workdir`` :class:`~pathlib.PurePosixPath` or `str` 76 | ``source`` `str` 77 | ``before_compile`` `str` or `list` 78 | ``compile`` `str` or `list` 79 | ``after_compile`` `str` or `list` 80 | ``before_judge`` `str` or `list` 81 | ``judge`` `str` 82 | ``after_judge`` `str` or `list` 83 | ================== ======================================== 84 | """ 85 | 86 | @staticmethod 87 | def _get_image_with_tag(image, tag): 88 | return image + (f":{tag}" if tag else "") 89 | 90 | image = None 91 | workdir = PurePosixPath("/dockerjudge") 92 | source = None 93 | before_compile = None 94 | compile = None 95 | after_compile = None 96 | before_judge = None 97 | judge = None 98 | after_judge = None 99 | 100 | 101 | class _Language(Enum): 102 | "Get programming language from enum" 103 | 104 | @classmethod 105 | def __get_language(cls, language, default=None): 106 | if isinstance(language, cls): 107 | return language 108 | try: 109 | return cls[language] 110 | except KeyError: 111 | try: 112 | return cls(language) 113 | except ValueError: 114 | return cls.__get_language(default) 115 | 116 | 117 | class Bash(Processor): 118 | """Bash is the GNU Project's Bourne Again SHell 119 | 120 | :param version: Tag name of Docker image |bash|_ 121 | :type version: `str`, `int` or `float` 122 | 123 | .. |bash| replace:: `bash` 124 | .. _bash: https://hub.docker.com/_/bash 125 | """ 126 | 127 | def __init__(self, version=None): 128 | self.image = self._get_image_with_tag("bash", version) 129 | self.source = "bash.sh" 130 | self.compile = ["bash", "-n", self.source] 131 | self.judge = f"bash {self.source}" 132 | 133 | 134 | class Clang(Processor): 135 | """Clang C Language Family Frontend for LLVM 136 | 137 | :param language: Programming panguage 138 | (``C``\\ /\\ `c` or ``C++``\\ /\\ ``cpp``), `C++` by default 139 | :type language: :class:`dockerjudge.processor.Clang.Language` or `str` 140 | :param version: Tag name of Docker image |clang|_ 141 | :type version: `str`, `int` or `float` 142 | :param filenames: Filenames of source code and binary file, 143 | C++ default: ``{'src': 'a.cpp', 'bin': None}`` 144 | :type filenames: `dict` 145 | :param options: Compiler options 146 | :type options: `list` or `str` 147 | 148 | .. |clang| replace:: `clangbuiltlinux/ubuntu` 149 | .. _clang: https://hub.docker.com/r/clangbuiltlinux/ubuntu 150 | """ 151 | 152 | class Language(_Language): 153 | """Programming language, `C` (``c``) or `C++` (``cpp``) 154 | 155 | :C: ``Clang.Language.c``, 156 | ``Clang.Language['c']`` or ``Clang.Language('C')`` 157 | :C++: ``Clang.Language.cpp``, 158 | ``Clang.Language['cpp']`` or ``Clang.Language('C++')`` 159 | """ 160 | 161 | c = "C" 162 | cpp = "C++" 163 | 164 | @classmethod 165 | def _get_language(cls, language): 166 | return super().__get_language(language, cls.cpp) 167 | 168 | def __init__( 169 | self, language=None, version=None, filenames=None, options=None 170 | ): 171 | lang = self.Language._get_language(language) 172 | fns = filenames or {} 173 | args = options or [] 174 | 175 | self.image = "clangbuiltlinux/ubuntu" + f":llvm{version}-latest" 176 | self.source = fns.get("src", f"a.{lang.name}") 177 | self.compile = ( 178 | [ 179 | {self.Language.c: "clang", self.Language.cpp: "clang++"}[lang] 180 | + f"-{version}", 181 | self.source, 182 | ] 183 | + (["-o", fns["bin"]] if fns.get("bin") else []) 184 | + (shlex.split(args) if isinstance(args, str) else args) 185 | ) 186 | self.after_compile = ["rm", self.source] 187 | self.judge = f"./{fns.get('bin', 'a.out')}" 188 | 189 | 190 | class GCC(Processor): 191 | """GNU project C, C++ and Go compiler 192 | 193 | :param language: Programming panguage 194 | (``C``\\ /\\ ``c``, ``C++``\\ /\\ ``cpp`` or ``Go``\\ /\\ ``go``), 195 | `C++` by default 196 | :type language: :class:`dockerjudge.processor.GCC.Language` or `str` 197 | :param version: Tag name of Docker image |gcc|_ 198 | :type version: `str`, `int` or `float` 199 | :param filenames: Filenames of source code and binary file, 200 | C++ default: ``{'src': 'a.cpp', 'bin': None}`` 201 | :type filenames: `dict` 202 | :param options: Compiler options 203 | :type options: `list` or `str` 204 | 205 | .. |gcc| replace:: `gcc` 206 | .. _gcc: https://hub.docker.com/_/gcc 207 | """ 208 | 209 | class Language(_Language): 210 | """Programming language, `C` (``c``), `C++` (``cpp``) or `Go` (``go``) 211 | 212 | :C: ``GCC.Language.c``, 213 | ``GCC.Language['c']`` or ``GCC.Language('C')`` 214 | :C++: ``GCC.Language.cpp``, 215 | ``GCC.Language['cpp']`` or ``GCC.Language('C++')`` 216 | :Go: ``GCC.Language.go``, 217 | ``GCC.Language['go']`` or ``GCC.Language('Go')`` 218 | """ 219 | 220 | c = "C" 221 | cpp = "C++" 222 | go = "Go" 223 | 224 | @classmethod 225 | def _get_language(cls, language): 226 | return super().__get_language(language, cls.cpp) 227 | 228 | def __init__( 229 | self, language=None, version=None, filenames=None, options=None 230 | ): 231 | lang = self.Language._get_language(language) 232 | fns = filenames or {} 233 | args = options or [] 234 | 235 | self.image = self._get_image_with_tag("gcc", version) 236 | self.source = fns.get("src", f"a.{lang.name}") 237 | self.compile = ( 238 | [ 239 | { 240 | self.Language.c: "gcc", 241 | self.Language.cpp: "g++", 242 | self.Language.go: "gccgo", 243 | }[lang], 244 | self.source, 245 | ] 246 | + (["-o", fns["bin"]] if fns.get("bin") else []) 247 | + (shlex.split(args) if isinstance(args, str) else args) 248 | ) 249 | self.after_compile = ["rm", self.source] 250 | self.judge = f"./{fns.get('bin', 'a.out')}" 251 | 252 | 253 | class Go(Processor): 254 | """The Go Programming Language 255 | 256 | :param version: Tag name of Docker image |golang|_ 257 | :type version: `str`, `int` or `float` 258 | :param filenames: Filenames of source code and binary file, 259 | default: ``{'src': 'main.go', 'bin': None}`` 260 | :type filenames: `dict` 261 | 262 | .. |golang| replace:: `golang` 263 | .. _golang: https://hub.docker.com/_/golang 264 | """ 265 | 266 | def __init__(self, version=None, filenames=None, options=None): 267 | fns = filenames or {} 268 | args = options or [] 269 | 270 | self.image = self._get_image_with_tag("golang", version) 271 | self.source = fns.get("src", "main.go") 272 | self.compile = ( 273 | ["go", "build"] 274 | + (["-o", fns["bin"]] if fns.get("bin") else []) 275 | + args 276 | + [self.source] 277 | ) 278 | self.after_compile = ["rm", self.source] 279 | self.judge = f"./{fns.get('bin', 'main')}" 280 | 281 | 282 | class Mono(Processor): 283 | """**Mono** is a software platform 284 | designed to allow developers to easily create 285 | cross platform applications part of the `.NET Foundation`_. 286 | 287 | Sponsored by Microsoft_, 288 | Mono is an open source implementation of Microsoft's .NET Framework 289 | based on the ECMA_ standards 290 | for `C#`_ and the `Common Language Runtime`_. 291 | 292 | :param language: Programming panguage 293 | (``Visual Basic``\\ /\\ ``vb`` or ``C#``\\ /\\ ``csharp``), 294 | C# by default 295 | :type language: :class:`dockerjudge.processor.Mono.Language` or `str` 296 | :param version: Tag name of Docker image |mono|_ 297 | :type version: `str`, `int` or `float` 298 | 299 | .. _.NET Foundation: https://www.dotnetfoundation.org 300 | .. _Microsoft: https://www.microsoft.com 301 | .. _ECMA: https://www.mono-project.com/docs/about-mono/languages/ecma/ 302 | .. _C#: https://www.mono-project.com/docs/about-mono/languages/csharp/ 303 | .. _Common Language Runtime: 304 | https://www.mono-project.com/docs/advanced/runtime/ 305 | .. |mono| replace:: `mono` 306 | .. _mono: https://hub.docker.com/_/mono 307 | """ 308 | 309 | class Language(_Language): 310 | """Programming language, `Visual Basic` (``vb``) or `C#` (``csharp``) 311 | 312 | :Visual Basic: ``GCC.Language.vb``, 313 | ``GCC.Language['vb']`` or ``GCC.Language('Visual Basic')`` 314 | :C#: ``GCC.Language.csharp``, 315 | ``GCC.Language['csharp']`` or ``GCC.Language('C#')`` 316 | """ 317 | 318 | vb = "Visual Basic" 319 | csharp = "C#" 320 | 321 | @classmethod 322 | def _get_language(cls, language): 323 | return super().__get_language(language, cls.csharp) 324 | 325 | def __init__(self, language=None, version=None): 326 | lang = self.Language._get_language(language) 327 | 328 | self.image = self._get_image_with_tag("mono", version) 329 | self.source = f"""mono.{ 330 | {self.Language.csharp: 'cs', self.Language.vb: 'vb'}[lang] 331 | }""" 332 | self.compile = [ 333 | {self.Language.csharp: "csc", self.Language.vb: "vbnc"}[lang], 334 | self.source, 335 | ] 336 | self.after_compile = ["rm", self.source] 337 | self.judge = "mono mono.exe" 338 | 339 | 340 | class Node(Processor): 341 | "Node.js®" 342 | 343 | def __init__(self, version=None): 344 | self.image = self._get_image_with_tag("node", version) 345 | self.source = "index.js" 346 | self.compile = ["node", "-c", self.source] 347 | self.judge = f"node {self.source}" 348 | 349 | 350 | class OpenJDK(Processor): 351 | "Open Java Development Kit" 352 | 353 | def __init__(self, version=None): 354 | self.image = self._get_image_with_tag("openjdk", version) 355 | self.source = "Main.java" 356 | self.compile = ["javac", self.source] 357 | self.after_compile = ["rm", self.source] 358 | self.judge = "java Main" 359 | 360 | 361 | class PHP(Processor): 362 | "PHP" 363 | 364 | def __init__(self, version=None): 365 | self.image = self._get_image_with_tag("php", version) 366 | self.source = "index.php" 367 | self.compile = ["php", "-l", self.source] 368 | self.judge = f"php {self.source}" 369 | 370 | 371 | class PyPy(Processor): 372 | "PyPy" 373 | 374 | def __init__(self, version=None): 375 | pypy = "pypy" if str(version).startswith("2") else "pypy3" 376 | 377 | self.image = self._get_image_with_tag("pypy", version) 378 | self.source = "__init__.py" 379 | self.compile = [pypy, "-m", "compileall", "."] 380 | self.judge = f"{pypy} {self.source}" 381 | 382 | 383 | class Python(Processor): 384 | "CPython" 385 | 386 | def __init__(self, version=None): 387 | self.image = self._get_image_with_tag("python", version) 388 | self.source = "__init__.py" 389 | self.compile = ["python", "-m", "compileall", "."] 390 | self.judge = f"python {self.source}" 391 | 392 | 393 | class Ruby(Processor): 394 | "Ruby" 395 | 396 | def __init__(self, version=None): 397 | self.image = self._get_image_with_tag("ruby", version) 398 | self.source = "ruby.rb" 399 | self.compile = ["ruby", "-wc", self.source] 400 | self.judge = f"ruby {self.source}" 401 | 402 | 403 | class Swift(Processor): 404 | "Swift" 405 | 406 | def __init__(self, version=None): 407 | self.image = self._get_image_with_tag("swift", version) 408 | self.source = "main.swift" 409 | self.compile = ["swiftc", self.source] 410 | self.judge = "./main" 411 | -------------------------------------------------------------------------------- /README.zh_Hans_CN.md: -------------------------------------------------------------------------------- 1 | 2 |

3 | 4 | Piterator 5 |
6 | 由 Piterator 团队用 <3 制作 7 |
8 |

9 | 10 | # ![dockerjudge](https://static.dockerjudge.piterator.com/dockerjudge.svg) 11 | 12 | [![Maintainability](https://api.codeclimate.com/v1/badges/dfe666a2140cd3390e56/maintainability)](https://codeclimate.com/github/wxh06/dockerjudge/maintainability) 13 | [![Python 包](https://github.com/wxh06/dockerjudge/workflows/Python%20package/badge.svg)](https://github.com/wxh06/dockerjudge/actions?query=workflow%3A%22Python+package%22) 14 | [![上传 Python 包](https://github.com/wxh06/dockerjudge/workflows/Upload%20Python%20Package/badge.svg)](https://github.com/wxh06/dockerjudge/actions?query=workflow%3A%22Upload+Python+Package%22) 15 | [![Transifex](https://github.com/wxh06/dockerjudge/workflows/Transifex/badge.svg)](https://www.transifex.com/piterator/dockerjudge/) 16 | [![构建状态](https://ci.appveyor.com/api/projects/status/qq5br83e4hp7utpo?svg=true)](https://ci.appveyor.com/project/t9n81yiq9d/dockerjudge) 17 | [![构建状态](https://dev.azure.com/wangxinhe/wangxinhe/_apis/build/status/wxh06.dockerjudge)](https://dev.azure.com/wangxinhe/wangxinhe/_build/latest?definitionId=1) 18 | [![Azure DevOps 测试](https://img.shields.io/azure-devops/tests/wangxinhe/wangxinhe/1)](https://dev.azure.com/wangxinhe/wangxinhe/_test/analytics?definitionId=1) 19 | [![构建状态](https://travis-ci.com/wxh06/dockerjudge.svg)](https://travis-ci.com/wxh06/dockerjudge) 20 | [![CodeCov](https://codecov.io/gh/wxh06/dockerjudge/graph/badge.svg)](https://codecov.io/gh/wxh06/dockerjudge) 21 | [![文档状态](https://readthedocs.org/projects/dockerjudge-zh-cn/badge/)](https://dockerjudge.readthedocs.io/zh_CN/latest/) 22 | [![Python 版本](https://img.shields.io/pypi/pyversions/dockerjudge.svg)](https://www.python.org/downloads/) 23 | [![GitHub pre-release](https://img.shields.io/github/release-pre/wxh06/dockerjudge.svg)](https://github.com/wxh06/dockerjudge/releases) 24 | [![PyPI](https://img.shields.io/pypi/v/dockerjudge.svg)](https://pypi.org/project/dockerjudge/#history) 25 | [![Wheel](https://img.shields.io/pypi/wheel/dockerjudge.svg)](https://pypi.org/project/dockerjudge/#files) 26 | [![License](https://img.shields.io/github/license/wxh06/dockerjudge.svg)](LICENSE) 27 | [![代码风格](https://img.shields.io/badge/code%20style-black-000000.svg)](https://black.readthedocs.io/) 28 | 29 | 🎌 [🇺🇸 English](README.md) | **🇨🇳 大陆简体** 30 | 31 | **基于 LXC 的在线测评引擎**,支持 [10+ 个编程语言处理程序](#支持的处理程序): 32 | 33 | - [Shell](https://zh.wikipedia.org/zh-cn/Unix_shell) 34 | - [Bash (**B**ourne-**A**gain **sh**ell)](https://zh.wikipedia.org/zh-cn/Bash) 35 | - [C](https://zh.wikipedia.org/zh-cn/C语言)/[C++](https://zh.wikipedia.org/zh-cn/C%2B%2B) 36 | - [GCC (The **G**NU **C**ompiler **C**ollection)](https://gcc.gnu.org/) 37 | - [LLVM Clang](https://clang.llvm.org/) 38 | - [.NET](https://docs.microsoft.com/zh-cn/dotnet/) ([C#](https://docs.microsoft.com/zh-cn/dotnet/csharp/) & [Visual Basic](https://docs.microsoft.com/zh-cn/dotnet/visual-basic/)) 39 | - [Mono](https://www.mono-project.com/) 40 | - [Go](https://golang.google.cn/) 41 | - [`go`](https://golang.google.cn/dl/) 42 | - [`gccgo` (GCC)](https://golang.google.cn/doc/install/gccgo) 43 | - [Java](https://www.oracle.com/cn/java/) 44 | - [OpenJDK](https://openjdk.java.net/) 45 | - [Node.js](https://nodejs.org/zh-cn/) 46 | - [`node`](https://nodejs.org/zh-cn/download/) 47 | - [PHP](https://www.php.net/) 48 | - [`php`](https://www.php.net/downloads) 49 | - [Python](https://www.python.org/) 50 | - [CPython](https://www.python.org/downloads/) 51 | - [PyPy](https://www.pypy.org/) 52 | - [Ruby](https://www.ruby-lang.org/zh_cn/) 53 | - [`ruby`](https://www.ruby-lang.org/zh_cn/downloads/) 54 | - [Swift](https://swift.org/) 55 | - [`swiftc`](https://swift.org/swift-compiler/) 56 | 57 | ## 支持的处理程序 58 | 59 | 阅读[文档](https://dockerjudge.readthedocs.io/zh_CN/latest/processor.html#module-dockerjudge.processor)以获取更多信息。 60 | 61 | 处理程序 | 语言\* | 必要的 [Docker 镜像](https://hub.docker.com/) 62 | -------- | ------ | --------------------------------------------- 63 | `Bash` | Shell | [`bash`](https://hub.docker.com/_/bash) 64 | `Clang` |
  • C (`c`)
  • **C++ (`cpp`)**
| [`clangbuiltlinux/ubuntu`](https://hub.docker.com/r/clangbuiltlinux/ubuntu) 65 | `GCC` |
  • C (`c`)
  • **C++ (`cpp`)**
  • Go (`go`)
| [`gcc`](https://hub.docker.com/_/gcc) 66 | `Go` | Go | [`golang`](https://hub.docker.com/_/golang) 67 | `Mono` |
  • Visual Basic (`vb`)
  • **C# (`csharp`)**
| [`mono`](https://hub.docker.com/_/mono) 68 | `Node` | Node.js | [`node`](https://hub.docker.com/_/node) 69 | `OpenJDK` | Java | [`openjdk`](https://hub.docker.com/_/openjdk) 70 | `PHP` | PHP | [`php`](https://hub.docker.com/_/php) 71 | `PyPy` | Python | [`pypy`](https://hub.docker.com/_/pypy) 72 | `Python`| Python | [`python`](https://hub.docker.com/_/python) 73 | `Ruby` | Ruby | [`ruby`](https://hub.docker.com/_/ruby) 74 | `Swift` | Swift | [`swift`](https://hub.docker.com/_/swift) 75 | 76 | **\*** 应该向多语言处理器提供 `language` 参数,否则将以默认语言(通常是 C++ 或 C#,表格中加粗的)对 `source` 进行测评。 77 | 78 | ## 安装 79 | 80 | 阅读[文档](https://dockerjudge.readthedocs.io/zh_CN/latest/installation.html)以获取更多信息。 81 | 82 | ### Docker 83 | 84 | 必须安装了 [Docker 引擎](https://www.docker.com/)才能运行 `dockerjudge`。 85 | 86 | #### 用简便脚本安装(服务器端) 87 | 88 | ```sh 89 | curl -fsSL https://get.docker.com -o get-docker.sh 90 | sudo sh get-docker.sh 91 | ``` 92 | 93 | 更多信息,请参阅 [Install Docker Engine \| Docker Documentation(英文)](https://docs.docker.com/engine/install/)。 94 | 95 | ### 包 96 | 97 | #### 从 [Python 包索引 (PyPI)](https://pypi.org/) 98 | 99 | [dockerjudge · PyPI](https://pypi.org/project/dockerjudge/) 100 | 101 | - [PyPI](https://pypi.org/simple/dockerjudge/) 102 | - [阿里巴巴开源镜像站](https://mirrors.aliyun.com/pypi/simple/dockerjudge/) 103 | - [清华大学开源软件镜像站 | Tsinghua Open Source Mirror](https://pypi.tuna.tsinghua.edu.cn/simple/dockerjudge/) 104 | 105 | ##### 通过 [pip](https://pip.pypa.io/) 106 | 107 | ```sh 108 | pip install dockerjudge 109 | ``` 110 | 111 | ##### 通过 [Easy install](https://setuptools.readthedocs.io/en/latest/easy_install.html) (不建议) 112 | 113 | ```sh 114 | easy_install dockerjudge 115 | ``` 116 | 117 | #### 从 [GitHub](https://github.com/) 118 | 119 | [wxh06/dockerjudge: A Docker Based Online Judge Engine](https://github.com/wxh06/dockerjudge) 120 | 121 | - HTTPS: `https://github.com/wxh06/dockerjudge.git` 122 | - SSH: `git@github.com:wxh06/dockerjudge.git` 123 | 124 | ```sh 125 | git clone https://github.com/wxh06/dockerjudge.git 126 | cd dockerjudge 127 | 128 | make pip && make # python3 -m pip install -Ur requirements.txt && python3 setup.py build 129 | sudo make install # python3 setup.py install 130 | ``` 131 | 132 | ## 用法示例 133 | 134 | 阅读[文档](https://dockerjudge.readthedocs.io/zh_CN/latest/__init__.html)以获取更多信息。 135 | 136 | ```python 137 | >>> from dockerjudge import judge 138 | >>> from dockerjudge.processor import GCC, Clang, Bash, Python, Node, OpenJDK, PHP, Ruby, Mono, Swift 139 | >>> 140 | >>> judge( 141 | ... GCC(GCC.Language.c), # 或 `GCC('c')` / `GCC('C')`,意为用 `gcc` 命令编译 C 语言源码 142 | ... b''' 143 | ... #include 144 | ... int main() { 145 | ... int a, b; 146 | ... scanf("%d %d", &a, &b); 147 | ... printf("%d", a / b); 148 | ... return 0; 149 | ... } 150 | ... ''', 151 | ... [ 152 | ... (b'1 1', b'1'), # AC 153 | ... (b'1 2', b'0.5'), # WA 154 | ... (b'0 0', b'') # RE 155 | ... ] 156 | ... ) 157 | [ 158 | [ 159 | (, (b'1', b''), 0.001), 160 | (, (b'0', b''), 0.001), 161 | (, (None, b'Floating point exception (core dumped)\n'), 0.01) 162 | ], 163 | b'' 164 | ] 165 | >>> 166 | >>> judge(GCC(GCC.Language.c), b'', [(b'', b'')]) # CE 167 | [ 168 | [ 169 | (, (None, None), 0.0) 170 | ], 171 | b"/usr/bin/ld: /usr/lib/x86_64-linux-gnu/crt1.o: in function `_start':\n(.text+0x20): undefined reference to `main'\ncollect2: error: ld returned 1 exit status\n" 172 | ] 173 | >>> 174 | >>> judge( 175 | ... GCC(GCC.Language.cpp), # 或 `GCC('cpp')` / `GCC('C++')`,意为用 `g++` 命令编译 C++ 源码 176 | ... b''' 177 | ... #include 178 | ... int main() { 179 | ... printf("Hello, world!"); 180 | ... while (true) 181 | ... ; 182 | ... } 183 | ... ''', 184 | ... [ 185 | ... (b'', b'Hello, world!') # TLE 186 | ... ], 187 | ... { 188 | ... 'limit': { 189 | ... 'time': .1 190 | ... } 191 | ... } 192 | ... ) 193 | [ 194 | [ 195 | (, (None, b'bash: line 1: 35 Killed timeout -sKILL 0.1 sh -c ./a.out > /dockerjudge/1.out < /dockerjudge/1.in\n'), 0.100) 196 | ], 197 | b'' 198 | ] 199 | >>> 200 | >>> judge( 201 | ... GCC( 202 | ... GCC.Language.c, 203 | ... 'latest', # GCC 版本号,比如 `4` 或 `4.8` 等 204 | ... {'bin': 'a'} # `gcc` 之 `-o` 选项的实参——二进制文件名 205 | ... ), 206 | ... b''' 207 | ... #include 208 | ... int main() { 209 | ... int a, b; 210 | ... freopen("a.in", "r", stdin); // Open `a.in` as stdin 211 | ... scanf("%d %d", &a, &b); // Scan from `a.in` 212 | ... freopen("a.out", "w", stdout); // Open `a.out` as stdout 213 | ... printf("%d", a / b); // Print to `a.out` 214 | ... return 0; 215 | ... } 216 | ... ''', 217 | ... [ 218 | ... (b'1 1', b'1'), # AC 219 | ... (b'1 2', b'0.5'), # WA 220 | ... (b'0 0', b'') # RE 221 | ... ], 222 | ... { 223 | ... 'iofilename': { 224 | ... 'in': 'a.in', 225 | ... 'out': 'a.out' 226 | ... } 227 | ... } 228 | ... ) 229 | [ 230 | [ 231 | (, (b'1', b''), 0.001), 232 | (, (b'0', b''), 0.001), 233 | (, (None, b'Floating point exception (core dumped)\n'), 0.001) 234 | ], 235 | b'' 236 | ] 237 | >>> 238 | >>> judge( 239 | ... GCC(GCC.Language.c, filenames={'bin': 'a'}), 240 | ... b''' 241 | ... #include 242 | ... int main() { 243 | ... int a, b; 244 | ... scanf("%d %d", &a, &b); 245 | ... printf("%d", a / b); 246 | ... return 0; 247 | ... } 248 | ... ''', 249 | ... [ 250 | ... (b'1 1', b'1'), 251 | ... (b'0 0', b'') 252 | ... ], 253 | ... { 254 | ... 'iofilename': { 255 | ... 'out': 'a.out' # ONF 256 | ... } 257 | ... } 258 | ... ) 259 | [ 260 | [ 261 | (, (None, b''), 0.001), 262 | (, (None, b'Floating point exception (core dumped)\n'), 0.001) 263 | ], 264 | b'' 265 | ] 266 | >>> 267 | >>> judge( # BTW,从 4.9 开始 GCC 还支持 Go,叫 `gccgo` 268 | ... GCC(GCC.Language.go), 269 | ... b'package main\n' 270 | ... b'' 271 | ... b'import "fmt"\n' 272 | ... b'' 273 | ... b'func main() {\n' 274 | ... br' fmt.Printf("hello, world\n")'b'\n' 275 | ... b'}\n', 276 | ... [(b'', b'hello, world')] 277 | ... ) 278 | [ 279 | [ 280 | (, (b'hello, world\n', b''), 0.02) 281 | ], 282 | b'' 283 | ] 284 | >>> 285 | >>> judge( 286 | ... Clang( # 除了 GCC,还支持 LLVM Clang(参数与 GCC 相同) 287 | ... Clang.Language.c, # 仅支持 C 与 C++ 288 | ... 11 # **必须**提供 LLVM CLang 的版本号! 289 | ... ), 290 | ... b'', # CE 291 | ... [ 292 | ... (b'', b'') 293 | ... ] 294 | ... ) 295 | [ 296 | [ 297 | (, (None, None), 0.0) 298 | ], 299 | b"/usr/bin/ld: /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crt1.o: in function `_start':\n' 300 | b"(.text+0x24): undefined reference to `main'\n" 301 | b'clang: error: linker command failed with exit code 1 (use -v to see invocation)\n' 302 | ] 303 | >>> 304 | >>> # 亦支持其它编程语言 305 | >>> judge(Bash(), b'echo Hello, world!', [(b'', b'Hello, world!')]) # Bash 306 | [ 307 | [ 308 | (, (b'Hello, world!\n', b''), 0.001) 309 | ], 310 | b'' 311 | ] 312 | >>> 313 | >>> judge(Python(3), b"print('Hello, world!')", [(b'', b'Hello, world!')]) # Python 3 314 | [ 315 | [ 316 | (, (b'Hello, world!\n', b''), 0.05) 317 | ], 318 | b"Listing '.'...\n" 319 | b"Compiling './__init__.py'...\n" 320 | ] 321 | >>> judge(PyPy(), b"print('Hello, world!')", [(b'', b'Hello, world!')]) # PyPy 3 322 | [ 323 | [ 324 | (, (b'Hello, world!\n', b''), 0.075) 325 | ], 326 | b"Listing '.'...\n" 327 | b"Compiling './__init__.py'...\n" 328 | ] 329 | >>> 330 | >>> judge(Node(12), b'console.log("Hello World")', [(b'', b'Hello World')]) # Node.js 331 | [ 332 | [ 333 | (, (b'Hello World\n', b''), 0.05) 334 | ], 335 | b'' 336 | ] 337 | >>> 338 | >>> judge( # Java / OpenJDK 339 | ... OpenJDK(), # 默认的源代码文件名是 `Main.java`,即 public class 名称应该为 `Main` 340 | ... b''' 341 | ... public class Main { 342 | ... public static void main(String[] args) { 343 | ... System.out.println("Hello, world!"); 344 | ... } 345 | ... } 346 | ... ''', 347 | ... [ 348 | ... (b'', b'Hello, world!') 349 | ... ] 350 | ... ) 351 | [ 352 | [ 353 | (, (b'Hello, world!\n', b''), 0.1) 354 | ], 355 | b'' 356 | ] 357 | >>> 358 | >>> judge(PHP(), b', (b'Hello, world!', b''), 0.05) 362 | ], 363 | b'No syntax errors detected in index.php\n' 364 | ] 365 | >>> 366 | >>> judge(Ruby(), b'print "Hello, world!";', [(b'', b'Hello, world!')]) # Ruby 367 | [ 368 | [ 369 | (, (b'Hello, world!', b''), 0.05) 370 | ], 371 | b'Syntax OK\n' 372 | ] 373 | >>> 374 | >>> judge( 375 | ... Mono(Mono.Language.csharp), # C# (Mono) 376 | ... b''' 377 | ... using System; 378 | ... 379 | ... public class HelloWorld 380 | ... { 381 | ... public static void Main(string[] args) 382 | ... { 383 | ... Console.WriteLine ("Hello Mono World"); 384 | ... } 385 | ... } 386 | ... ''', 387 | ... [ 388 | ... (b'', b'Hello Mono World') 389 | ... ] 390 | ... ) 391 | [ 392 | [ 393 | (, (b'Hello Mono World\n', b''), 0.02) 394 | ], 395 | b'Microsoft (R) Visual C# Compiler version 3.5.0-beta1-19606-04 (d2bd58c6)\n' 396 | b'Copyright (C) Microsoft Corporation. All rights reserved.\n' 397 | b'\n' 398 | ] 399 | >>> judge( 400 | ... Mono(Mono.Language.vb), # Visual Basic (Mono) 401 | ... b''' 402 | ... Imports System 403 | ... 404 | ... Module HelloWorld 405 | ... Sub Main() 406 | ... Console.WriteLine("Hello World!") 407 | ... End Sub 408 | ... End Module 409 | ... ''', 410 | ... [ 411 | ... (b'', b'Hello World!') 412 | ... ] 413 | ... ) 414 | [ 415 | [ 416 | (, (b'Hello World!\n', b''), 0.024) 417 | ], 418 | b'Visual Basic.Net Compiler version 0.0.0.5943 (Mono 4.7 - tarball)\n' 419 | b'Copyright (C) 2004-2010 Rolf Bjarne Kvinge. All rights reserved.\n' 420 | b'\n' 421 | b"Assembly 'mono, Version=0.0, Culture=neutral, PublicKeyToken=null' saved successfully to '/dockerjudge/0/mono.exe'.\r\n" 422 | b'Compilation successful\r\n' 423 | b'Compilation took 00:00:00.0000000\n' 424 | ] 425 | >>> 426 | >>> judge(Swift(), b'print("Hello, world!")', [(b'', b'Hello, world!')]) # Swift 427 | [ 428 | [ 429 | (, (b'Hello, world!\n', b''), 0.2) 430 | ], 431 | b'' 432 | ] 433 | ``` 434 | 435 | ## [许可协议](LICENSE) 436 | 437 | 以 [**Apache License 2.0**](https://www.apache.org/licenses/LICENSE-2.0) 进行授权 438 | Wide Apache Software Foundation Logo with Feather.svg 439 | -------------------------------------------------------------------------------- /test_dockerjudge.py: -------------------------------------------------------------------------------- 1 | "Test dockerjudge" 2 | # pylint: disable = C0103, C0115, C0116 3 | 4 | import unittest 5 | from time import time 6 | 7 | from dockerjudge import judge 8 | from dockerjudge.processor import ( 9 | GCC, 10 | PHP, 11 | Bash, 12 | Clang, 13 | Go, 14 | Mono, 15 | Node, 16 | OpenJDK, 17 | PyPy, 18 | Python, 19 | Ruby, 20 | Swift, 21 | ) 22 | from dockerjudge.status import Status 23 | 24 | 25 | class TestProcessor(unittest.TestCase): 26 | def test_GCC(self): 27 | self.assertEqual(GCC(GCC.Language.c, 4.8).image, "gcc:4.8") 28 | self.assertEqual(GCC(GCC.Language.c, "4.8").image, "gcc:4.8") 29 | self.assertEqual(GCC(GCC.Language.c).source, GCC("c").source) 30 | self.assertEqual(GCC(GCC.Language.c).source, GCC("C").source) 31 | self.assertEqual(GCC(GCC.Language.cpp).source, GCC("cpp").source) 32 | self.assertEqual(GCC(GCC.Language.cpp).source, GCC("C++").source) 33 | self.assertEqual(GCC(GCC.Language.cpp).source, GCC("c++").source) 34 | self.assertEqual(GCC(GCC.Language.go).source, GCC("go").source) 35 | self.assertEqual(GCC(GCC.Language.go).source, GCC("Go").source) 36 | self.assertEqual(GCC(GCC.Language.c).compile[0], "gcc") 37 | self.assertEqual(GCC(GCC.Language.cpp).compile[0], "g++") 38 | self.assertEqual(GCC(GCC.Language.go).compile[0], "gccgo") 39 | 40 | def test_Go(self): 41 | self.assertEqual(Go(1).image, Go("1").image) 42 | self.assertEqual(Go("1").image, "golang:1") 43 | self.assertEqual(Go(None, {"src": "golang.go"}).source, "golang.go") 44 | self.assertEqual(Go(None, {"bin": "golang"}).judge, "./golang") 45 | 46 | def test_PyPy(self): 47 | self.assertEqual(PyPy().compile, PyPy(3).compile) 48 | self.assertEqual(PyPy().judge, PyPy(3).judge) 49 | 50 | def test_mono(self): 51 | self.assertTrue(Mono(Mono.Language.csharp).source.endswith(".cs")) 52 | self.assertEqual(Mono().source, Mono(Mono.Language.csharp).source) 53 | 54 | 55 | class TestDockerJudge(unittest.TestCase): 56 | def test_judge(self): 57 | result = judge( 58 | GCC("c", "4.8"), 59 | b""" 60 | #include 61 | int main() { 62 | int a, b; 63 | scanf("%d %d", &a, &b); 64 | printf("%d", a / b); 65 | return 0; 66 | } 67 | """, 68 | [(b"1 1", b"1"), (b"1 2", b"0.5"), (b"0 0", b"")], 69 | ) 70 | self.assertEqual(result[0][0][0], Status.AC) 71 | self.assertEqual(result[0][1][0], Status.WA) 72 | self.assertEqual(result[0][2][0], Status.RE) 73 | self.assertTrue(result[0][2][1][1]) 74 | self.assertFalse(result[1]) 75 | 76 | def test_CE(self): 77 | result = judge( 78 | GCC("c", "4.8"), 79 | b""" 80 | #include 81 | int main() { 82 | int a, b; 83 | scanf("%d %d", &a, &b); 84 | printf("%d", a / b); 85 | return 0; 86 | } 87 | """, 88 | [(b"1 1", b"1"), (b"1 2", b"0.5")], 89 | ) 90 | self.assertEqual(result[0][0][0], Status.CE) 91 | self.assertEqual(result[0][1][0], Status.CE) 92 | self.assertTrue(result[1]) 93 | self.assertFalse(result[0][0][1][0]) 94 | self.assertFalse(result[0][0][1][1]) 95 | self.assertFalse(result[0][1][1][0]) 96 | self.assertFalse(result[0][1][1][1]) 97 | 98 | def test_TLE(self): 99 | result = judge( 100 | GCC("cpp", "4.8"), 101 | b""" 102 | #include 103 | int main() { 104 | int a, b; 105 | scanf("%d %d", &a, &b); 106 | printf("%d", a / b); 107 | while (true) 108 | ; 109 | } 110 | """, 111 | [(b"1 1", b"1"), (b"0 0", b"")], 112 | {"limit": {"time": 0.8}}, 113 | ) 114 | self.assertEqual(result[0][0][0], Status.TLE) 115 | self.assertEqual(result[0][1][0], Status.RE) 116 | self.assertAlmostEqual(result[0][0][2], 0.8, 0) 117 | self.assertLess(result[0][1][2], 0.8, 0) 118 | self.assertFalse(result[1]) 119 | 120 | def test_iofile(self): 121 | result = judge( 122 | GCC("c", "4.8"), 123 | b""" 124 | #include 125 | int main() { 126 | freopen("in.txt", "r", stdin); 127 | freopen("out.txt", "w", stdout); 128 | int a, b; 129 | scanf("%d %d", &a, &b); 130 | printf("%d", a / b); 131 | return 0; 132 | } 133 | """, 134 | [(b"1 1", b"1"), (b"1 2", b"0.5"), (b"0 0", b"")], 135 | {"iofilename": {"in": "in.txt", "out": "out.txt"}}, 136 | ) 137 | self.assertEqual(result[0][0][0], Status.AC) 138 | self.assertEqual(result[0][1][0], Status.WA) 139 | self.assertEqual(result[0][2][0], Status.RE) 140 | self.assertTrue(result[0][2][1][1]) 141 | self.assertFalse(result[1]) 142 | 143 | def test_ONF(self): 144 | result = judge( 145 | GCC("c", "4.8"), 146 | b""" 147 | #include 148 | int main() { 149 | int a, b; 150 | scanf("%d %d", &a, &b); 151 | printf("%d", a / b); 152 | return 0; 153 | } 154 | """, 155 | [(b"1 1", b"1"), (b"0 0", b"")], 156 | {"iofilename": {"out": "out.txt"}}, 157 | ) 158 | self.assertEqual(result[0][0][0], Status.ONF) 159 | self.assertEqual(result[0][1][0], Status.RE) 160 | self.assertTrue(result[0][1][1][1]) 161 | self.assertFalse(result[1]) 162 | 163 | def test_callback(self): 164 | def compiling_callback(code, stderr): 165 | self.assertFalse(code) 166 | self.assertFalse(stderr) 167 | 168 | def judging_callback( 169 | id, status, stderr, duration # pylint: disable = W0622 170 | ): # pylint: disable = W0613 171 | statuses = [Status.AC, Status.WA, Status.RE] 172 | self.assertEqual(status, statuses[id]) 173 | 174 | result = judge( 175 | GCC("c", "4.8"), 176 | b""" 177 | #include 178 | int main() { 179 | int a, b; 180 | scanf("%d %d", &a, &b); 181 | printf("%d", a / b); 182 | return 0; 183 | } 184 | """, 185 | [(b"1 1", b"1"), (b"1 2", b"0.5"), (b"0 0", b"")], 186 | { 187 | "callback": { 188 | "compile": compiling_callback, 189 | "judge": judging_callback, 190 | } 191 | }, 192 | ) 193 | self.assertEqual(result[0][0][0], Status.AC) 194 | self.assertEqual(result[0][1][0], Status.WA) 195 | self.assertEqual(result[0][2][0], Status.RE) 196 | self.assertFalse(result[1]) 197 | 198 | def test_threads(self): 199 | t = time() 200 | result = judge( 201 | GCC("cpp", "4.8"), 202 | b""" 203 | int main() { 204 | while (true) 205 | ; 206 | } 207 | """, 208 | [(b"", b"")] * 3, 209 | {"limit": {"time": 5}, "threads": 2}, 210 | ) 211 | self.assertEqual(result[0][0][0], Status.TLE) 212 | self.assertEqual(result[0][1][0], Status.TLE) 213 | self.assertEqual(result[0][2][0], Status.TLE) 214 | self.assertAlmostEqual(result[0][0][2], 5, 0) 215 | self.assertAlmostEqual(result[0][1][2], 5, 0) 216 | self.assertAlmostEqual(result[0][2][2], 5, 0) 217 | self.assertFalse(result[1]) 218 | self.assertGreater(time() - t, 10) 219 | 220 | 221 | class TestLlvmClang(unittest.TestCase): 222 | def test_llvm_clang_10(self): 223 | result = judge( 224 | Clang("c", 10), 225 | b""" 226 | #include 227 | int main() { 228 | int a, b; 229 | scanf("%d %d", &a, &b); 230 | printf("%d", a / b); 231 | return 0; 232 | } 233 | """, 234 | [(b"1 1", b"1"), (b"1 2", b"0.5"), (b"0 0", b"")], 235 | ) 236 | self.assertEqual(result[0][0][0], Status.AC) 237 | self.assertEqual(result[0][1][0], Status.WA) 238 | self.assertEqual(result[0][2][0], Status.RE) 239 | self.assertTrue(result[0][2][1][1]) 240 | self.assertFalse(result[1]) 241 | 242 | def test_llvm_clang_11(self): 243 | result = judge( 244 | Clang("c", 11), 245 | b""" 246 | #include 247 | int main() { 248 | int a, b; 249 | scanf("%d %d", &a, &b); 250 | printf("%d", a / b); 251 | return 0; 252 | } 253 | """, 254 | [(b"1 1", b"1"), (b"1 2", b"0.5"), (b"0 0", b"")], 255 | ) 256 | self.assertEqual(result[0][0][0], Status.AC) 257 | self.assertEqual(result[0][1][0], Status.WA) 258 | self.assertEqual(result[0][2][0], Status.RE) 259 | self.assertTrue(result[0][2][1][1]) 260 | self.assertFalse(result[1]) 261 | 262 | 263 | class TestPython(unittest.TestCase): 264 | def test_python2(self): 265 | result = judge( 266 | Python("2"), 267 | b"a, b = [int(i) for i in raw_input().split()]; print a / b", 268 | [(b"1 1", b"1"), (b"1 2", b"0.5"), (b"0 0", b"")], 269 | ) 270 | self.assertEqual(result[0][0][0], Status.AC) 271 | self.assertEqual(result[0][1][0], Status.WA) 272 | self.assertEqual(result[0][2][0], Status.RE) 273 | self.assertTrue(result[0][2][1][1]) 274 | 275 | def test_python3(self): 276 | result = judge( 277 | Python("3"), 278 | b"a, b = [int(i) for i in input().split()]; print(a / b)", 279 | [(b"1 1", b"1"), (b"1 2", b"0.5"), (b"0 0", b"")], 280 | ) 281 | self.assertEqual(result[0][0][0], Status.WA) 282 | self.assertEqual(result[0][1][0], Status.AC) 283 | self.assertEqual(result[0][2][0], Status.RE) 284 | self.assertTrue(result[0][2][1][1]) 285 | 286 | def test_CE(self): 287 | result = judge(Python("3"), b"import", [(b"", b""), (b"", b"")]) 288 | self.assertEqual(result[0][0][0], Status.CE) 289 | self.assertEqual(result[0][1][0], Status.CE) 290 | self.assertTrue(result[1]) 291 | self.assertFalse(result[0][0][1][0]) 292 | self.assertFalse(result[0][0][1][1]) 293 | self.assertFalse(result[0][1][1][0]) 294 | self.assertFalse(result[0][1][1][1]) 295 | 296 | def test_processor_tuple(self): 297 | result = judge( 298 | ("Python", ("3",)), 299 | b"print('Hello, world!')", 300 | [(b"", b"Hello, world!")], 301 | ) 302 | self.assertEqual(result[0][0][0], Status.AC) 303 | 304 | def test_processor_dict(self): 305 | result = judge( 306 | ("Python", {"version": "3"}), 307 | b"print('Hello, world!')", 308 | [(b"", b"Hello, world!")], 309 | ) 310 | self.assertEqual(result[0][0][0], Status.AC) 311 | 312 | def test_pypy(self): 313 | result = judge( 314 | PyPy(2), 315 | b"a, b = [int(i) for i in raw_input().split()]; print a / b", 316 | [(b"1 1", b"1"), (b"1 2", b"0.5"), (b"0 0", b"")], 317 | ) 318 | self.assertEqual(result[0][0][0], Status.AC) 319 | self.assertEqual(result[0][1][0], Status.WA) 320 | self.assertEqual(result[0][2][0], Status.RE) 321 | self.assertTrue(result[0][2][1][1]) 322 | 323 | def test_pypy3(self): 324 | result = judge( 325 | PyPy(3), 326 | b"a, b = [int(i) for i in input().split()]; print(a / b)", 327 | [(b"1 1", b"1"), (b"1 2", b"0.5"), (b"0 0", b"")], 328 | ) 329 | self.assertEqual(result[0][0][0], Status.WA) 330 | self.assertEqual(result[0][1][0], Status.AC) 331 | self.assertEqual(result[0][2][0], Status.RE) 332 | self.assertTrue(result[0][2][1][1]) 333 | 334 | 335 | class TestGoLang(unittest.TestCase): 336 | def test_go(self): 337 | result = judge( 338 | Go(1), 339 | br""" 340 | package main 341 | 342 | import "fmt" 343 | 344 | func main() { 345 | fmt.Printf("hello, world\n") 346 | } 347 | """, 348 | [(b"", b"hello, world")], 349 | ) 350 | self.assertEqual(result[0][0][0], Status.AC) 351 | 352 | def test_gccgo(self): 353 | result = judge( 354 | GCC("go", 4.9), 355 | b"package main\n" 356 | b'import "fmt"\n' 357 | b"func main() {\n" 358 | br' fmt.Printf("hello, world\n")' 359 | b"\n" 360 | b"}\n", 361 | [(b"", b"hello, world\n")], 362 | ) 363 | self.assertEqual(result[0][0][0], Status.AC) 364 | 365 | 366 | class TestJava(unittest.TestCase): 367 | def test_openjdk(self): 368 | result = judge( 369 | OpenJDK(), 370 | b""" 371 | public class Main { 372 | public static void main(String[] args) { 373 | System.out.println("Hello, world!"); 374 | } 375 | } 376 | """, 377 | [(b"", b"Hello, world!")], 378 | ) 379 | self.assertEqual(result[0][0][0], Status.AC) 380 | 381 | 382 | class TestNode(unittest.TestCase): 383 | def test_nodejs(self): 384 | result = judge( 385 | Node(12), b'console.log("Hello World")', [(b"", b"Hello World")] 386 | ) 387 | self.assertEqual(result[0][0][0], Status.AC) 388 | 389 | 390 | class TestShell(unittest.TestCase): 391 | def test_bash(self): 392 | result = judge( 393 | Bash(), b"echo Hello, world!", [(b"", b"Hello, world!")] 394 | ) 395 | self.assertEqual(result[0][0][0], Status.AC) 396 | 397 | 398 | class TestPHP(unittest.TestCase): 399 | def test_php(self): 400 | result = judge( 401 | PHP(), b' 2 |

3 | 4 | Piterator 5 |
6 | Made by Piterator with <3 7 |
8 |

9 | 10 | # ![dockerjudge](https://static.dockerjudge.piterator.com/dockerjudge.svg) 11 | 12 | [![Maintainability](https://api.codeclimate.com/v1/badges/dfe666a2140cd3390e56/maintainability)](https://codeclimate.com/github/wxh06/dockerjudge/maintainability) 13 | [![Python package](https://github.com/wxh06/dockerjudge/workflows/Python%20package/badge.svg)](https://github.com/wxh06/dockerjudge/actions?query=workflow%3A%22Python+package%22) 14 | [![Upload Python Package](https://github.com/wxh06/dockerjudge/workflows/Upload%20Python%20Package/badge.svg)](https://github.com/wxh06/dockerjudge/actions?query=workflow%3A%22Upload+Python+Package%22) 15 | [![Transifex](https://github.com/wxh06/dockerjudge/workflows/Transifex/badge.svg)](https://www.transifex.com/piterator/dockerjudge/) 16 | [![Build status](https://ci.appveyor.com/api/projects/status/qq5br83e4hp7utpo?svg=true)](https://ci.appveyor.com/project/t9n81yiq9d/dockerjudge) 17 | [![Build Status](https://dev.azure.com/wangxinhe/wangxinhe/_apis/build/status/wxh06.dockerjudge)](https://dev.azure.com/wangxinhe/wangxinhe/_build/latest?definitionId=1) 18 | [![Azure DevOps tests](https://img.shields.io/azure-devops/tests/wangxinhe/wangxinhe/1)](https://dev.azure.com/wangxinhe/wangxinhe/_test/analytics?definitionId=1) 19 | [![Build Status](https://travis-ci.com/wxh06/dockerjudge.svg)](https://travis-ci.com/wxh06/dockerjudge) 20 | [![CodeCov](https://codecov.io/gh/wxh06/dockerjudge/graph/badge.svg)](https://codecov.io/gh/wxh06/dockerjudge) 21 | [![Documentation Status](https://readthedocs.org/projects/dockerjudge/badge/)](https://dockerjudge.readthedocs.io/en/latest/) 22 | [![Python Version](https://img.shields.io/pypi/pyversions/dockerjudge.svg)](https://www.python.org/downloads/) 23 | [![GitHub pre-release](https://img.shields.io/github/release-pre/wxh06/dockerjudge.svg)](https://github.com/wxh06/dockerjudge/releases) 24 | [![PyPI](https://img.shields.io/pypi/v/dockerjudge.svg)](https://pypi.org/project/dockerjudge/#history) 25 | [![Wheel](https://img.shields.io/pypi/wheel/dockerjudge.svg)](https://pypi.org/project/dockerjudge/#files) 26 | [![License](https://img.shields.io/github/license/wxh06/dockerjudge.svg)](LICENSE) 27 | [![Code style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://black.readthedocs.io/) 28 | 29 | 🎌 **🇺🇸 English** | [🇨🇳 大陆简体](README.zh_Hans_CN.md) 30 | 31 | **A LXC based online judge engine**, which supports [10+ programming language processors](#supported-processors): 32 | 33 | - [Shell](https://en.wikipedia.org/wiki/Unix_shell) 34 | - [Bash (**B**ourne-**A**gain **sh**ell)](https://en.wikipedia.org/wiki/Bash_(Unix_shell)) 35 | - [C](https://en.wikipedia.org/wiki/C_(programming_language))/[C++](https://en.wikipedia.org/wiki/C%2B%2B) 36 | - [GCC (The **G**NU **C**ompiler **C**ollection)](https://gcc.gnu.org/) 37 | - [LLVM Clang](https://clang.llvm.org/) 38 | - [.NET](https://docs.microsoft.com/en-us/dotnet/) ([C#](https://docs.microsoft.com/en-us/dotnet/csharp/) & [Visual Basic](https://docs.microsoft.com/en-us/dotnet/visual-basic/)) 39 | - [Mono](https://www.mono-project.com/) 40 | - [Go](https://golang.org/) 41 | - [`go`](https://golang.org/dl/) 42 | - [`gccgo` (GCC)](https://golang.org/doc/install/gccgo) 43 | - [Java](https://www.oracle.com/java/) 44 | - [OpenJDK](https://openjdk.java.net/) 45 | - [Node.js](https://nodejs.org/en/) 46 | - [`node`](https://nodejs.org/en/download/) 47 | - [PHP](https://www.php.net/) 48 | - [`php`](https://www.php.net/downloads) 49 | - [Python](https://www.python.org/) 50 | - [CPython](https://www.python.org/downloads/) 51 | - [PyPy](https://www.pypy.org/) 52 | - [Ruby](https://www.ruby-lang.org/en/) 53 | - [`ruby`](https://www.ruby-lang.org/en/downloads/) 54 | - [Swift](https://swift.org/) 55 | - [`swiftc`](https://swift.org/swift-compiler/) 56 | 57 | ## Supported processors 58 | 59 | Read the [docs](https://dockerjudge.readthedocs.io/en/latest/processor.html#module-dockerjudge.processor) for more information. 60 | 61 | Processor | Language(s)\* | Required [Docker image](https://hub.docker.com/) 62 | --------- | ------------- | ------------------------------------------------ 63 | `Bash` | Shell | [`bash`](https://hub.docker.com/_/bash) 64 | `Clang` |
  • C (`c`)
  • **C++ (`cpp`)**
| [`clangbuiltlinux/ubuntu`](https://hub.docker.com/r/clangbuiltlinux/ubuntu) 65 | `GCC` |
  • C (`c`)
  • **C++ (`cpp`)**
  • Go (`go`)
| [`gcc`](https://hub.docker.com/_/gcc) 66 | `Go` | Go | [`golang`](https://hub.docker.com/_/golang) 67 | `Mono` |
  • Visual Basic (`vb`)
  • **C# (`csharp`)**
| [`mono`](https://hub.docker.com/_/mono) 68 | `Node` | Node.js | [`node`](https://hub.docker.com/_/node) 69 | `OpenJDK` | Java | [`openjdk`](https://hub.docker.com/_/openjdk) 70 | `PHP` | PHP | [`php`](https://hub.docker.com/_/php) 71 | `PyPy` | Python | [`pypy`](https://hub.docker.com/_/pypy) 72 | `Python`| Python | [`python`](https://hub.docker.com/_/python) 73 | `Ruby` | Ruby | [`ruby`](https://hub.docker.com/_/ruby) 74 | `Swift` | Swift | [`swift`](https://hub.docker.com/_/swift) 75 | 76 | **\*** The `language` argumnets of the processors which support multiple languages should be provided, or the `source` will be judged in the default language (usually C++ or C#, emboldened in the table). 77 | 78 | ## Installation 79 | 80 | Read the [docs](https://dockerjudge.readthedocs.io/en/latest/installation.html) for more information. 81 | 82 | ### Docker 83 | 84 | To run `dockerjudge`, [Docker Engine](https://www.docker.com/) is required. 85 | 86 | #### Install using the convenience script (for servers) 87 | 88 | ```sh 89 | curl -fsSL https://get.docker.com -o get-docker.sh 90 | sudo sh get-docker.sh 91 | ``` 92 | 93 | See [Install Docker Engine \| Docker Documentation](https://docs.docker.com/engine/install/) for more information. 94 | 95 | ### Package 96 | 97 | #### From the [Python Package Index (PyPI)](https://pypi.org/) 98 | 99 | [dockerjudge · PyPI](https://pypi.org/project/dockerjudge/) 100 | 101 | - [PyPI](https://pypi.org/simple/dockerjudge/) 102 | - [阿里巴巴开源镜像站 (Alibaba Open Source Mirror)](https://mirrors.aliyun.com/pypi/simple/dockerjudge/) 103 | - [清华大学开源软件镜像站 | Tsinghua Open Source Mirror](https://pypi.tuna.tsinghua.edu.cn/simple/dockerjudge/) 104 | 105 | ##### Via [pip](https://pip.pypa.io/) 106 | 107 | ```sh 108 | pip install dockerjudge 109 | ``` 110 | 111 | ##### Via [Easy install](https://setuptools.readthedocs.io/en/latest/easy_install.html) (deprecated) 112 | 113 | ```sh 114 | easy_install dockerjudge 115 | ``` 116 | 117 | #### From [GitHub](https://github.com/) 118 | 119 | [wxh06/dockerjudge: A Docker Based Online Judge Engine](https://github.com/wxh06/dockerjudge) 120 | 121 | - HTTPS: `https://github.com/wxh06/dockerjudge.git` 122 | - SSH: `git@github.com:wxh06/dockerjudge.git` 123 | 124 | ```sh 125 | git clone https://github.com/wxh06/dockerjudge.git 126 | cd dockerjudge 127 | 128 | make pip && make # python3 -m pip install -Ur requirements.txt && python3 setup.py build 129 | sudo make install # python3 setup.py install 130 | ``` 131 | 132 | ## Usage 133 | 134 | Read the [docs](https://dockerjudge.readthedocs.io/en/latest/__init__.html) for more information. 135 | 136 | ```python 137 | >>> from dockerjudge import judge 138 | >>> from dockerjudge.processor import GCC, Clang, Bash, Python, Node, OpenJDK, PHP, Ruby, Mono, Swift 139 | >>> 140 | >>> judge( 141 | ... GCC(GCC.Language.c), # or `GCC('c')` / `GCC('C')`, which means compile the source code in the C programming language with `gcc` command 142 | ... b''' 143 | ... #include 144 | ... int main() { 145 | ... int a, b; 146 | ... scanf("%d %d", &a, &b); 147 | ... printf("%d", a / b); 148 | ... return 0; 149 | ... } 150 | ... ''', 151 | ... [ 152 | ... (b'1 1', b'1'), # AC 153 | ... (b'1 2', b'0.5'), # WA 154 | ... (b'0 0', b'') # RE 155 | ... ] 156 | ... ) 157 | [ 158 | [ 159 | (, (b'1', b''), 0.001), 160 | (, (b'0', b''), 0.001), 161 | (, (None, b'Floating point exception (core dumped)\n'), 0.01) 162 | ], 163 | b'' 164 | ] 165 | >>> 166 | >>> judge(GCC(GCC.Language.c), b'', [(b'', b'')]) # CE 167 | [ 168 | [ 169 | (, (None, None), 0.0) 170 | ], 171 | b"/usr/bin/ld: /usr/lib/x86_64-linux-gnu/crt1.o: in function `_start':\n(.text+0x20): undefined reference to `main'\ncollect2: error: ld returned 1 exit status\n" 172 | ] 173 | >>> 174 | >>> judge( 175 | ... GCC(GCC.Language.cpp), # or `GCC('cpp')` / `GCC('C++')`, which means compile the source code in the C++ programming language with `g++` command 176 | ... b''' 177 | ... #include 178 | ... int main() { 179 | ... printf("Hello, world!"); 180 | ... while (true) 181 | ... ; 182 | ... } 183 | ... ''', 184 | ... [ 185 | ... (b'', b'Hello, world!') # TLE 186 | ... ], 187 | ... { 188 | ... 'limit': { 189 | ... 'time': .1 190 | ... } 191 | ... } 192 | ... ) 193 | [ 194 | [ 195 | (, (None, b'bash: line 1: 35 Killed timeout -sKILL 0.1 sh -c ./a.out > /dockerjudge/1.out < /dockerjudge/1.in\n'), 0.100) 196 | ], 197 | b'' 198 | ] 199 | >>> 200 | >>> judge( 201 | ... GCC( 202 | ... GCC.Language.c, 203 | ... 'latest', # The GCC version number, such as `4`, `4.8`, etc. 204 | ... {'bin': 'a'} # The binary filename, which passes to `gcc`'s `-o` option 205 | ... ), 206 | ... b''' 207 | ... #include 208 | ... int main() { 209 | ... int a, b; 210 | ... freopen("a.in", "r", stdin); // Open `a.in` as stdin 211 | ... scanf("%d %d", &a, &b); // Scan from `a.in` 212 | ... freopen("a.out", "w", stdout); // Open `a.out` as stdout 213 | ... printf("%d", a / b); // Print to `a.out` 214 | ... return 0; 215 | ... } 216 | ... ''', 217 | ... [ 218 | ... (b'1 1', b'1'), # AC 219 | ... (b'1 2', b'0.5'), # WA 220 | ... (b'0 0', b'') # RE 221 | ... ], 222 | ... { 223 | ... 'iofilename': { 224 | ... 'in': 'a.in', 225 | ... 'out': 'a.out' 226 | ... } 227 | ... } 228 | ... ) 229 | [ 230 | [ 231 | (, (b'1', b''), 0.001), 232 | (, (b'0', b''), 0.001), 233 | (, (None, b'Floating point exception (core dumped)\n'), 0.001) 234 | ], 235 | b'' 236 | ] 237 | >>> 238 | >>> judge( 239 | ... GCC(GCC.Language.c, filenames={'bin': 'a'}), 240 | ... b''' 241 | ... #include 242 | ... int main() { 243 | ... int a, b; 244 | ... scanf("%d %d", &a, &b); 245 | ... printf("%d", a / b); 246 | ... return 0; 247 | ... } 248 | ... ''', 249 | ... [ 250 | ... (b'1 1', b'1'), 251 | ... (b'0 0', b'') 252 | ... ], 253 | ... { 254 | ... 'iofilename': { 255 | ... 'out': 'a.out' # ONF 256 | ... } 257 | ... } 258 | ... ) 259 | [ 260 | [ 261 | (, (None, b''), 0.001), 262 | (, (None, b'Floating point exception (core dumped)\n'), 0.001) 263 | ], 264 | b'' 265 | ] 266 | >>> 267 | >>> judge( # BTW, GCC starting from 4.9 also supports Go, named `gccgo` 268 | ... GCC(GCC.Language.go), 269 | ... b'package main\n' 270 | ... b'' 271 | ... b'import "fmt"\n' 272 | ... b'' 273 | ... b'func main() {\n' 274 | ... br' fmt.Printf("hello, world\n")'b'\n' 275 | ... b'}\n', 276 | ... [(b'', b'hello, world')] 277 | ... ) 278 | [ 279 | [ 280 | (, (b'hello, world\n', b''), 0.02) 281 | ], 282 | b'' 283 | ] 284 | >>> 285 | >>> judge( 286 | ... Clang( # Besides GCC, LLVM Clang is also supported (The same arguments as GCC's) 287 | ... Clang.Language.c, # Only C and C++ supported 288 | ... 11 # The version number of LLVM CLang is **required**! 289 | ... ), 290 | ... b'', # CE 291 | ... [ 292 | ... (b'', b'') 293 | ... ] 294 | ... ) 295 | [ 296 | [ 297 | (, (None, None), 0.0) 298 | ], 299 | b"/usr/bin/ld: /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crt1.o: in function `_start':\n' 300 | b"(.text+0x24): undefined reference to `main'\n" 301 | b'clang: error: linker command failed with exit code 1 (use -v to see invocation)\n' 302 | ] 303 | >>> 304 | >>> # Other programming languages are also supported 305 | >>> judge(Bash(), b'echo Hello, world!', [(b'', b'Hello, world!')]) # Bash 306 | [ 307 | [ 308 | (, (b'Hello, world!\n', b''), 0.001) 309 | ], 310 | b'' 311 | ] 312 | >>> 313 | >>> judge(Python(3), b"print('Hello, world!')", [(b'', b'Hello, world!')]) # Python 3 314 | [ 315 | [ 316 | (, (b'Hello, world!\n', b''), 0.05) 317 | ], 318 | b"Listing '.'...\n" 319 | b"Compiling './__init__.py'...\n" 320 | ] 321 | >>> judge(PyPy(), b"print('Hello, world!')", [(b'', b'Hello, world!')]) # PyPy 3 322 | [ 323 | [ 324 | (, (b'Hello, world!\n', b''), 0.075) 325 | ], 326 | b"Listing '.'...\n" 327 | b"Compiling './__init__.py'...\n" 328 | ] 329 | >>> 330 | >>> judge(Node(12), b'console.log("Hello World")', [(b'', b'Hello World')]) # Node.js 331 | [ 332 | [ 333 | (, (b'Hello World\n', b''), 0.05) 334 | ], 335 | b'' 336 | ] 337 | >>> 338 | >>> judge( # Java / OpenJDK 339 | ... OpenJDK(), # The default public class name is `Main` 340 | ... b''' 341 | ... public class Main { 342 | ... public static void main(String[] args) { 343 | ... System.out.println("Hello, world!"); 344 | ... } 345 | ... } 346 | ... ''', 347 | ... [ 348 | ... (b'', b'Hello, world!') 349 | ... ] 350 | ... ) 351 | [ 352 | [ 353 | (, (b'Hello, world!\n', b''), 0.1) 354 | ], 355 | b'' 356 | ] 357 | >>> 358 | >>> judge(PHP(), b', (b'Hello, world!', b''), 0.05) 362 | ], 363 | b'No syntax errors detected in index.php\n' 364 | ] 365 | >>> 366 | >>> judge(Ruby(), b'print "Hello, world!";', [(b'', b'Hello, world!')]) # Ruby 367 | [ 368 | [ 369 | (, (b'Hello, world!', b''), 0.05) 370 | ], 371 | b'Syntax OK\n' 372 | ] 373 | >>> 374 | >>> judge( 375 | ... Mono(Mono.Language.csharp), # C# (Mono) 376 | ... b''' 377 | ... using System; 378 | ... 379 | ... public class HelloWorld 380 | ... { 381 | ... public static void Main(string[] args) 382 | ... { 383 | ... Console.WriteLine ("Hello Mono World"); 384 | ... } 385 | ... } 386 | ... ''', 387 | ... [ 388 | ... (b'', b'Hello Mono World') 389 | ... ] 390 | ... ) 391 | [ 392 | [ 393 | (, (b'Hello Mono World\n', b''), 0.02) 394 | ], 395 | b'Microsoft (R) Visual C# Compiler version 3.5.0-beta1-19606-04 (d2bd58c6)\n' 396 | b'Copyright (C) Microsoft Corporation. All rights reserved.\n' 397 | b'\n' 398 | ] 399 | >>> judge( 400 | ... Mono(Mono.Language.vb), # Visual Basic (Mono) 401 | ... b''' 402 | ... Imports System 403 | ... 404 | ... Module HelloWorld 405 | ... Sub Main() 406 | ... Console.WriteLine("Hello World!") 407 | ... End Sub 408 | ... End Module 409 | ... ''', 410 | ... [ 411 | ... (b'', b'Hello World!') 412 | ... ] 413 | ... ) 414 | [ 415 | [ 416 | (, (b'Hello World!\n', b''), 0.024) 417 | ], 418 | b'Visual Basic.Net Compiler version 0.0.0.5943 (Mono 4.7 - tarball)\n' 419 | b'Copyright (C) 2004-2010 Rolf Bjarne Kvinge. All rights reserved.\n' 420 | b'\n' 421 | b"Assembly 'mono, Version=0.0, Culture=neutral, PublicKeyToken=null' saved successfully to '/dockerjudge/0/mono.exe'.\r\n" 422 | b'Compilation successful\r\n' 423 | b'Compilation took 00:00:00.0000000\n' 424 | ] 425 | >>> 426 | >>> judge(Swift(), b'print("Hello, world!")', [(b'', b'Hello, world!')]) # Swift 427 | [ 428 | [ 429 | (, (b'Hello, world!\n', b''), 0.2) 430 | ], 431 | b'' 432 | ] 433 | ``` 434 | 435 | ## [License](LICENSE) 436 | 437 | Licensed under [the **Apache License, Version 2.0**](https://www.apache.org/licenses/LICENSE-2.0) 438 | Wide Apache Software Foundation Logo with Feather.svg 439 | --------------------------------------------------------------------------------