@@ -17,6 +17,7 @@ import requests.utils
17
17
import stat
18
18
import sys
19
19
import time
20
+ from typing import NoReturn
20
21
try :
21
22
import magic
22
23
except ModuleNotFoundError :
@@ -40,12 +41,14 @@ num_warnings = 0
40
41
41
42
headers = {'user-agent' : f'domjudge-submit-client ({ requests .utils .default_user_agent ()} )' }
42
43
44
+
43
45
def confirm (message : str ) -> bool :
44
46
answer = ''
45
47
while answer not in ['y' , 'n' ]:
46
48
answer = input (f'{ message } (y/n) ' ).lower ()
47
49
return answer == 'y'
48
50
51
+
49
52
def warn_user (msg : str ):
50
53
global num_warnings
51
54
num_warnings += 1
@@ -54,18 +57,21 @@ def warn_user(msg: str):
54
57
else :
55
58
print (f'WARNING: { msg } !' )
56
59
57
- def usage (msg : str ):
60
+
61
+ def usage (msg : str ) -> NoReturn :
58
62
logging .error (f'error: { msg } ' )
59
63
print (f"Type '{ sys .argv [0 ]} --help' to get help." )
60
64
exit (1 )
61
65
62
- def error (msg : str ):
66
+
67
+ def error (msg : str ) -> NoReturn :
63
68
logging .error (msg )
64
69
exit (- 1 )
65
70
71
+
66
72
def read_contests () -> list :
67
73
'''Read all contests from the API.
68
-
74
+
69
75
Returns:
70
76
The contests or None if an error occurred.
71
77
'''
@@ -74,28 +80,29 @@ def read_contests() -> list:
74
80
data = do_api_request ('contests' )
75
81
except RuntimeError as e :
76
82
logging .warning (e )
77
- return None
83
+ return []
78
84
79
85
if not isinstance (data , list ):
80
86
logging .warning ("DOMjudge's API returned unexpected JSON data for endpoint 'contests'." )
81
- return None
87
+ return []
82
88
83
89
contests = []
84
90
for contest in data :
85
91
if ('id' not in contest
86
- or 'shortname' not in contest
87
- or not contest ['id' ]
88
- or not contest ['shortname' ]):
92
+ or 'shortname' not in contest
93
+ or not contest ['id' ]
94
+ or not contest ['shortname' ]):
89
95
logging .warning ("DOMjudge's API returned unexpected JSON data for 'contests'." )
90
- return None
96
+ return []
91
97
contests .append (contest )
92
98
93
99
logging .info (f'Read { len (contests )} contest(s) from the API.' )
94
100
return contests
95
101
102
+
96
103
def read_languages () -> list :
97
104
'''Read all languages for the current contest from the API.
98
-
105
+
99
106
Returns:
100
107
The languages or None if an error occurred.
101
108
'''
@@ -105,22 +112,22 @@ def read_languages() -> list:
105
112
data = do_api_request (endpoint )
106
113
except RuntimeError as e :
107
114
logging .warning (e )
108
- return None
115
+ return []
109
116
110
117
if not isinstance (data , list ):
111
118
logging .warning ("DOMjudge's API returned unexpected JSON data for endpoint 'languages'." )
112
- return None
119
+ return []
113
120
114
121
languages = []
115
122
116
123
for item in data :
117
124
if ('id' not in item
118
- or 'extensions' not in item
119
- or not item ['id' ]
120
- or not isinstance (item ['extensions' ], list )
121
- or len (item ['extensions' ]) == 0 ):
125
+ or 'extensions' not in item
126
+ or not item ['id' ]
127
+ or not isinstance (item ['extensions' ], list )
128
+ or len (item ['extensions' ]) == 0 ):
122
129
logging .warning ("DOMjudge's API returned unexpected JSON data for 'languages'." )
123
- return None
130
+ return []
124
131
language = {
125
132
'id' : item ['id' ],
126
133
'name' : item ['name' ],
@@ -134,9 +141,10 @@ def read_languages() -> list:
134
141
135
142
return languages
136
143
144
+
137
145
def read_problems () -> list :
138
146
'''Read all problems for the current contest from the API.
139
-
147
+
140
148
Returns:
141
149
The problems or None if an error occurred.
142
150
'''
@@ -146,30 +154,31 @@ def read_problems() -> list:
146
154
data = do_api_request (endpoint )
147
155
except RuntimeError as e :
148
156
logging .warning (e )
149
- return None
157
+ return []
150
158
151
159
if not isinstance (data , list ):
152
160
logging .warning ("DOMjudge's API returned unexpected JSON data for endpoint 'problems'." )
153
- return None
161
+ return []
154
162
155
163
problems = []
156
164
157
165
for problem in data :
158
166
if ('id' not in problem
159
- or 'label' not in problem
160
- or not problem ['id' ]
161
- or not problem ['label' ]):
167
+ or 'label' not in problem
168
+ or not problem ['id' ]
169
+ or not problem ['label' ]):
162
170
logging .warning ("DOMjudge's API returned unexpected JSON data for 'problems'." )
163
- return None
171
+ return []
164
172
problems .append (problem )
165
173
166
174
logging .info (f'Read { len (problems )} problem(s) from the API.' )
167
175
168
176
return problems
169
177
178
+
170
179
def do_api_request (name : str ):
171
180
'''Perform an API call to the given endpoint and return its data.
172
-
181
+
173
182
Parameters:
174
183
name (str): the endpoint to call
175
184
@@ -184,7 +193,7 @@ def do_api_request(name: str):
184
193
raise RuntimeError ('No baseurl set' )
185
194
186
195
url = f'{ baseurl } api/{ api_version } { name } '
187
-
196
+
188
197
logging .info (f'Connecting to { url } ' )
189
198
190
199
try :
@@ -203,6 +212,7 @@ def do_api_request(name: str):
203
212
204
213
return json .loads (response .text )
205
214
215
+
206
216
def kotlin_base_entry_point (filebase : str ) -> str :
207
217
if filebase == "" :
208
218
return "_"
@@ -219,6 +229,7 @@ def kotlin_base_entry_point(filebase: str) -> str:
219
229
220
230
return filebase
221
231
232
+
222
233
def get_epilog ():
223
234
'''Get the epilog for the help text.'''
224
235
@@ -248,7 +259,7 @@ drop-down box in the web interface.'''
248
259
language_part = 'For LANGUAGE use the ID or a common extension.'
249
260
else :
250
261
language_part = 'For LANGUAGE use one of the following IDs or extensions:'
251
- max_length = max ([len (l ['name' ]) for l in languages ])
262
+ max_length = max ([len (L ['name' ]) for L in languages ])
252
263
for language in languages :
253
264
sorted_exts = ', ' .join (sorted (language ['extensions' ]))
254
265
language_part += f"\n { language ['name' ]:<{max_length }} - { sorted_exts } "
@@ -287,6 +298,7 @@ Submit multiple files (the problem and language are taken from the first):
287
298
288
299
return "\n \n " .join (part for part in epilog_parts if part )
289
300
301
+
290
302
def do_api_submit ():
291
303
'''Submit to the API with the given data.'''
292
304
@@ -323,15 +335,16 @@ def do_api_submit():
323
335
error (f'Parsing DOMjudge\' s API output failed: { e } ' )
324
336
325
337
if (not isinstance (submission , dict )
326
- or not 'id' in submission
327
- or not isinstance (submission ['id' ], str )):
338
+ or 'id' not in submission
339
+ or not isinstance (submission ['id' ], str )):
328
340
error ('DOMjudge\' s API returned unexpected JSON data.' )
329
341
330
342
time = datetime .datetime .fromisoformat (submission ['time' ]).strftime ('%H:%M:%S' )
331
343
sid = submission ['id' ]
332
344
print (f"Submission received: id = s{ sid } , time = { time } " )
333
345
print (f"Check { baseurl } team/submission/{ sid } for the result." )
334
346
347
+
335
348
version_text = '''
336
349
submit -- part of DOMjudge
337
350
Written by the DOMjudge developers
@@ -364,7 +377,8 @@ parser.add_argument('-c', '--contest', help='''submit for contest with ID or sho
364
377
parser .add_argument ('-p' , '--problem' , help = 'submit for problem with ID or label PROBLEM' , default = '' )
365
378
parser .add_argument ('-l' , '--language' , help = 'submit in language with ID LANGUAGE' , default = '' )
366
379
parser .add_argument ('-e' , '--entry_point' , help = 'set an explicit entry_point, e.g. the java main class' )
367
- parser .add_argument ('-v' , '--verbose' , help = 'increase verbosity' , choices = loglevels .keys (), nargs = '?' , const = 'INFO' , default = 'WARNING' )
380
+ parser .add_argument ('-v' , '--verbose' ,
381
+ help = 'increase verbosity' , choices = loglevels .keys (), nargs = '?' , const = 'INFO' , default = 'WARNING' )
368
382
parser .add_argument ('-q' , '--quiet' , help = 'suppress warning/info messages' , action = 'store_true' )
369
383
parser .add_argument ('-y' , '--assume-yes' , help = 'suppress user input and assume yes' , action = 'store_true' )
370
384
parser .add_argument ('-u' , '--url' , help = '''submit to server with base address URL
@@ -377,7 +391,7 @@ verbosity = args.verbose
377
391
if args .quiet :
378
392
verbosity = 'ERROR'
379
393
380
- logging .basicConfig (format = f '%(message)s' , level = loglevels [verbosity ])
394
+ logging .basicConfig (format = '%(message)s' , level = loglevels [verbosity ])
381
395
logging .info (f'set verbosity to { verbosity } ' )
382
396
383
397
problem_id = args .problem
@@ -403,9 +417,9 @@ contests = read_contests() if baseurl else None
403
417
if not contests and not args .help :
404
418
logging .warning ('Could not obtain active contests.' )
405
419
406
- my_contest = None
407
- my_language = None
408
- my_problem = None
420
+ my_contest : dict = {}
421
+ my_language : dict = {}
422
+ my_problem : dict = {}
409
423
410
424
if not contest_id :
411
425
if not contests :
@@ -423,8 +437,8 @@ elif contests:
423
437
my_contest = contest
424
438
break
425
439
426
- languages = None
427
- problems = None
440
+ languages : list = []
441
+ problems : list = []
428
442
if my_contest and baseurl :
429
443
if 'allow_submit' in my_contest and not my_contest ['allow_submit' ]:
430
444
warn_user ('Submissions for contest (temporarily) disabled' )
@@ -470,7 +484,7 @@ for index, filename in enumerate(args.filename, 1):
470
484
st = os .stat (filename )
471
485
except FileNotFoundError :
472
486
usage (f"Cannot find file `{ filename } '." )
473
-
487
+
474
488
logging .debug (f"submission file { index } : `{ filename } '" )
475
489
476
490
# Do some checks on submission file and warn user
@@ -496,6 +510,7 @@ for index, filename in enumerate(args.filename, 1):
496
510
497
511
filebase = os .path .basename (filenames [0 ])
498
512
513
+ ext = ""
499
514
if '.' in filebase :
500
515
dot = filebase .rfind ('.' )
501
516
ext = filebase [dot + 1 :]
@@ -527,7 +542,7 @@ for problem in problems:
527
542
break
528
543
529
544
if not my_problem :
530
- usage ('No known problem specified or detected.' )
545
+ usage ('No known problem specified or detected.' )
531
546
532
547
# Guess entry point if not already specified.
533
548
if not entry_point and my_language ['entry_point_required' ]:
0 commit comments