Пример #1
0
 def download_sample_cases(
     self,
     session: Optional[requests.Session] = None
 ) -> List[onlinejudge.type.TestCase]:
     session = session or utils.new_default_session()
     # get
     resp = _request('GET', self.get_url(), session=session)
     msgs = AtCoderService._get_messages_from_cookie(resp.cookies)
     if AtCoderService._report_messages(msgs, unexpected=True):
         # example message: "message: You cannot see this page."
         log.warning('are you logged in?')
         return []
     # parse
     soup = bs4.BeautifulSoup(resp.content.decode(resp.encoding),
                              utils.html_parser)
     samples = onlinejudge._implementation.testcase_zipper.SampleZipper()
     lang = None
     for pre, h3 in self._find_sample_tags(soup):
         s = utils.textfile(utils.dos2unix(pre.string.lstrip()))
         name = h3.string
         l = self._get_tag_lang(pre)
         if lang is None:
             lang = l
         elif lang != l:
             log.info(
                 'skipped due to language: current one is %s, not %s: %s ',
                 lang, l, name)
             continue
         samples.add(s.encode(), name)
     return samples.get()
Пример #2
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
Пример #3
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.')
Пример #4
0
def code_statistics(args: 'argparse.Namespace') -> None:
    with open(args.file, 'rb') as fh:
        code = fh.read()
    stat = get_statistics(code)
    stat['size'] = len(code)
    for key in ('size', 'binary', 'alnum', 'symbol', 'whitespace'):
        log.info('%s = %d', key, stat[key])
Пример #5
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)
Пример #6
0
def download(args: 'argparse.Namespace') -> None:
    # prepare values
    problem = onlinejudge.dispatch.problem_from_url(args.url)
    if problem is None:
        raise requests.exceptions.InvalidURL('The contest "%s" is not supported' % args.url)
    is_default_format = args.format is None and args.directory is None  # must be here since args.directory and args.format are overwritten
    if args.directory is None:
        args.directory = pathlib.Path('test')
    if args.format is None:
        args.format = '%b.%e'

    # get samples from the server
    with utils.with_cookiejar(utils.new_session_with_our_user_agent(), path=args.cookie) as sess:
        if args.yukicoder_token and isinstance(problem, YukicoderProblem):
            sess.headers['Authorization'] = 'Bearer {}'.format(args.yukicoder_token)
        if args.system:
            samples = problem.download_system_cases(session=sess)
        else:
            samples = problem.download_sample_cases(session=sess)

    if not samples:
        raise onlinejudge.type.SampleParseError("Sample not found")

    # append the history for submit command
    if not args.dry_run and is_default_format:
        history = onlinejudge._implementation.download_history.DownloadHistory()
        history.add(problem)

    # write samples to files
    for i, sample in enumerate(samples):
        log.emit('')
        log.info('sample %d', i)
        for ext in ['in', 'out']:
            data = getattr(sample, ext + 'put_data')
            if data is None:
                continue
            name = sample.name
            table = {}
            table['i'] = str(i + 1)
            table['e'] = ext
            table['n'] = name
            table['b'] = os.path.basename(name)
            table['d'] = os.path.dirname(name)
            path = args.directory / format_utils.percentformat(args.format, table)  # type: pathlib.Path
            log.status('%sput: %s', ext, name)
            if not args.silent:
                log.emit(utils.snip_large_file_content(data, limit=40, head=20, tail=10, bold=True))
            if args.dry_run:
                continue
            if path.exists():
                raise FileExistsError('Failed to download since file already exists: ' + str(path))
            path.parent.mkdir(parents=True, exist_ok=True)
            with path.open('wb') as fh:
                fh.write(data)
            log.success('saved to: %s', path)

    # print json
    if args.json:
        print(json.dumps(list(map(convert_sample_to_dict, samples))))
Пример #7
0
def is_logged_in_with_message(service: onlinejudge.type.Service, *,
                              session: requests.Session) -> bool:
    if service.is_logged_in(session=session):
        log.info('You have already signed in.')
        return True
    else:
        log.warning('You are not signed in.')
        return False
