Mercurial > ~astiob > upreckon > hgweb
view config.py @ 76:0e5ae28e0b2b
Points are now weighted on a test context basis
In particular, this has allowed for simple extensions to the format
of testconf to award points to whole test groups without at the same time
compromising the future ability of giving partial score for correct
but slow solutions. Specifically, the groupweight configuration variable
has been added and normally has the format {groupindex: points} where
groupindex is the group's index in the tests configuration variable.
The backwards incompatible change is that test contexts are no longer
guaranteed to learn the score awarded or the maximum possible score
for every test case and may instead be notified about them in batches.
In other news, the pointmap and groupweight configuration variables can
(now) be given as sequences in addition to mappings. (Technically,
the distinction currently made is dict versus everything else.) Items
of a sequence pointmap/groupweight correspond directly to the test cases/
groups defined in the tests configuration variable; in particular,
when groups are used, tests=[1],[2,3];pointmap={1:1,2:2,3:3} can now be
written as pointmap=tests=[1],[2,3]. Missing items are handled in the same
way in which they are handled when the variable is a mapping. Note
that the items of groupweight correspond to whole test groups rather
than individual test cases.
In other news again, the wording of problem total lines has been changed
from '<unweighted> points; weighted score: <weighted>' to '<weighted>
points (<unweighted> before weighting)', and group total lines now
properly report fractional numbers of points (this is a bug fix).
author | Oleg Oshmyan <chortos@inbox.lv> |
---|---|
date | Sat, 08 Jan 2011 16:03:35 +0200 |
parents | aea4fc87698a |
children | d46bd7ee3e69 |
line wrap: on
line source
#! /usr/bin/env python # Copyright (c) 2010 Chortos-2 <chortos@inbox.lv> from __future__ import division, with_statement try: from compat import * import files except ImportError: import __main__ __main__.import_error(sys.exc_info()[1]) else: from __main__ import options if files.ZipArchive: try: import zipimport except ImportError: zipimport = None else: zipimport = None import imp, os, sys, tempfile __all__ = 'load_problem', 'load_global', 'globalconf' defaults_problem = {'kind': 'batch', 'usegroups': False, 'maxtime': None, 'maxmemory': None, 'dummies': {}, 'testsexcluded': (), 'padtests': 0, 'paddummies': 0, 'taskweight': 100, 'groupweight': {}, 'pointmap': {}, 'stdio': False, 'dummyinname': '', 'dummyoutname': '', 'tester': None, 'maxexitcode': 0, 'inname': '', 'ansname': ''} defaults_global = {'tasknames': None, 'force_zero_exitcode': True} defaults_noerase = {'inname': '%.in', 'outname': '%.out', 'ansname': '%.ans'} patterns = ('inname', 'outname', 'ansname', 'testcaseinname', 'testcaseoutname', 'dummyinname', 'dummyoutname') class Config(object): __slots__ = 'modules', '__dict__' def __init__(self, *modules): self.modules = modules def __getattr__(self, name): for module in self.modules: try: return getattr(module, name) except AttributeError: pass # TODO: provide a message raise AttributeError(name) # A helper context manager class ReadDeleting(object): __slots__ = 'name', 'file' def __init__(self, name): self.name = name def __enter__(self): try: self.file = open(self.name, 'rU') return self.file except: try: self.__exit__(None, None, None) except: pass raise def __exit__(self, exc_type, exc_val, exc_tb): self.file.close() os.remove(self.name) def load_problem(problem_name): global builtins dwb = sys.dont_write_bytecode sys.dont_write_bytecode = True metafile = files.File('/'.join((problem_name, 'testconf.py')), True, 'configuration') module = None with CompatBuiltins() as builtins: if zipimport and isinstance(metafile.archive, files.ZipArchive): try: module = zipimport.zipimporter(os.path.dirname(metafile.full_real_path)).load_module('testconf') except zipimport.ZipImportError: pass else: del sys.modules['testconf'] if not module: try: with metafile.open() as f: module = imp.load_module('testconf', f, metafile.full_real_path, ('.py', 'r', imp.PY_SOURCE)) # Handle the case when f is not a true file object but imp requires one except ValueError: # FIXME: 2.5 lacks the delete parameter with tempfile.NamedTemporaryFile(delete=False) as f: inputdatafname = f.name metafile.copy(inputdatafname) with ReadDeleting(inputdatafname) as f: module = imp.load_module('testconf', f, metafile.full_real_path, ('.py', 'r', imp.PY_SOURCE)) del sys.modules['testconf'] if hasattr(module, 'padwithzeroestolength'): if not hasattr(module, 'padtests'): try: module.padtests = module.padwithzeroestolength[0] except TypeError: module.padtests = module.padwithzeroestolength if not hasattr(module, 'paddummies'): try: module.paddummies = module.padwithzeroestolength[1] except TypeError: module.paddummies = module.padwithzeroestolength for name in defaults_problem: if not hasattr(globalconf, name): setattr(module, name, getattr(module, name, defaults_problem[name])) module = Config(module, globalconf) if not module.dummyinname: module.dummyinname = getattr(module, 'testcaseinname', module.dummyinname) if not module.dummyoutname: module.dummyoutname = getattr(module, 'testcaseoutname', module.dummyoutname) if not hasattr(module, 'path'): if hasattr(module, 'name'): module.path = module.name elif sys.platform != 'win32': module.path = os.path.join(os.path.curdir, problem_name) else: module.path = problem_name for name in 'pointmap', 'groupweight': oldmap = getattr(module, name) if isinstance(oldmap, dict): newmap = {} for key in oldmap: if not options.legacy and isinstance(key, basestring): newmap[key] = oldmap[key] else: try: for k in key: newmap[k] = oldmap[key] except TypeError: newmap[key] = oldmap[key] setattr(module, name, newmap) if options.no_maxtime: module.maxtime = 0 sys.dont_write_bytecode = dwb for name in patterns: if hasattr(module, name): setattr(module, name, getattr(module, name).replace('%', problem_name)) return module def load_global(): global builtins dwb = sys.dont_write_bytecode sys.dont_write_bytecode = True metafile = files.File('testconf.py', True, 'configuration') module = None with CompatBuiltins() as builtins: if zipimport and isinstance(metafile.archive, files.ZipArchive): try: module = zipimport.zipimporter(os.path.dirname(metafile.full_real_path)).load_module('testconf') except zipimport.ZipImportError: pass else: del sys.modules['testconf'] if not module: try: with metafile.open() as f: module = imp.load_module('testconf', f, metafile.full_real_path, ('.py', 'r', imp.PY_SOURCE)) # Handle the case when f is not a true file object but imp requires one except ValueError: # FIXME: 2.5 lacks the delete parameter with tempfile.NamedTemporaryFile(delete=False) as f: inputdatafname = f.name metafile.copy(inputdatafname) with ReadDeleting(inputdatafname) as f: module = imp.load_module('testconf', f, metafile.full_real_path, ('.py', 'r', imp.PY_SOURCE)) del sys.modules['testconf'] for name in defaults_global: setattr(module, name, getattr(module, name, defaults_global[name])) if not options.erase: for name in defaults_noerase: setattr(module, name, getattr(module, name, defaults_noerase[name])) global globalconf globalconf = module sys.dont_write_bytecode = dwb return module