@@ -17,6 +17,7 @@ import requests.utils
1717import stat
1818import sys
1919import time
20+ from typing import NoReturn
2021try :
2122 import magic
2223except ModuleNotFoundError :
@@ -40,12 +41,14 @@ num_warnings = 0
4041
4142headers = {'user-agent' : f'domjudge-submit-client ({ requests .utils .default_user_agent ()} )' }
4243
44+
4345def confirm (message : str ) -> bool :
4446 answer = ''
4547 while answer not in ['y' , 'n' ]:
4648 answer = input (f'{ message } (y/n) ' ).lower ()
4749 return answer == 'y'
4850
51+
4952def warn_user (msg : str ):
5053 global num_warnings
5154 num_warnings += 1
@@ -54,18 +57,21 @@ def warn_user(msg: str):
5457 else :
5558 print (f'WARNING: { msg } !' )
5659
57- def usage (msg : str ):
60+
61+ def usage (msg : str ) -> NoReturn :
5862 logging .error (f'error: { msg } ' )
5963 print (f"Type '{ sys .argv [0 ]} --help' to get help." )
6064 exit (1 )
6165
62- def error (msg : str ):
66+
67+ def error (msg : str ) -> NoReturn :
6368 logging .error (msg )
6469 exit (- 1 )
6570
71+
6672def read_contests () -> list :
6773 '''Read all contests from the API.
68-
74+
6975 Returns:
7076 The contests or None if an error occurred.
7177 '''
@@ -74,28 +80,29 @@ def read_contests() -> list:
7480 data = do_api_request ('contests' )
7581 except RuntimeError as e :
7682 logging .warning (e )
77- return None
83+ return []
7884
7985 if not isinstance (data , list ):
8086 logging .warning ("DOMjudge's API returned unexpected JSON data for endpoint 'contests'." )
81- return None
87+ return []
8288
8389 contests = []
8490 for contest in data :
8591 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' ]):
8995 logging .warning ("DOMjudge's API returned unexpected JSON data for 'contests'." )
90- return None
96+ return []
9197 contests .append (contest )
9298
9399 logging .info (f'Read { len (contests )} contest(s) from the API.' )
94100 return contests
95101
102+
96103def read_languages () -> list :
97104 '''Read all languages for the current contest from the API.
98-
105+
99106 Returns:
100107 The languages or None if an error occurred.
101108 '''
@@ -105,22 +112,22 @@ def read_languages() -> list:
105112 data = do_api_request (endpoint )
106113 except RuntimeError as e :
107114 logging .warning (e )
108- return None
115+ return []
109116
110117 if not isinstance (data , list ):
111118 logging .warning ("DOMjudge's API returned unexpected JSON data for endpoint 'languages'." )
112- return None
119+ return []
113120
114121 languages = []
115122
116123 for item in data :
117124 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 ):
122129 logging .warning ("DOMjudge's API returned unexpected JSON data for 'languages'." )
123- return None
130+ return []
124131 language = {
125132 'id' : item ['id' ],
126133 'name' : item ['name' ],
@@ -134,9 +141,10 @@ def read_languages() -> list:
134141
135142 return languages
136143
144+
137145def read_problems () -> list :
138146 '''Read all problems for the current contest from the API.
139-
147+
140148 Returns:
141149 The problems or None if an error occurred.
142150 '''
@@ -146,30 +154,31 @@ def read_problems() -> list:
146154 data = do_api_request (endpoint )
147155 except RuntimeError as e :
148156 logging .warning (e )
149- return None
157+ return []
150158
151159 if not isinstance (data , list ):
152160 logging .warning ("DOMjudge's API returned unexpected JSON data for endpoint 'problems'." )
153- return None
161+ return []
154162
155163 problems = []
156164
157165 for problem in data :
158166 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' ]):
162170 logging .warning ("DOMjudge's API returned unexpected JSON data for 'problems'." )
163- return None
171+ return []
164172 problems .append (problem )
165173
166174 logging .info (f'Read { len (problems )} problem(s) from the API.' )
167175
168176 return problems
169177
178+
170179def do_api_request (name : str ):
171180 '''Perform an API call to the given endpoint and return its data.
172-
181+
173182 Parameters:
174183 name (str): the endpoint to call
175184
@@ -184,7 +193,7 @@ def do_api_request(name: str):
184193 raise RuntimeError ('No baseurl set' )
185194
186195 url = f'{ baseurl } api/{ api_version } { name } '
187-
196+
188197 logging .info (f'Connecting to { url } ' )
189198
190199 try :
@@ -203,6 +212,7 @@ def do_api_request(name: str):
203212
204213 return json .loads (response .text )
205214
215+
206216def kotlin_base_entry_point (filebase : str ) -> str :
207217 if filebase == "" :
208218 return "_"
@@ -219,6 +229,7 @@ def kotlin_base_entry_point(filebase: str) -> str:
219229
220230 return filebase
221231
232+
222233def get_epilog ():
223234 '''Get the epilog for the help text.'''
224235
@@ -248,7 +259,7 @@ drop-down box in the web interface.'''
248259 language_part = 'For LANGUAGE use the ID or a common extension.'
249260 else :
250261 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 ])
252263 for language in languages :
253264 sorted_exts = ', ' .join (sorted (language ['extensions' ]))
254265 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):
287298
288299 return "\n \n " .join (part for part in epilog_parts if part )
289300
301+
290302def do_api_submit ():
291303 '''Submit to the API with the given data.'''
292304
@@ -323,15 +335,16 @@ def do_api_submit():
323335 error (f'Parsing DOMjudge\' s API output failed: { e } ' )
324336
325337 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 )):
328340 error ('DOMjudge\' s API returned unexpected JSON data.' )
329341
330342 time = datetime .datetime .fromisoformat (submission ['time' ]).strftime ('%H:%M:%S' )
331343 sid = submission ['id' ]
332344 print (f"Submission received: id = s{ sid } , time = { time } " )
333345 print (f"Check { baseurl } team/submission/{ sid } for the result." )
334346
347+
335348version_text = '''
336349submit -- part of DOMjudge
337350Written by the DOMjudge developers
@@ -364,7 +377,8 @@ parser.add_argument('-c', '--contest', help='''submit for contest with ID or sho
364377parser .add_argument ('-p' , '--problem' , help = 'submit for problem with ID or label PROBLEM' , default = '' )
365378parser .add_argument ('-l' , '--language' , help = 'submit in language with ID LANGUAGE' , default = '' )
366379parser .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' )
368382parser .add_argument ('-q' , '--quiet' , help = 'suppress warning/info messages' , action = 'store_true' )
369383parser .add_argument ('-y' , '--assume-yes' , help = 'suppress user input and assume yes' , action = 'store_true' )
370384parser .add_argument ('-u' , '--url' , help = '''submit to server with base address URL
@@ -377,7 +391,7 @@ verbosity = args.verbose
377391if args .quiet :
378392 verbosity = 'ERROR'
379393
380- logging .basicConfig (format = f '%(message)s' , level = loglevels [verbosity ])
394+ logging .basicConfig (format = '%(message)s' , level = loglevels [verbosity ])
381395logging .info (f'set verbosity to { verbosity } ' )
382396
383397problem_id = args .problem
@@ -403,9 +417,9 @@ contests = read_contests() if baseurl else None
403417if not contests and not args .help :
404418 logging .warning ('Could not obtain active contests.' )
405419
406- my_contest = None
407- my_language = None
408- my_problem = None
420+ my_contest : dict = {}
421+ my_language : dict = {}
422+ my_problem : dict = {}
409423
410424if not contest_id :
411425 if not contests :
@@ -423,8 +437,8 @@ elif contests:
423437 my_contest = contest
424438 break
425439
426- languages = None
427- problems = None
440+ languages : list = []
441+ problems : list = []
428442if my_contest and baseurl :
429443 if 'allow_submit' in my_contest and not my_contest ['allow_submit' ]:
430444 warn_user ('Submissions for contest (temporarily) disabled' )
@@ -470,7 +484,7 @@ for index, filename in enumerate(args.filename, 1):
470484 st = os .stat (filename )
471485 except FileNotFoundError :
472486 usage (f"Cannot find file `{ filename } '." )
473-
487+
474488 logging .debug (f"submission file { index } : `{ filename } '" )
475489
476490 # Do some checks on submission file and warn user
@@ -496,6 +510,7 @@ for index, filename in enumerate(args.filename, 1):
496510
497511filebase = os .path .basename (filenames [0 ])
498512
513+ ext = ""
499514if '.' in filebase :
500515 dot = filebase .rfind ('.' )
501516 ext = filebase [dot + 1 :]
@@ -527,7 +542,7 @@ for problem in problems:
527542 break
528543
529544if not my_problem :
530- usage ('No known problem specified or detected.' )
545+ usage ('No known problem specified or detected.' )
531546
532547# Guess entry point if not already specified.
533548if not entry_point and my_language ['entry_point_required' ]:
0 commit comments