Пример #8
0
def version_check() -> None:
    if utils.is_update_available_on_pypi():
        log.warning('update available: %s -> %s', version.__version__,
                    utils.get_latest_version_from_pypi())
        log.info('run: $ pip3 install -U %s', version.__package_name__)
        log.info(
            'see: https://github.com/kmyk/online-judge-tools/blob/master/CHANGELOG.md'
        )
Пример #9
0
def download(args: 'argparse.Namespace') -> None:
    # prepare values
    problem = onlinejudge.dispatch.problem_from_url(args.url)
    if problem is None:
        sys.exit(1)
    is_default_format = args.format is None and args.directory is None  # must be here since args.directory and args.format are overwritten
    if args.directory is None:
        args.directory = pathlib.Path('test')
    if args.format is None:
        args.format = '%b.%e'

    # get samples from the server
    with utils.with_cookiejar(utils.new_session_with_our_user_agent(), path=args.cookie) as sess:
        if args.system:
            samples = problem.download_system_cases(session=sess)  # type: ignore
        else:
            samples = problem.download_sample_cases(session=sess)  # type: ignore

    # append the history for submit command
    if not args.dry_run and is_default_format:
        history = onlinejudge._implementation.download_history.DownloadHistory()
        history.add(problem)

    # write samples to files
    for i, sample in enumerate(samples):
        log.emit('')
        log.info('sample %d', i)
        for ext in ['in', 'out']:
            data = getattr(sample, ext + 'put_data')
            if data is None:
                continue
            name = sample.name
            table = {}
            table['i'] = str(i + 1)
            table['e'] = ext
            table['n'] = name
            table['b'] = os.path.basename(name)
            table['d'] = os.path.dirname(name)
            path = args.directory / format_utils.percentformat(args.format, table)  # type: pathlib.Path
            log.status('%sput: %s', ext, name)
            if not args.silent:
                log.emit(utils.snip_large_file_content(data, limit=40, head=20, tail=10, bold=True))
            if args.dry_run:
                continue
            if path.exists():
                log.warning('file already exists: %s', path)
                if not args.overwrite:
                    log.warning('skipped')
                    continue
            path.parent.mkdir(parents=True, exist_ok=True)
            with path.open('wb') as fh:
                fh.write(data)
            log.success('saved to: %s', path)

    # print json
    if args.json:
        print(json.dumps(list(map(convert_sample_to_dict, samples))))
Пример #10
0
 def download_sample_cases(self, session: Optional[requests.Session] = None) -> List[onlinejudge.type.TestCase]:
     session = session or utils.get_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
     return onlinejudge._implementation.testcase_zipper.extract_from_zip(resp.content, '%s.%e', out='ans')
Пример #11
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')
Пример #12
0
def generate_output(args: 'argparse.Namespace') -> None:
    if not args.test:
        args.test = cutils.glob_with_format(args.directory, args.format)  # by default
    if args.ignore_backup:
        args.test = cutils.drop_backup_or_hidden_files(args.test)
    tests = cutils.construct_relationship_of_files(args.test, args.directory, args.format)
    for name, it in sorted(tests.items()):
        log.emit('')
        log.info('%s', name)
        if 'out' in it:
            log.info('output file already exists.')
            log.info('skipped.')
            continue
        with it['in'].open() as inf:
            begin = time.perf_counter()
            answer, proc = utils.exec_command(args.command, shell=True, stdin=inf)
            end = time.perf_counter()
            log.status('time: %f sec', end - begin)
        if proc.returncode != 0:
            log.failure(log.red('RE') + ': return code %d', proc.returncode)
            log.info('skipped.')
            continue
        log.emit(utils.snip_large_file_content(answer, limit=40, head=20, tail=10, bold=True))
        match_result = cutils.match_with_format(args.directory, args.format, it['in'])  # type: Optional[Match[Any]]
        if match_result is not None:
            matched_name = match_result.groupdict()['name']  # type: str
        else:
            assert False
        path = cutils.path_from_format(args.directory, args.format, name=matched_name, ext='out')
        if not path.parent.is_dir():
            os.makedirs(str(path.parent), exist_ok=True)
        with path.open('wb') as fh:
            fh.write(answer)
        log.success('saved to: %s', path)
