# HG changeset patch # User Oleg Oshmyan # Date 1313623224 -10800 # Node ID ede78fbd509a05f1492fad3db620fcb015eb39de # Parent 946e8c09ba1253400e5d8a225dfa177bef4d2bf8 Revamped files.File This changes (simplifies) file search path: * when searching for testconf, .../testconf.py is now given precendence over .../tests/testconf.py; * when searching inside archives, a/tests/b/c is now given precedence over a/c. diff -r 946e8c09ba12 -r ede78fbd509a upreckon/config.py --- a/upreckon/config.py Thu Aug 18 01:19:15 2011 +0300 +++ b/upreckon/config.py Thu Aug 18 02:20:24 2011 +0300 @@ -114,7 +114,7 @@ sys.dont_write_bytecode = True except AttributeError: pass - metafile = files.File(problem_name + '/testconf.py', True, 'configuration') + metafile = files.File.from_virtual_path(problem_name + '/testconf.py', True, 'configuration') module = None with CompatBuiltins() as builtins: if zipimport and isinstance(metafile.archive, files.ZipArchive): @@ -225,7 +225,7 @@ sys.dont_write_bytecode = True except AttributeError: pass - metafile = files.File('testconf.py', True, 'configuration') + metafile = files.File.from_virtual_path('testconf.py', True, 'configuration') module = None with CompatBuiltins() as builtins: if zipimport and isinstance(metafile.archive, files.ZipArchive): diff -r 946e8c09ba12 -r ede78fbd509a upreckon/files.py --- a/upreckon/files.py Thu Aug 18 01:19:15 2011 +0300 +++ b/upreckon/files.py Thu Aug 18 02:20:24 2011 +0300 @@ -209,83 +209,119 @@ return archive class File(object): - __slots__ = 'virtual_path', 'real_path', 'full_real_path', 'archive' - - def __init__(self, virtpath, allow_root=False, msg='test data'): - self.virtual_path = virtpath - self.archive = None - if not self.realize_path('', tuple(comp.replace('.', os.path.extsep) for comp in virtpath.split('/')), allow_root): - raise IOError("%s file '%s' could not be found" % (msg, virtpath)) + __slots__ = ('virtual_path', 'archive', + '_external_path', '_internal_path', '_has_tests') - def realize_path(self, root, virtpath, allow_root=False, hastests=False): - if root and not os.path.exists(root): - return False - if len(virtpath) > 1: - if self.realize_path(os.path.join(root, virtpath[0]), virtpath[1:], allow_root, hastests): - return True - elif not hastests: - if self.realize_path(os.path.join(root, 'tests'), virtpath, allow_root, True): - return True - for archive in archives: - path = os.path.join(root, archive) - if os.path.exists(path): - if self.realize_path_archive(open_archive(path), '', virtpath, path): - return True - if self.realize_path(root, virtpath[1:], allow_root, hastests): - return True + def __init__(self, _virtual_path='', _external_path='', _internal_path='', + _archive=None, _has_tests=False): + self.virtual_path = _virtual_path + self._external_path = _external_path + self._internal_path = _internal_path + self.archive = _archive + self._has_tests = _has_tests + if not _archive: + try: + self.archive = open_archive(_external_path) + except Exception: + pass + + @property + def full_real_path(self): + intpath = self._internal_path.split('/') if self._internal_path else () + return os.path.join(self._external_path, + *(filename.replace('.', os.path.extsep) + for filename in intpath)) + + def exists(self): + if self.archive: + return self.archive.exists(self._internal_path) else: - if not hastests: - path = os.path.join(root, 'tests', virtpath[0]) - if os.path.exists(path): - self.full_real_path = self.real_path = path - return True - for archive in archives: - path = os.path.join(root, archive) - if os.path.exists(path): - if self.realize_path_archive(open_archive(path), '', virtpath, path): - return True - if hastests or allow_root: - path = os.path.join(root, virtpath[0]) - if os.path.exists(path): - self.full_real_path = self.real_path = path - return True - return False - - def realize_path_archive(self, archive, root, virtpath, archpath, hastests=False): - if root and not archive.exists(root): - return False - path = posixpath.join(root, virtpath[0]) - if len(virtpath) > 1: - if self.realize_path_archive(archive, path, virtpath[1:], archpath): - return True - elif self.realize_path_archive(archive, root, virtpath[1:], archpath): - return True - else: - if archive.exists(path): - self.archive = archive - self.real_path = path - self.full_real_path = os.path.join(archpath, *path.split('/')) - return True - if not hastests: - if self.realize_path_archive(archive, posixpath.join(root, 'tests'), virtpath, archpath, True): - return True - return False + return (not self._external_path or + os.path.exists(self._external_path)) def open(self): if self.archive: - file = self.archive.open(self.real_path) + file = self.archive.open(self._internal_path) if hasattr(file, '__exit__'): return file else: return contextlib.closing(file) else: - return open(self.real_path, 'rb') + return open(self._external_path, 'rb') def copy(self, target): if self.archive: - self.archive.extract(self.real_path, target) + self.archive.extract(self._internal_path, target) + else: + shutil.copy(self._external_path, target) + + def listdir(self): + if self.archive: + return self.archive.listdir(self._internal_path) else: - shutil.copy(self.real_path, target) + return os.listdir(self._external_path) + + @classmethod + def from_virtual_path(cls, virtual_path, allow_root, msg): + metafile = cls()._realize_path(virtual_path.split('/'), allow_root) + if not metafile: + raise IOError("%s file with virtual path %r could not be found" % + (msg, virtual_path)) + assert metafile.virtual_path == virtual_path + return metafile + + def _realize_path(self, virtpath, allow_root): + if not self.exists(): + return None + elif not virtpath: + if allow_root or self._has_tests or self.archive: + return self + return None + cand = (self + virtpath[0])._realize_path(virtpath[1:], allow_root) + if cand: return cand + if not cand and not self._has_tests: + for metafile in self._add_tests(): + cand = metafile._realize_path(virtpath, allow_root) + if cand: return cand + if not cand and len(virtpath) > 1: + metafile = self._add_virtual(virtpath[0]) + cand = metafile._realize_path(virtpath[1:], allow_root) + if cand: return cand + + def _add_tests(self): + assert not self._has_tests + metafile = self.__add__('tests', False) + metafile._has_tests = True + yield metafile + if not self.archive: + for filename in archives: + yield self.__add__(filename, False) + + def _add_virtual(self, filename): + return File(posixpath.join(self.virtual_path, filename), + self._external_path, + self._internal_path, + self.archive, + self._has_tests) + + def __add__(self, filename, add_virtual=True): + if not isinstance(filename, basestring): + return NotImplemented + if add_virtual: + virtual_path = posixpath.join(self.virtual_path, filename) + else: + virtual_path = self.virtual_path + if self.archive: + return File(virtual_path, + self._external_path, + posixpath.join(self._internal_path, filename), + self.archive, + self._has_tests) + else: + filename = filename.replace('.', os.path.extsep) + return File(virtual_path, + os.path.join(self._external_path, filename), + _has_tests=self._has_tests) class RegexpMatchFile(object): __slots__ = 'virtual_path', 'real_path', 'hastests', 'archive' diff -r 946e8c09ba12 -r ede78fbd509a upreckon/testcases.py --- a/upreckon/testcases.py Thu Aug 18 01:19:15 2011 +0300 +++ b/upreckon/testcases.py Thu Aug 18 02:20:24 2011 +0300 @@ -146,14 +146,14 @@ def open_infile(case): try: - case.infile = files.File('/'.join((case.problem.name, case.realinname.replace('$', case.id)))) + case.infile = files.File.from_virtual_path('/'.join((case.problem.name, case.realinname.replace('$', case.id))), False, 'test data') except IOError: e = sys.exc_info()[1] raise CannotReadInputFile(e) def open_outfile(case): try: - case.outfile = files.File('/'.join((case.problem.name, case.realoutname.replace('$', case.id)))) + case.outfile = files.File.from_virtual_path('/'.join((case.problem.name, case.realoutname.replace('$', case.id))), False, 'test data') except IOError: e = sys.exc_info()[1] raise CannotReadAnswerFile(e)