예제 #1
0
 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)
예제 #2
0
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)
예제 #3
0
 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()
예제 #4
0
 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()
예제 #5
0
 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
예제 #6
0
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)
예제 #7
0
    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)
예제 #8
0
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
예제 #9
0
 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.')
예제 #10
0
    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)
예제 #11
0
 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()
예제 #12
0
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
예제 #13
0
 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')
예제 #14
0
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
예제 #15
0
    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]
예제 #16
0
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()
예제 #18
0
 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
예제 #19
0
 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
예제 #20
0
 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']
예제 #21
0
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
예제 #22
0
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
예제 #23
0
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')
예제 #24
0
    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')
예제 #25
0
    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')
예제 #26
0
    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)
예제 #27
0
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
예제 #28
0
    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))
예제 #29
0
 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()
예제 #30
0
 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()