Пример #13
0
def _AtCoderProblemContent_parse_sample_cases(
        soup: bs4.BeautifulSoup) -> List[onlinejudge.type.TestCase]:
    samples = onlinejudge._implementation.testcase_zipper.SampleZipper()
    lang = None
    for pre, h3 in _AtCoderProblemContent_find_sample_tags(soup):
        s = utils.textfile(utils.dos2unix(pre.string.lstrip()))
        name = h3.string
        l = _AtCoderProblemContent_get_tag_lang(pre)
        if lang is None:
            lang = l
        elif lang != l:
            log.info('skipped due to language: current one is %s, not %s: %s ',
                     lang, l, name)
            continue
        samples.add(s.encode(), name)
    return samples.get()
Пример #14
0
def login_with_browser(service: onlinejudge.type.Service, *,
                       session: requests.Session) -> None:
    try:
        import selenium.webdriver
    except ImportError:
        raise

    try:
        profile = selenium.webdriver.FirefoxProfile()
        profile.set_preference("general.useragent.override",
                               session.headers['User-Agent'])
        driver = selenium.webdriver.Firefox(firefox_profile=profile)
    except selenium.common.exceptions.WebDriverException as e:
        raise WebDriverException(e)

    # get cookies via Selenium
    url = service.get_url_of_login_page()
    log.info('open with WebDriver: %s', url)
    driver.get(url)
    cookies = []  # type: List[Dict[str, str]]
    try:
        while driver.current_url:
            cookies = driver.get_cookies()
            time.sleep(0.1)
    except selenium.common.exceptions.WebDriverException:
        pass  # the window is closed

    # set cookies to the requests.Session
    log.info('copy cookies from WebDriver')
    for c in cookies:
        log.status('set cookie: %s', c['name'])
        morsel = http.cookies.Morsel()  # type: http.cookies.Morsel
        morsel.set(c['name'], c['value'], c['value'])
        morsel.update({
            key: value
            for key, value in c.items() if morsel.isReservedKey(key)
        })
        if not morsel['expires']:
            expires = datetime.datetime.now(
                datetime.timezone.utc).astimezone() + datetime.timedelta(
                    days=180)
            morsel.update({
                'expires':
                expires.strftime('%a, %d-%b-%Y %H:%M:%S GMT')
            })  # RFC2109 format
        cookie = requests.cookies.morsel_to_cookie(morsel)
        session.cookies.set_cookie(cookie)  # type: ignore
Пример #15
0
def test_single_case(test_name: str, test_input_path: pathlib.Path, test_output_path: Optional[pathlib.Path], *, lock: Optional[threading.Lock] = None, args: 'argparse.Namespace') -> Dict[str, Any]:
    # print the header earlier if not in parallel
    if lock is None:
        log.emit('')
        log.info('%s', test_name)

    # run the binary
    with test_input_path.open() as inf:
        info, proc = utils.exec_command(args.command, stdin=inf, timeout=args.tle, gnu_time=args.gnu_time)
        # TODO: the `answer` should be bytes, not str
        answer = (info['answer'] or b'').decode(errors='replace')  # type: str
        elapsed = info['elapsed']  # type: float
        memory = info['memory']  # type: Optional[float]

    # lock is require to avoid mixing logs if in parallel
    nullcontext = contextlib.ExitStack()  # TODO: use contextlib.nullcontext() after updating Python to 3.7
    with lock or nullcontext:
        if lock is not None:
            log.emit('')
            log.info('%s', test_name)
        log.status('time: %f sec', elapsed)
        if memory:
            if memory < MEMORY_PRINT:
                if args.print_memory:
                    log.status('memory: %f MB', memory)
            elif memory < MEMORY_WARNING:
                log.status('memory: %f MB', memory)
            else:
                log.warning('memory: %f MB', memory)

        status = compare_and_report(proc, answer, elapsed, memory, test_input_path, test_output_path, mle=args.mle, mode=args.mode, error=args.error, does_print_input=args.print_input, silent=args.silent, rstrip=args.rstrip, judge=args.judge)

    # return the result
    testcase = {
        'name': test_name,
        'input': str(test_input_path.resolve()),
    }
    if test_output_path:
        testcase['output'] = str(test_output_path.resolve())
    return {
        'status': status,
        'testcase': testcase,
        'output': answer,
        'exitcode': proc.returncode,
        'elapsed': elapsed,
        'memory': memory,
    }
