Skip to content
This repository has been archived by the owner on Jul 22, 2022. It is now read-only.

Commit

Permalink
Merge pull request #12 from wangxinhe2006/dev
Browse files Browse the repository at this point in the history
Bump version number to v0.7
  • Loading branch information
wxh06 authored Dec 14, 2019
2 parents 92ee37d + e4e2e70 commit a9fbd8a
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 41 deletions.
50 changes: 30 additions & 20 deletions dockerjudge/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,40 +9,46 @@
import docker
import ruamel.yaml

__version__ = '0.6.1'
__version__ = '0.7'


class Thread(threading.Thread):
'Subclass of threading.Thread with return value'
return_value = 'UE' # Unknown Error

def __init__(self, callback, *args, **kwargs):
self.return_value = 'UE' # Unknown Error
self._callback = callback
super().__init__(*args, **kwargs)

def run(self):
try:
if self._target:
self.return_value = self._target(*self._args, **self._kwargs)
if self._callback:
self._callback(self._args[0], *self.return_value)
finally:
del self._target, self._args, self._kwargs


def _judge(dir, container, commands, stdio, timeout, iofn):
def _judge(dir, container, commands, ioput, timeout, iofile) -> (str, float):
'Run each test case'
container.exec_run(['bash', '-c', 'mkdir {}'.format(dir)])
if commands[0]:
container.exec_run(['bash', '-c',
'cd {}&&{}'.format(dir, commands[0].format('..'))])
if iofn[0]:
if iofile[0]:
container.exec_run(['bash', '-c', 'echo {}>'
'{}/{}'.format(shlex.quote(stdio[0]),
dir, iofn[0])])
'{}/{}'.format(shlex.quote(ioput[0]),
dir, iofile[0])])
result = container.exec_run(['bash', '-c', 'cd {}&&time timeout -s '
'KILL {} {}<{}'.format(dir, timeout,
commands[1] % '..',
iofn[0])],
iofile[0])],
demux=True)
else:
result = container.exec_run(['bash', '-c', 'cd {}&&time echo {}|'
'timeout -s KILL {} '
'{}'.format(dir, shlex.quote(stdio[0]),
'{}'.format(dir, shlex.quote(ioput[0]),
timeout, commands[1] % '..')],
demux=True)
if commands[2]:
Expand All @@ -57,45 +63,49 @@ def _judge(dir, container, commands, stdio, timeout, iofn):
return ('TLE', duration) # Time Limit Exceeded
if result.exit_code:
return ('RE', duration) # Runtime Error
if iofn[1]:
if iofile[1]:
cat = container.exec_run(['bash', '-c',
'cat {}/{}'.format(dir, iofn[1])],
'cat {}/{}'.format(dir, iofile[1])],
demux=True)
if cat.exit_code:
return ('ONF', duration) # Output Not Found
output = cat.output[0]
else:
output = result.output[0]
if (output or b'').decode().rstrip() != stdio[1].rstrip():
if (output or b'').decode().rstrip() != ioput[1].rstrip():
return ('WA', duration) # Wrong Answer
return ('AC', duration) # Accepted


def judge(settings, source='', tests=[], timeout=1, iofn=(None, None),
client=docker.from_env()):
def judge(settings, source='', tests=[], timeout=1, iofile=(None, None),
callback={}, client=docker.from_env()):
'Main judge function'
tests = list(tests)
container = client.containers.run(settings['image'], detach=True,
network_disabled=True, tty=True)
try:
container.exec_run(['bash', '-c', 'echo {} > {}'.format(
shlex.quote(source), settings['source'])])
if 'before_compile' in settings:
container.exec_run(settings['before_compile'])
if 'before_compiling' in settings:
container.exec_run(settings['before_compiling'])
compiler = container.exec_run(settings['compile'], demux=True)
if 'after_compile' in settings:
container.exec_run(settings['after_compile'])
if 'after_compiling' in settings:
container.exec_run(settings['after_compiling'])
if callback.get('compiling'):
callback['compiling'](compiler.exit_code,
(compiler.output[1] or b'').decode())
if compiler.exit_code:
result = [('CE', .0) for test in tests]
else:
threads = []
for i in range(len(tests)):
thread = Thread(target=_judge,
args=(i, container,
(settings.get('before_judge'),
(settings.get('before_judging'),
settings['judge'],
settings.get('after_judge')),
tests[i], timeout, iofn))
settings.get('after_judging')),
tests[i], timeout, iofile),
callback=callback.get('judging'))
thread.start()
threads.append(thread)
result = []
Expand Down
68 changes: 47 additions & 21 deletions tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,14 @@ def test_ce(self):
'source': 'a.cpp',
'compile': 'gcc a.cpp',
'judge': '%s/a.out'},
r'#include <iostream>''\n'
r'int main() {''\n'
r' long long a, b;''\n'
r' std::cin >> a >> b;''\n'
r' std::cout << a + b;''\n'
r' return 0;''\n'
r'}''\n',
[('1 1', '1'),
('1 2', '0.5'),
('1 0', '')])
[('1 2', '3')])
self.assertEqual(result[0][0][0], 'CE')
self.assertTrue(result[1])

