def request(self, session: requests.Session, method: str = None, action: Optional[str] = None, raise_for_status: bool = True, **kwargs) -> requests.Response: if method is None: method = self.form['method'].upper() url = urllib.parse.urljoin(self.url, action) action = action or self.form['action'] log.debug('payload: %s', str(self.payload)) return request(method, url, session=session, raise_for_status=raise_for_status, data=self.payload, files=self.files, **kwargs)
def main(args: Optional[List[str]] = None) -> None: log.addHandler(log.logging.StreamHandler(sys.stderr)) log.setLevel(log.logging.INFO) version_check() parser = get_parser() namespace = parser.parse_args(args=args) try: run_program(namespace, parser=parser) except NotImplementedError as e: log.debug('\n' + traceback.format_exc()) log.error('NotImplementedError') log.info( 'The operation you specified is not supported yet. Pull requests are welcome.' ) log.info( 'see: https://github.com/kmyk/online-judge-tools/blob/master/CONTRIBUTING.md' ) except onlinejudge.type.NotLoggedInError: log.error('login required') sys.exit(1) except requests.exceptions.HTTPError as e: log.error(str(e)) log.debug(traceback.format_exc()) sys.exit(1) except requests.exceptions.InvalidURL as e: log.error(str(e)) sys.exit(1) except onlinejudge.type.SampleParseError: log.error('Failed to parse sample.') sys.exit(1) except FileExistsError as e: log.error(str(e)) sys.exit(1)
def download_sample_cases( self, *, session: Optional[requests.Session] = None ) -> List[onlinejudge.type.TestCase]: session = session or utils.get_default_session() resp = utils.request('GET', self.get_url(), session=session) soup = bs4.BeautifulSoup(resp.content.decode(resp.encoding), utils.html_parser) samples = onlinejudge._implementation.testcase_zipper.SampleZipper() for table in soup.find_all('table', class_="samples"): log.debug('table: %s', str(table)) case = table.find('tbody').find('tr') assert len(list(case.children)) == 2 input_pre, output_pre = list( map(lambda td: td.find('pre'), list(case.children))) assert input_pre.name == 'pre' assert output_pre.name == 'pre' assert re.search("^preSample.*Input$", input_pre.attrs['id']) assert re.search("^preSample.*Output$", output_pre.attrs['id']) samples.add( utils.parse_content(input_pre).lstrip().encode(), "Input") samples.add( utils.parse_content(output_pre).lstrip().encode(), "Output") return samples.get()
def download_sample_cases( self, *, session: Optional[requests.Session] = None ) -> List[onlinejudge.type.TestCase]: session = session or utils.get_default_session() # get resp = utils.request('GET', self.get_url(), session=session) # parse soup = bs4.BeautifulSoup(resp.content.decode(resp.encoding), utils.html_parser) samples = onlinejudge._implementation.testcase_zipper.SampleZipper() for tag in soup.find_all('div', class_=re.compile( '^(in|out)put$')): # Codeforces writes very nice HTML :) log.debug('tag: %s', str(tag)) assert len(list(tag.children)) title, pre = list(tag.children) assert 'title' in title.attrs['class'] assert pre.name == 'pre' s = '' for it in pre.children: if it.name == 'br': s += '\n' else: s += it.string s = s.lstrip() samples.add(s.encode(), title.string) return samples.get()
def download_sample_cases( self, session: Optional[requests.Session] = None ) -> List[onlinejudge.type.TestCase]: session = session or utils.new_default_session() # get url = self.get_url(contests=False) + '/file/statement/samples.zip' resp = utils.request('GET', url, session=session, raise_for_status=False) if resp.status_code == 404: log.warning('samples.zip not found') log.info( 'this 404 happens in both cases: 1. no sample cases as intended; 2. just an error' ) return [] resp.raise_for_status() # parse with zipfile.ZipFile(io.BytesIO(resp.content)) as fh: samples = [] # type: List[TestCase] for filename in sorted(fh.namelist()): log.debug('filename: %s', filename) if filename.endswith('.in'): inpath = filename outpath = filename[:-3] + '.ans' indata = fh.read(inpath).decode() outdata = fh.read(outpath).decode() samples += [ TestCase(LabeledString(inpath, indata), LabeledString(outpath, outdata)) ] return samples
def run_program(args: argparse.Namespace, parser: argparse.ArgumentParser) -> None: if args.version: print('online-judge-tools {}'.format(onlinejudge.__version__)) sys.exit(0) if args.verbose: log.setLevel(log.logging.DEBUG) log.debug('args: %s', str(args)) if args.subcommand in ['download', 'd', 'dl']: download(args) elif args.subcommand in ['login', 'l']: login(args) elif args.subcommand in ['submit', 's']: submit(args) elif args.subcommand in ['test', 't']: test(args) elif args.subcommand in ['test-reactive', 't/r']: test_reactive(args) elif args.subcommand in ['generate-output', 'g/o']: generate_output(args) elif args.subcommand in ['generate-input', 'g/i']: generate_input(args) else: parser.print_help(file=sys.stderr) sys.exit(1)
def iterate_contest_data(self, *, lang: str = 'ja', session: Optional[requests.Session] = None) -> Iterator['AtCoderContestData']: """ :param lang: must be `ja` (default) or `en`. :note: `lang=ja` is required to see some Japanese-local contests. :note: You can use `lang=en` to see the English names of contests. """ assert lang in ('ja', 'en') session = session or utils.get_default_session() last_page = None for page in itertools.count(1): # 1-based if last_page is not None and page > last_page: break # get url = 'https://atcoder.jp/contests/archive?lang={}&page={}'.format(lang, page) resp = _request('GET', url, session=session) timestamp = datetime.datetime.now(datetime.timezone.utc).astimezone() # parse soup = bs4.BeautifulSoup(resp.content.decode(resp.encoding), utils.html_parser) if last_page is None: last_page = int(soup.find('ul', class_='pagination').find_all('li')[-1].text) log.debug('last page: %s', last_page) tbody = soup.find('tbody') for tr in tbody.find_all('tr'): yield AtCoderContestData._from_table_row(tr, lang=lang, response=resp, session=session, timestamp=timestamp)
def generate_scanner(args: 'argparse.Namespace') -> None: if not args.silent: log.warning('This feature is ' + log.red('experimental') + '.') if args.silent: for handler in log.logger.handlers: log.removeHandler(handler) problem = onlinejudge.dispatch.problem_from_url(args.url) if problem is None: sys.exit(1) with utils.with_cookiejar(utils.new_default_session(), path=args.cookie) as sess: it = problem.get_input_format(session=sess) # type: Any if not it: log.error('input format not found') sys.exit(1) try: log.debug('original data: %s', repr(it)) it = list(tokenize(it)) log.debug('tokenized: %s', str(it)) it = list(parse(it)) log.debug('parsed: %s', str(it)) it = postprocess(it) log.debug('postprocessed: %s', str(it)) it = export(it, use_scanf=args.scanf, repeat_macro=args.repeat_macro) log.debug('result: %s', repr(it)) except: log.error('something wrong') raise log.success('success:') print(log.bold(it.rstrip())) # to stdout
def login(self, *, get_credentials: onlinejudge.type.CredentialsProvider, session: Optional[requests.Session] = None) -> None: """ :raises LoginError: """ session = session or utils.get_default_session() url = 'https://codeforces.com/enter' # get resp = utils.request('GET', url, session=session) if resp.url != url: # redirected log.info('You have already signed in.') return # parse soup = bs4.BeautifulSoup(resp.content.decode(resp.encoding), utils.html_parser) form = soup.find('form', id='enterForm') log.debug('form: %s', str(form)) username, password = get_credentials() form = utils.FormSender(form, url=resp.url) form.set('handleOrEmail', username) form.set('password', password) form.set('remember', 'on') # post resp = form.request(session) resp.raise_for_status() if resp.url != url: # redirected log.success('Welcome, %s.', username) else: log.failure('Invalid handle or password.') raise LoginError('Invalid handle or password.')
def _find_sample_tags( self, soup) -> Generator[Tuple[bs4.Tag, bs4.Tag], None, None]: for pre in soup.find_all('pre'): log.debug('pre tag: %s', str(pre)) if not pre.string: continue prv = utils.previous_sibling_tag(pre) # the first format: h3+pre if prv and prv.name == 'h3' and prv.string: yield (pre, prv) else: # ignore tags which are not samples # example: https://atcoder.jp/contests/abc003/tasks/abc003_4 while prv is not None: if prv.name == 'pre': break prv = utils.previous_sibling_tag(prv) if prv is not None: continue # the second format: h3+section pre if pre.parent and pre.parent.name == 'section': prv = pre.parent and utils.previous_sibling_tag(pre.parent) if prv and prv.name == 'h3' and prv.string: yield (pre, prv)
def download_sample_cases( self, *, session: Optional[requests.Session] = None ) -> List[onlinejudge.type.TestCase]: session = session or utils.get_default_session() # get resp = utils.request('GET', self.get_url(), session=session) # parse soup = bs4.BeautifulSoup(resp.content.decode(resp.encoding), utils.html_parser) samples = onlinejudge._implementation.testcase_zipper.SampleZipper() for tag in soup.find_all('div', class_=re.compile( '^(in|out)put$')): # Codeforces writes very nice HTML :) log.debug('tag: %s', str(tag)) non_empty_children = [ child for child in tag.children if child.name or child.strip() ] log.debug("tags after removing empty strings: %s", non_empty_children) assert len(non_empty_children ) == 2 # if not 2, next line throws ValueError. title, pre = list(non_empty_children) assert 'title' in title.attrs['class'] assert pre.name == 'pre' data = utils.format_sample_case(str(utils.parse_content(pre))) samples.add(data.encode(), title.string) return samples.get()
def glob_with_format(directory: pathlib.Path, format: str) -> List[pathlib.Path]: table = {} table['s'] = '*' table['e'] = '*' pattern = (glob.escape(str(directory)) + '/' + utils.percentformat(glob.escape(format).replace('\\%', '%'), table)) paths = list(map(pathlib.Path, glob.glob(pattern))) for path in paths: log.debug('testcase globbed: %s', path) return paths
def submit_code(self, code: bytes, language_id: LanguageId, filename: Optional[str] = None, session: Optional[requests.Session] = None) -> Submission: """ :raises NotLoggedInError: :raises SubmissionError: """ assert language_id in [ language.id for language in self.get_available_languages(session=session) ] session = session or utils.new_default_session() # get url = 'http://{}.contest.atcoder.jp/submit'.format( self.contest_id) # TODO: use beta.atcoder.jp resp = _request('GET', url, session=session) msgs = AtCoderService._get_messages_from_cookie(resp.cookies) if AtCoderService._report_messages(msgs, unexpected=True): raise SubmissionError # check whether logged in path = utils.normpath(urllib.parse.urlparse(resp.url).path) if path.startswith('/login'): log.error('not logged in') raise NotLoggedInError # parse soup = bs4.BeautifulSoup(resp.content.decode(resp.encoding), utils.html_parser) form = soup.find('form', action=re.compile(r'^/submit\?task_id=')) if not form: log.error('form not found') raise SubmissionError log.debug('form: %s', str(form)) # post task_id = self._get_task_id(session=session) form = utils.FormSender(form, url=resp.url) form.set('task_id', str(task_id)) form.set('source_code', code) form.set('language_id_{}'.format(task_id), str(language_id)) resp = form.request(session=session) resp.raise_for_status() # result msgs = AtCoderService._get_messages_from_cookie(resp.cookies) AtCoderService._report_messages(msgs) if '/submissions/me' in resp.url: # example: https://practice.contest.atcoder.jp/submissions/me#32174 # CAUTION: this URL is not a URL of the submission log.success('success: result: %s', resp.url) # NOTE: ignore the returned legacy URL and use beta.atcoder.jp's one url = 'https://beta.atcoder.jp/contests/{}/submissions/me'.format( self.contest_id) return utils.DummySubmission(url, problem=self) else: log.failure('failure') log.debug('redirected to %s', resp.url) raise SubmissionError('it may be a rate limit')
def exec_command( command_str: str, *, stdin: Optional[IO[Any]] = None, input: Optional[bytes] = None, timeout: Optional[float] = None, gnu_time: Optional[str] = None ) -> Tuple[Dict[str, Any], subprocess.Popen]: if input is not None: assert stdin is None stdin = subprocess.PIPE # type: ignore if gnu_time is not None: context = tempfile.NamedTemporaryFile(delete=True) # type: Any else: context = contextlib.ExitStack( ) # TODO: we should use contextlib.nullcontext() if possible with context as fh: command = shlex.split(command_str) if gnu_time is not None: command = [gnu_time, '-f', '%M', '-o', fh.name, '--'] + command if os.name == 'nt': # HACK: without this encoding and decoding, something randomly fails with multithreading; see https://github.com/kmyk/online-judge-tools/issues/468 command = command_str.encode().decode() # type: ignore begin = time.perf_counter() try: proc = subprocess.Popen(command, stdin=stdin, stdout=subprocess.PIPE, stderr=sys.stderr) except FileNotFoundError: log.error('No such file or directory: %s', command) sys.exit(1) except PermissionError: log.error('Permission denied: %s', command) sys.exit(1) try: answer, _ = proc.communicate(input=input, timeout=timeout) except subprocess.TimeoutExpired: proc.terminate() answer = None end = time.perf_counter() memory = None # type: Optional[float] if gnu_time is not None: with open(fh.name) as fh1: reported = fh1.read() log.debug('GNU time says:\n%s', reported) if reported.strip() and reported.splitlines()[-1].isdigit(): memory = int(reported.splitlines()[-1]) / 1000 info = { 'answer': answer, # Optional[byte] 'elapsed': end - begin, # float, in second 'memory': memory, # Optional[float], in megabyte } return info, proc
def download_sample_cases( self, *, session: Optional[requests.Session] = None) -> List[TestCase]: session = session or utils.get_default_session() if self.domain == 'codingcompetitions.withgoogle.com': url = 'https://codejam.googleapis.com/dashboard/{}/poll?p=e30'.format( self.contest_id) resp = utils.request('GET', url, session=session) data = json.loads( base64.urlsafe_b64decode(resp.content + b'=' * ((-len(resp.content)) % 4)).decode()) log.debug('%s', data) # parse JSON for task in data['challenge']['tasks']: if task['id'] == self.problem_id: statement = task['statement'] break else: raise SampleParseError( "the problem {} is not found in the challenge {}".format( repr(self.problem_id), repr(self.contest_id))) elif self.domain == 'code.google.com': url = 'https://{}/{}/contest/{}/dashboard/ContestInfo'.format( self.domain, self.kind, self.contest_id) resp = utils.request('GET', url, session=session) data = json.loads(resp.content.decode()) # parse JSON assert self.problem_id.startswith('p') i = int(self.problem_id[1:]) statement = data['problems'][i]['body'] else: assert False # parse HTML soup = bs4.BeautifulSoup(statement, utils.html_parser) io_contents = soup.find_all('pre', class_='io-content') if len(io_contents) != 2: raise SampleParseError( """the number of <pre class="io-content"> is not two""") if io_contents[0].text.startswith('Case #'): log.warning('''the sample input starts with "Case #"''') if not io_contents[1].text.startswith('Case #'): log.warning('''the sample output doesn't start with "Case #"''') sample = TestCase( 'sample', 'Input', utils.textfile(io_contents[0].text.rstrip()).encode(), 'Output', utils.textfile(io_contents[1].text.rstrip()).encode(), ) return [sample]
def _request(*args, **kwargs): """ This is a workaround. AtCoder's servers sometime fail to send "Content-Type" field. see https://github.com/kmyk/online-judge-tools/issues/28 and https://github.com/kmyk/online-judge-tools/issues/232 """ resp = utils.request(*args, **kwargs) log.debug('AtCoder\'s server said "Content-Type: %s"', resp.headers.get('Content-Type', '(not sent)')) resp.encoding = 'UTF-8' _list_alert(resp, print_=True) return resp
def get_handmade_sample_cases(self, *, html: str) -> List[TestCase]: # parse soup = bs4.BeautifulSoup(html, utils.html_parser) samples = onlinejudge._implementation.testcase_zipper.SampleZipper() for pre in soup.select('.sample pre'): log.debug('pre %s', str(pre)) it = self._parse_sample_tag(pre) if it is not None: data, name = it samples.add(data.encode(), name) return samples.get()
def _parse_sample_tag(self, tag: bs4.Tag) -> Optional[Tuple[str, str]]: assert isinstance(tag, bs4.Tag) assert tag.name == 'pre' prv = utils.previous_sibling_tag(tag) pprv = tag.parent and utils.previous_sibling_tag(tag.parent) if prv.name == 'h6' and tag.parent.name == 'div' and tag.parent[ 'class'] == ['paragraph'] and pprv.name == 'h5': log.debug('h6: %s', str(prv)) log.debug('name.encode(): %s', prv.string.encode()) s = tag.string or '' # tag.string for the tag "<pre></pre>" returns None return utils.textfile(s.lstrip()), pprv.string + ' ' + prv.string return None
def download_sample_cases( self, *, session: Optional[requests.Session] = None) -> List[TestCase]: session = session or utils.get_default_session() # get resp = utils.request('GET', self.get_url(), session=session) # parse soup = bs4.BeautifulSoup(resp.content.decode(resp.encoding), utils.html_parser) in_pre, out_pre = soup.find_all('pre', class_='sio') in_p = in_pre.find_previous_sibling('p', class_='pst') out_p = out_pre.find_previous_sibling('p', class_='pst') log.debug('pre (in): %s', in_pre.contents) log.debug('pre (out): %s', out_pre.contents) assert in_p.text.strip() == 'Sample Input' assert out_p.text.strip() == 'Sample Output' assert len(in_pre.contents) == len(out_pre.contents) samples = [] # type: List[TestCase] if len(in_pre.contents) == 1: assert isinstance(in_pre.contents[0], bs4.NavigableString) assert isinstance(out_pre.contents[0], bs4.NavigableString) samples += [ TestCase( 'sample', in_p.text.strip(), in_pre.text.encode() + b'\r\n', out_p.text.strip(), out_pre.text.encode() + b'\r\n', ) ] else: assert len(in_pre.contents) % 2 == 0 for i in range(len(in_pre.contents) // 2): in_name = in_pre.contents[2 * i] in_data = in_pre.contents[2 * i + 1] out_name = out_pre.contents[2 * i] out_data = out_pre.contents[2 * i + 1] assert in_name.name == 'b' assert isinstance(in_data, bs4.NavigableString) assert out_name.name == 'b' assert isinstance(out_data, bs4.NavigableString) samples += [ TestCase( 'sample-{}'.format(i + 1), in_name.text.strip(), str(in_data).strip().encode() + b'\r\n', out_name.text.strip(), str(out_data).strip().encode() + b'\r\n', ) ] return samples
def __init__(self, form: bs4.Tag, url: str): assert isinstance(form, bs4.Tag) assert form.name == 'form' self.form = form self.url = url self.payload = {} # type: Dict[str, str] self.files = {} # type: Dict[str, IO[Any]] for input in self.form.find_all('input'): log.debug('input: %s', str(input)) if input.attrs.get('type') in ['checkbox', 'radio']: continue if 'name' in input.attrs and 'value' in input.attrs: self.payload[input['name']] = input['value']
def request(method: str, url: str, session: requests.Session, raise_for_status: bool = True, **kwargs) -> requests.Response: assert method in ['GET', 'POST'] kwargs.setdefault('allow_redirects', True) log.status('%s: %s', method, url) if 'data' in kwargs: log.debug('data: %s', repr(kwargs['data'])) resp = session.request(method, url, **kwargs) if resp.url != url: log.status('redirected: %s', resp.url) log.status(describe_status_code(resp.status_code)) if raise_for_status: resp.raise_for_status() return resp
def glob_with_format(directory: pathlib.Path, format: str) -> List[pathlib.Path]: if os.name == 'nt': format = format.replace('/', '\\') table = {} table['s'] = '*' table['e'] = '*' pattern = (glob.escape(str(directory) + os.path.sep) + percentformat( glob.escape(format).replace(glob.escape('%'), '%'), table)) paths = list(map(pathlib.Path, glob.glob(pattern))) for path in paths: log.debug('testcase globbed: %s', path) return paths
def main(args: Optional[List[str]] = None) -> None: log.addHandler(log.logging.StreamHandler(sys.stderr)) log.setLevel(log.logging.INFO) version_check() parser = get_parser() namespace = parser.parse_args(args=args) try: run_program(namespace, parser=parser) except NotImplementedError as e: log.debug('\n' + traceback.format_exc()) log.error('NotImplementedError') log.info('The operation you specified is not supported yet. Pull requests are welcome.') log.info('see: https://github.com/kmyk/online-judge-tools/blob/master/CONTRIBUTING.md')
def submit_code( self, code: bytes, language_id: LanguageId, filename: Optional[str] = None, session: Optional[requests.Session] = None) -> 'AtCoderSubmission': """ :raises NotLoggedInError: :raises SubmissionError: """ session = session or utils.get_default_session() assert language_id in [ language.id for language in self.get_available_languages(session=session) ] # get url = 'https://atcoder.jp/contests/{}/submit'.format(self.contest_id) resp = _request('GET', url, session=session) # check whether logged in if 'login' in resp.url: raise NotLoggedInError # parse soup = bs4.BeautifulSoup(resp.content.decode(resp.encoding), utils.html_parser) form = soup.find('form', action='/contests/{}/submit'.format(self.contest_id)) if not form: raise SubmissionError('something wrong') log.debug('form: %s', str(form)) # post form = utils.FormSender(form, url=resp.url) form.set('data.TaskScreenName', self.problem_id) form.set('data.LanguageId', str(language_id)) form.set('sourceCode', code) resp = form.request(session=session) _list_alert(resp, print_=True) # result if '/submissions/me' in resp.url: submission = next( AtCoderContest( self.contest_id)._iterate_submissions_from_response(resp)) log.success('success: result: %s', submission.get_url()) return submission else: raise SubmissionError('it may be a rate limit')
def submit_code(self, code: bytes, language_id: LanguageId, filename: Optional[str] = None, session: Optional[requests.Session] = None) -> Submission: """ :raises NotLoggedInError: :raises SubmissionError: """ assert language_id in [ language.id for language in self.get_available_languages(session=session) ] session = session or utils.new_default_session() # get url = 'https://atcoder.jp/contests/{}/submit'.format(self.contest_id) resp = _request('GET', url, session=session) # check whether logged in if 'login' in resp.url: raise NotLoggedInError # parse soup = bs4.BeautifulSoup(resp.content.decode(resp.encoding), utils.html_parser) form = soup.find('form', action='/contests/{}/submit'.format(self.contest_id)) if not form: raise SubmissionError('something wrong') log.debug('form: %s', str(form)) # post form = utils.FormSender(form, url=resp.url) form.set('data.TaskScreenName', self.problem_id) form.set('data.LanguageId', str(language_id)) form.set('sourceCode', code) resp = form.request(session=session) _list_alert(resp, print_=True) # result if '/submissions/me' in resp.url: # example: https://practice.contest.atcoder.jp/submissions/me#32174 # CAUTION: this URL is not a URL of the submission log.success('success: result: %s', resp.url) return utils.DummySubmission(resp.url, problem=self) else: raise SubmissionError('it may be a rate limit')
def submit_code( self, code: bytes, language_id: LanguageId, *, filename: Optional[str] = None, session: Optional[requests.Session] = None ) -> onlinejudge.type.Submission: """ :raises NotLoggedInError: :raises SubmissionError: """ session = session or utils.get_default_session() if not self.get_service().is_logged_in(session=session): raise NotLoggedInError # get resp = utils.request('GET', self.get_url(), session=session) # parse soup = bs4.BeautifulSoup(resp.content.decode(resp.encoding), utils.html_parser) csrftoken = soup.find('meta', attrs={ 'name': 'csrf-token' }).attrs['content'] # post url = 'https://www.hackerrank.com/rest/contests/{}/challenges/{}/submissions'.format( self.contest_slug, self.challenge_slug) payload = { 'code': code, 'language': str(language_id), 'contest_slug': self.contest_slug } log.debug('payload: %s', payload) resp = utils.request('POST', url, session=session, json=payload, headers={'X-CSRF-Token': csrftoken}) # parse it = json.loads(resp.content.decode()) log.debug('json: %s', it) if not it['status']: log.failure('Submit Code: failed') raise SubmissionError model_id = it['model']['id'] url = self.get_url().rstrip('/') + '/submissions/code/{}'.format( model_id) log.success('success: result: %s', url) return utils.DummySubmission(url, problem=self)
def check_gnu_time(gnu_time: str) -> bool: try: with tempfile.NamedTemporaryFile(delete=True) as fh: proc = subprocess.run([gnu_time, '-f', '%M KB', '-o', fh.name, '--', 'true']) assert proc.returncode == 0 with open(fh.name) as fh1: data = fh1.read() int(utils.remove_suffix(data.rstrip().splitlines()[-1], ' KB')) return True except NameError: raise # NameError is not a runtime error caused by the environment, but a coding mistake except AttributeError: raise # AttributeError is also a mistake except Exception as e: log.debug(traceback.format_exc()) return False
def submit_code( self, code: bytes, language_id: LanguageId, *, filename: Optional[str] = None, session: Optional[requests.Session] = None ) -> onlinejudge.type.Submission: """ :raises NotLoggedInError: :raises SubmissionError: """ session = session or utils.get_default_session() # get resp = utils.request('GET', self.get_url(), session=session) # parse soup = bs4.BeautifulSoup(resp.content.decode(resp.encoding), utils.html_parser) form = soup.find('form', class_='submitForm') if form is None: log.error('not logged in') raise NotLoggedInError log.debug('form: %s', str(form)) # make data form = utils.FormSender(form, url=resp.url) form.set('programTypeId', language_id) form.set_file('sourceFile', filename or 'code', code) resp = form.request(session=session) resp.raise_for_status() # result if resp.url.endswith('/my'): # example: https://codeforces.com/contest/598/my log.success('success: result: %s', resp.url) return utils.DummySubmission(resp.url, problem=self) else: log.failure('failure') # parse error messages soup = bs4.BeautifulSoup(resp.content.decode(resp.encoding), utils.html_parser) msgs = [] # type: List[str] for span in soup.findAll('span', class_='error'): msgs += [span.string] log.warning('Codeforces says: "%s"', span.string) raise SubmissionError( 'it may be the "You have submitted exactly the same code before" error: ' + str(msgs))
def download_sample_cases( self, session: Optional[requests.Session] = None) -> List[TestCase]: session = session or utils.get_default_session() # get resp = utils.request('GET', self.get_url(), session=session) # parse soup = bs4.BeautifulSoup(resp.content.decode(resp.encoding), utils.html_parser) samples = onlinejudge._implementation.testcase_zipper.SampleZipper() for pre in soup.select('.sample pre'): log.debug('pre: %s', str(pre)) it = self._parse_sample_tag(pre) if it is not None: data, name = it samples.add(data.encode(), name) return samples.get()
def _parse_sample_cases(cls, soup: bs4.BeautifulSoup) -> List[onlinejudge.type.TestCase]: """ :raises SampleParseError: """ samples = onlinejudge._implementation.testcase_zipper.SampleZipper() lang = None for pre, h3 in cls._find_sample_tags(soup): s = utils.textfile(utils.dos2unix(pre.string.lstrip())) name = h3.string l = cls._get_tag_lang(pre) if lang is None: lang = l elif lang != l: log.debug('skipped due to language: current one is %s, not %s: %s ', lang, l, name) continue samples.add(s.encode(), name) return samples.get()