Пример #16
0
    def login(self,
              get_credentials: onlinejudge.type.CredentialsProvider,
              session: Optional[requests.Session] = None) -> None:
        """
        :raises LoginError:
        """

        session = session or utils.new_default_session()
        url = 'https://www.hackerrank.com/auth/login'
        # get
        resp = utils.request('GET', url, session=session)
        if resp.url != url:
            log.info('You have already signed in.')
            return
        # parse
        soup = bs4.BeautifulSoup(resp.content.decode(resp.encoding),
                                 utils.html_parser)
        csrftoken = soup.find('meta', attrs={
            'name': 'csrf-token'
        }).attrs['content']
        tag = soup.find('input', attrs={'name': 'username'})
        while tag.name != 'form':
            tag = tag.parent
        form = tag
        # post
        username, password = get_credentials()
        form = utils.FormSender(form, url=resp.url)
        form.set('login', username)
        form.set('password', password)
        form.set('remember_me', 'true')
        form.set('fallback', 'true')
        resp = form.request(session,
                            method='POST',
                            action='/rest/auth/login',
                            headers={'X-CSRF-Token': csrftoken})
        resp.raise_for_status()
        # result
        if '/auth' not in resp.url:
            log.success('You signed in.')
        else:
            log.failure('You failed to sign in. Wrong user ID or password.')
            raise LoginError(
                'You failed to sign in. Wrong user ID or password.')
Пример #17
0
def construct_relationship_of_files(paths: List[pathlib.Path], directory: pathlib.Path, format: str) -> Dict[str, Dict[str, pathlib.Path]]:
    tests = collections.defaultdict(dict)  # type: Dict[str, Dict[str, pathlib.Path]]
    for path in paths:
        m = match_with_format(directory, format, path.resolve())
        if not m:
            log.error('unrecognizable file found: %s', path)
            sys.exit(1)
        name = m.groupdict()['name']
        ext = m.groupdict()['ext']
        assert ext not in tests[name]
        tests[name][ext] = path
    for name in tests:
        if 'in' not in tests[name]:
            assert 'out' in tests[name]
            log.error('dangling output case: %s', tests[name]['out'])
            sys.exit(1)
    if not tests:
        log.error('no cases found')
        sys.exit(1)
    log.info('%d cases found', len(tests))
    return tests
Пример #18
0
def split_input(args: 'argparse.Namespace') -> None:
    with open(args.input) as fh:
        inf = fh.read()
    if args.footer == split_input_auto_footer:
        args.footer = inf.splitlines(keepends=True)[-1]
    with subprocess.Popen(args.command,
                          shell=True,
                          stdin=subprocess.PIPE,
                          stdout=subprocess.PIPE,
                          stderr=sys.stderr) as proc:
        index = 0
        acc = ''
        for line in inf.splitlines(keepends=True):
            if args.ignore:
                args.ignore -= 1
            else:
                acc += line
            proc.stdin.write(line.encode())
            proc.stdin.flush()
            time.sleep(args.time)
            if non_block_read(proc.stdout):  # if output exists
                index += 1
                path = format_utils.percentformat(args.output,
                                                  {'i': str(index)})
                log.info('case found: %d', index)
                if args.header:
                    if args.header == args.header.strip():
                        acc = '\n' + acc
                    acc = args.header + acc
                if args.footer:
                    acc = acc + args.footer
                log.emit(log.bold(acc))
                with open(path, 'w') as fh:
                    fh.write(acc)
                log.success('saved to: %s', path)
                acc = ''
                while non_block_read(proc.stdout):  # consume all
                    pass
Пример #19
0
    def download_sample_cases(
        self,
        session: Optional[requests.Session] = None
    ) -> List[onlinejudge.type.TestCase]:
        """
        :raises Exception: if no such problem exists
        """

        session = session or utils.new_default_session()

        # get
        resp = _request('GET',
                        self.get_url(type='beta'),
                        raise_for_status=False,
                        session=session)
        if _list_alert(resp):
            log.warning('are you logged in?')
        resp.raise_for_status()

        # parse
        soup = bs4.BeautifulSoup(resp.content.decode(resp.encoding),
                                 utils.html_parser)
        samples = onlinejudge._implementation.testcase_zipper.SampleZipper()
        lang = None
        for pre, h3 in self._find_sample_tags(soup):
            s = utils.textfile(utils.dos2unix(pre.string.lstrip()))
            name = h3.string
            l = self._get_tag_lang(pre)
            if lang is None:
                lang = l
            elif lang != l:
                log.info(
                    'skipped due to language: current one is %s, not %s: %s ',
                    lang, l, name)
                continue
            samples.add(s.encode(), name)
        return samples.get()
