# Python Version: 3.x import collections import glob import os import pathlib import re import sys from typing import Dict, Generator, List, Match, Optional, Set import onlinejudge_command.logging as log def percentsplit(s: str) -> Generator[str, None, None]: for m in re.finditer('[^%]|%(.)', s): yield m.group(0) def percentformat(s: str, table: Dict[str, str]) -> str: assert '%' not in table or table['%'] == '%' table['%'] = '%' result = '' for c in percentsplit(s): if c.startswith('%'): result += table[c[1]] else: result += c return result def percentparse(s: str, format: str, table: Dict[str, str]) -> Optional[Dict[str, str]]: table = {key: '(?P<{}>{})'.format(key, value) for key, value in table.items()} used = set() # type: Set[str] pattern = '' for token in percentsplit(re.escape(format).replace('\\%', '%')): if token.startswith('%'): c = token[1] if c not in used: pattern += table[c] used.add(c) else: pattern += r'(?P={})'.format(c) else: pattern += token m = re.match(pattern, s) if not m: return None return m.groupdict() 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 match_with_format(directory: pathlib.Path, format: str, path: pathlib.Path) -> Optional[Match[str]]: if os.name == 'nt': format = format.replace('/', '\\') table = {} table['s'] = '(?P<name>.+)' table['e'] = '(?P<ext>in|out)' pattern = re.compile(re.escape(str(directory.resolve()) + os.path.sep) + percentformat(re.escape(format).replace(re.escape('%'), '%'), table)) return pattern.match(str(path.resolve())) def path_from_format(directory: pathlib.Path, format: str, name: str, ext: str) -> pathlib.Path: table = {} table['s'] = name table['e'] = ext return directory / percentformat(format, table) def is_backup_or_hidden_file(path: pathlib.Path) -> bool: basename = path.name return basename.endswith('~') or (basename.startswith('#') and basename.endswith('#')) or basename.startswith('.') def drop_backup_or_hidden_files(paths: List[pathlib.Path]) -> List[pathlib.Path]: result = [] # type: List[pathlib.Path] for path in paths: if is_backup_or_hidden_file(path): log.warning('ignore a backup file: %s', path) else: result += [path] return result 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