Expand All @@ -52,50 +51,49 @@ def test_tle(self):
r' while (true)''\n'
r' ;''\n'
r'}''\n',
[('1 1', '1'),
('1 2', '0.5'),
('1 0', '')])
[('', '')],
0.05)
self.assertEqual(result[0][0][0], 'TLE')
self.assertAlmostEqual(result[0][0][1], 0.05, 1)
self.assertFalse(result[1])

def test_before_judge(self):
def test_before_judging(self):
result = judge({'image': 'gcc:4.8',
'source': 'a.c',
'compile': 'gcc a.c',
'before_judge': 'rm {}/a.out',
'before_judging': 'rm {}/a.out',
'judge': '%s/a.out'},
r'#include <stdio.h>''\n'
r'int main() {''\n'
r' long long a, b;''\n'
r' scanf("%lld %lld", &a, &b);''\n'
r' printf("%d\n", a / b);''\n'
r' printf("%d\n", a + b);''\n'
r' return 0;''\n'
r'}''\n',
[('1 1', '1')])
[('1 2', '3')])
self.assertEqual(result[0][0][0], 'RE')
self.assertFalse(result[1])

def test_after_judge(self):
def test_after_judging(self):
result = judge({'image': 'gcc:4.8',
'source': 'a.c',
'compile': 'gcc a.c',
'judge': '%s/a.out',
'after_judge': 'rm a.out'},
'after_judging': 'rm a.out'},
r'#include <stdio.h>''\n'
r'int main() {''\n'
r' freopen("a.out", "w", stdout);''\n'
r' long long a, b;''\n'
r' scanf("%lld %lld", &a, &b);''\n'
r' printf("%d\n", a / b);''\n'
r' printf("%d\n", a + b);''\n'
r' return 0;''\n'
r'}''\n',
[('1 1', '1')],
1,
(None, 'a.out'))
[('1 2', '3')],
iofile=(None, 'a.out'))
self.assertEqual(result[0][0][0], 'ONF')
self.assertFalse(result[1])

def test_io(self):
def test_iofile(self):
result = judge({'image': 'gcc:4.8',
'source': 'a.c',
'compile': 'gcc a.c',
Expand All @@ -112,8 +110,7 @@ def test_io(self):
[('1 1', '1'),
('1 2', '0.5'),
('1 0', '')],
1,
('a.in', 'a.out'))
iofile=('a.in', 'a.out'))
self.assertEqual(result[0][0][0], 'AC')
self.assertEqual(result[0][1][0], 'WA')
self.assertEqual(result[0][2][0], 'RE')
Expand All @@ -134,13 +131,42 @@ def test_onf(self):
[('1 1', '1'),
('1 2', '0.5'),
('1 0', '')],
1,
(None, 'a.out'))
iofile=(None, 'a.out'))
self.assertEqual(result[0][0][0], 'ONF')
self.assertEqual(result[0][1][0], 'ONF')
self.assertEqual(result[0][2][0], 'RE')
self.assertFalse(result[1])

def test_callback(self):
def compiling_callback(code, err):
self.assertFalse(code)
self.assertFalse(err)

def judging_callback(id, status, duration):
statuses = ['AC', 'WA', 'RE']
self.assertEqual(status, statuses[id])

result = judge({'image': 'gcc:4.8',
'source': 'a.c',
'compile': 'gcc a.c',
'judge': '%s/a.out'},
r'#include <stdio.h>''\n'
r'int main() {''\n'
r' long long a, b;''\n'
r' scanf("%lld %lld", &a, &b);''\n'
r' printf("%d\n", a / b);''\n'
r' return 0;''\n'
r'}''\n',
[('1 1', '1'),
('1 2', '0.5'),
('1 0', '')],
callback={'compiling': compiling_callback,
'judging': judging_callback})
self.assertEqual(result[0][0][0], 'AC')
self.assertEqual(result[0][1][0], 'WA')
self.assertEqual(result[0][2][0], 'RE')
self.assertFalse(result[1])


if __name__ == '__main__':
unittest.main()

0 comments on commit a9fbd8a

Please sign in to comment.