def generate_output_single_case(test_name: str, test_input_path: pathlib.Path, *, lock: Optional[threading.Lock] = None, args: 'argparse.Namespace') -> None:

    # print the header
    if lock is None:
        log.emit('')
        log.info('%s', test_name)

    # run the command
    with test_input_path.open() as inf:
        info, proc = utils.exec_command(args.command, stdin=inf, timeout=args.tle)
        answer = info['answer']  # type: Optional[bytes]
        elapsed = info['elapsed']  # type: float

    # acquire lock to print logs properly, if in parallel
    nullcontext = contextlib.ExitStack()
    with lock or nullcontext:
        if lock is not None:
            log.emit('')
            log.info('%s', test_name)

        # check the result
        log.status('time: %f sec', elapsed)
        if proc.returncode is None:
            log.failure(log.red('TLE'))
            log.info('skipped.')
            return
        elif proc.returncode != 0:
            log.failure(log.red('RE') + ': return code %d', proc.returncode)
            log.info('skipped.')
            return
        assert answer is not None
        log.emit(utils.snip_large_file_content(answer, limit=40, head=20, tail=10, bold=True))

        # find the destination path
        match_result = fmtutils.match_with_format(args.directory, args.format, test_input_path)  # type: Optional[Match[Any]]
        if match_result is not None:
            matched_name = match_result.groupdict()['name']  # type: str
        else:
            assert False
        test_output_path = fmtutils.path_from_format(args.directory, args.format, name=matched_name, ext='out')

        # write the result to the file
        if not test_output_path.parent.is_dir():
            os.makedirs(str(test_output_path.parent), exist_ok=True)
        with test_output_path.open('wb') as fh:
            fh.write(answer)
        log.success('saved to: %s', test_output_path)
Пример #21
0
    def login_with_github(
            self,
            get_credentials: onlinejudge.type.CredentialsProvider,
            session: Optional[requests.Session] = None) -> None:
        """
        :raise LoginError:
        """

        session = session or utils.get_default_session()
        url = 'https://yukicoder.me/auth/github'
        # get
        resp = utils.request('GET', url, session=session)
        if urllib.parse.urlparse(resp.url).hostname == 'yukicoder.me':
            log.info('You have already signed in.')
            return
        # redirect to github.com
        # parse
        soup = bs4.BeautifulSoup(resp.content.decode(resp.encoding),
                                 utils.html_parser)
        form = soup.find('form')
        if not form:
            log.error('form not found')
            raise LoginError('something wrong')
        log.debug('form: %s', str(form))
        # post
        username, password = get_credentials()
        form = utils.FormSender(form, url=resp.url)
        form.set('login', username)
        form.set('password', password)
        resp = form.request(session)
        resp.raise_for_status()
        if urllib.parse.urlparse(resp.url).hostname == 'yukicoder.me':
            log.success('You signed in.')
        else:
            log.failure('You failed to sign in. Wrong user ID or password.')
            raise LoginError
Пример #22
0
    def login(self,
              get_credentials: onlinejudge.type.CredentialsProvider,
              session: Optional[requests.Session] = None) -> None:
        """
        :raises LoginError:
        """
        session = session or utils.new_default_session()
        url = 'https://toph.co/login'
        # get
        resp = utils.request('GET', url, session=session)
        if resp.url != url:  # redirected
            log.info('You are already logged in.')
            return
        # parse
        soup = bs4.BeautifulSoup(resp.content.decode(resp.encoding),
                                 utils.html_parser)
        form = soup.find('form', class_='login-form')
        log.debug('form: %s', str(form))
        username, password = get_credentials()
        form[
            'action'] = '/login'  # to avoid KeyError inside form.request method as Toph does not have any defined action
        form = utils.FormSender(form, url=resp.url)
        form.set('handle', username)
        form.set('password', password)
        # post
        resp = form.request(session)
        resp.raise_for_status()

        resp = utils.request(
            'GET', url, session=session
        )  # Toph's Location header is not getting the expected value
        if resp.url != url:
            log.success('Welcome, %s.', username)
        else:
            log.failure('Invalid handle/email or password.')
            raise LoginError('Invalid handle/email or password.')
def generate_output_single_case_exists_ok(test_name: str, test_input_path: pathlib.Path, test_output_path: Optional[pathlib.Path], *, lock: Optional[threading.Lock] = None, args: 'argparse.Namespace') -> None:
    if test_output_path is not None:
        nullcontext = contextlib.ExitStack()
        with lock or nullcontext:
            log.emit('')
            log.info('%s', test_name)
            log.info('output file already exists.')
            log.info('skipped.')
    else:
        generate_output_single_case(test_name, test_input_path, lock=lock, args=args)
Пример #24
0
def login(args: 'argparse.Namespace') -> None:
    # get service
    service = onlinejudge.dispatch.service_from_url(args.url)
    if service is None:
        sys.exit(1)

    # configure
    kwargs = {}
    if isinstance(service, onlinejudge.service.yukicoder.YukicoderService):
        if not args.method:
            args.method = 'github'
        if args.method not in ['github', 'twitter']:
            log.failure('login for yukicoder: invalid option: --method %s', args.method)
            sys.exit(1)
        kwargs['method'] = args.method
    else:
        if args.method:
            log.failure('login for %s: invalid option: --method %s', service.get_name(), args.method)
            sys.exit(1)

    with utils.with_cookiejar(utils.new_session_with_our_user_agent(), path=args.cookie) as sess:

        if args.check:
            if service.is_logged_in(session=sess):
                log.info('You have already signed in.')
            else:
                log.info('You are not signed in.')
                sys.exit(1)

        else:
            # login
            def get_credentials() -> Tuple[str, str]:
                if args.username is None:
                    args.username = input('Username: '******'If you don\'t want to give your password to this program, you can give only your session tokens.')
            log.info('see: https://github.com/kmyk/online-judge-tools/blob/master/LOGIN_WITH_COOKIES.md')

            try:
                service.login(get_credentials, session=sess, **kwargs)  # type: ignore
            except onlinejudge.type.LoginError:
                pass
Пример #25
0
def submit(args: 'argparse.Namespace') -> None:
    # guess url
    history = onlinejudge._implementation.download_history.DownloadHistory()
    if args.file.parent.resolve() == pathlib.Path.cwd():
        guessed_urls = history.get()
    else:
        log.warning(
            'cannot guess URL since the given file is not in the current directory'
        )
        guessed_urls = []
    if args.url is None:
        if len(guessed_urls) == 1:
            args.url = guessed_urls[0]
            log.info('guessed problem: %s', args.url)
        else:
            log.error('failed to guess the URL to submit')
            log.info('please manually specify URL as: $ oj submit URL FILE')
            sys.exit(1)

    # parse url
    problem = onlinejudge.dispatch.problem_from_url(args.url)
    if problem is None:
        sys.exit(1)

    # read code
    with args.file.open('rb') as fh:
        code = fh.read()  # type: bytes
    format_config = {
        'dos2unix': args.format_dos2unix or args.golf,
        'rstrip': args.format_dos2unix or args.golf,
    }
    code = format_code(code, **format_config)

    # report code
    log.info('code (%d byte):', len(code))
    log.emit(
        utils.snip_large_file_content(code,
                                      limit=30,
                                      head=10,
                                      tail=10,
                                      bold=True))

    with utils.with_cookiejar(utils.new_session_with_our_user_agent(),
                              path=args.cookie) as sess:
        # guess or select language ids
        langs = {
            language.id: {
                'description': language.name
            }
            for language in problem.get_available_languages(session=sess)
        }  # type: Dict[LanguageId, Dict[str, str]]
        matched_lang_ids = None  # type: Optional[List[str]]
        if args.language in langs:
            matched_lang_ids = [args.language]
        else:
            if args.guess:
                kwargs = {
                    'language_dict': langs,
                    'cxx_latest': args.guess_cxx_latest,
                    'cxx_compiler': args.guess_cxx_compiler,
                    'python_version': args.guess_python_version,
                    'python_interpreter': args.guess_python_interpreter,
                }
                matched_lang_ids = guess_lang_ids_of_file(
                    args.file, code, **kwargs)
                if not matched_lang_ids:
                    log.info('failed to guess languages from the file name')
                    matched_lang_ids = list(langs.keys())
                if args.language is not None:
                    log.info(
                        'you can use `--no-guess` option if you want to do an unusual submission'
                    )
                    matched_lang_ids = select_ids_of_matched_languages(
                        args.language.split(),
                        matched_lang_ids,
                        language_dict=langs)
            else:
                if args.language is None:
                    matched_lang_ids = None
                else:
                    matched_lang_ids = select_ids_of_matched_languages(
                        args.language.split(),
                        list(langs.keys()),
                        language_dict=langs)

        # report selected language ids
        if matched_lang_ids is not None and len(matched_lang_ids) == 1:
            args.language = matched_lang_ids[0]
            log.info('chosen language: %s (%s)', args.language,
                     langs[LanguageId(args.language)]['description'])
        else:
            if matched_lang_ids is None:
                log.error('language is unknown')
                log.info('supported languages are:')
            elif len(matched_lang_ids) == 0:
                log.error('no languages are matched')
                log.info('supported languages are:')
            else:
                log.error('Matched languages were not narrowed down to one.')
                log.info('You have to choose:')
            for lang_id in sorted(matched_lang_ids or langs.keys()):
                log.emit('%s (%s)', lang_id,
                         langs[LanguageId(lang_id)]['description'])
            sys.exit(1)

        # confirm
        guessed_unmatch = ([problem.get_url()] != guessed_urls)
        if guessed_unmatch:
            samples_text = ('samples of "{}'.format('", "'.join(guessed_urls))
                            if guessed_urls else 'no samples')
            log.warning(
                'the problem "%s" is specified to submit, but %s were downloaded in this directory. this may be mis-operation',
                problem.get_url(), samples_text)
        if args.wait:
            log.status('sleep(%.2f)', args.wait)
            time.sleep(args.wait)
        if not args.yes:
            if guessed_unmatch:
                problem_id = problem.get_url().rstrip('/').split(
                    '/')[-1].split('?')[-1]  # this is too ad-hoc
                key = problem_id[:3] + (problem_id[-1]
                                        if len(problem_id) >= 4 else '')
                sys.stdout.write('Are you sure? Please type "{}" '.format(key))
                sys.stdout.flush()
                c = sys.stdin.readline().rstrip()
                if c != key:
                    log.info('terminated.')
                    return
            else:
                sys.stdout.write('Are you sure? [y/N] ')
                sys.stdout.flush()
                c = sys.stdin.read(1)
                if c.lower() != 'y':
                    log.info('terminated.')
                    return

        # submit
        kwargs = {}
        if isinstance(problem,
                      onlinejudge.service.topcoder.TopcoderLongContestProblem):
            if args.full_submission:
                kwargs['kind'] = 'full'
            else:
                kwargs['kind'] = 'example'
        try:
            submission = problem.submit_code(code,
                                             language_id=LanguageId(
                                                 args.language),
                                             session=sess,
                                             **kwargs)
        except NotLoggedInError:
            log.failure('login required')
            sys.exit(1)
        except SubmissionError:
            log.failure('submission failed')
            sys.exit(1)

        # show result
        if args.open:
            browser = webbrowser.get()
            log.status('open the submission page with: %s', browser.name)
            opened = browser.open_new_tab(submission.get_url())
            if not opened:
                log.failure(
                    'failed to open the url. please set the $BROWSER envvar')
Пример #26
0
def test(args: 'argparse.Namespace') -> None:
    # prepare
    if not args.test:
        args.test = cutils.glob_with_format(args.directory, args.format)  # by default
    if args.ignore_backup:
        args.test = cutils.drop_backup_or_hidden_files(args.test)
    tests = cutils.construct_relationship_of_files(args.test, args.directory, args.format)
    if args.error:  # float mode
        match = lambda a, b: compare_as_floats(a, b, args.error)
    else:

        def match(a, b):
            if a == b:
                return True
            if args.rstrip and a.rstrip(rstrip_targets) == b.rstrip(rstrip_targets):
                log.warning('WA if no rstrip')
                return True
            return False

    rstrip_targets = ' \t\r\n\f\v\0'  # ruby's one, follow AnarchyGolf
    slowest = -1  # type: Union[int, float]
    slowest_name = ''
    ac_count = 0

    history = []  # type: List[Dict[str, Any]]
    for name, it in sorted(tests.items()):
        is_input_printed = False

        def print_input():
            nonlocal is_input_printed
            if args.print_input and not is_input_printed:
                is_input_printed = True
                with open(it['in'], 'rb') as inf:
                    log.emit('input:\n%s', utils.snip_large_file_content(inf.read(), limit=40, head=20, tail=10, bold=True))

        log.emit('')
        log.info('%s', name)

        # run the binary
        with it['in'].open() as inf:
            begin = time.perf_counter()
            answer_byte, proc = utils.exec_command(args.command, shell=True, stdin=inf, timeout=args.tle)
            end = time.perf_counter()
            elapsed = end - begin
            answer = answer_byte.decode()  # TODO: the `answer` should be bytes, not str
            if slowest < elapsed:
                slowest = elapsed
                slowest_name = name
            log.status('time: %f sec', elapsed)
            proc.terminate()

        # check TLE, RE or not
        result = 'AC'
        if proc.returncode is None:
            log.failure(log.red('TLE'))
            result = 'TLE'
            print_input()
        elif proc.returncode != 0:
            log.failure(log.red('RE') + ': return code %d', proc.returncode)
            result = 'RE'
            print_input()

        # check WA or not
        if 'out' in it:
            with it['out'].open() as outf:
                correct = outf.read()
            # compare
            if args.mode == 'all':
                if not match(answer, correct):
                    log.failure(log.red('WA'))
                    print_input()
                    if not args.silent:
                        log.emit('output:\n%s', utils.snip_large_file_content(answer.encode(), limit=40, head=20, tail=10, bold=True))
                        log.emit('expected:\n%s', utils.snip_large_file_content(correct.encode(), limit=40, head=20, tail=10, bold=True))
                    result = 'WA'
            elif args.mode == 'line':
                answer_words = answer.splitlines()
                correct_words = correct.splitlines()
                for i, (x, y) in enumerate(zip(answer_words + [None] * len(correct_words), correct_words + [None] * len(answer_words))):  # type: ignore
                    if x is None and y is None:
                        break
                    elif x is None:
                        print_input()
                        log.failure(log.red('WA') + ': line %d: line is nothing: expected "%s"', i + 1, log.bold(y))
                        result = 'WA'
                    elif y is None:
                        print_input()
                        log.failure(log.red('WA') + ': line %d: unexpected line: output "%s"', i + 1, log.bold(x))
                        result = 'WA'
                    elif not match(x, y):
                        print_input()
                        log.failure(log.red('WA') + ': line %d: output "%s": expected "%s"', i + 1, log.bold(x), log.bold(y))
                        result = 'WA'
            else:
                assert False
        else:
            if not args.silent:
                log.emit(('output:\n%s' if is_input_printed else '%s'), utils.snip_large_file_content(answer.encode(), limit=40, head=20, tail=10, bold=True))
        if result == 'AC':
            log.success(log.green('AC'))
            ac_count += 1

        # push the result
        testcase = {
            'name': name,
            'input': str(it['in'].resolve()),
        }
        if 'out' in it:
            testcase['output'] = str(it['out'].resolve())
        history += [{
            'result': result,
            'testcase': testcase,
            'output': answer,
            'exitcode': proc.returncode,
            'elapsed': elapsed,
        }]

    # summarize
    log.emit('')
    log.status('slowest: %f sec  (for %s)', slowest, slowest_name)
    if ac_count == len(tests):
        log.success('test ' + log.green('success') + ': %d cases', len(tests))
    else:
        log.failure('test ' + log.red('failed') + ': %d AC / %d cases', ac_count, len(tests))

    if args.json:
        print(json.dumps(history))

    if ac_count != len(tests):
        sys.exit(1)