Mercurial > ~astiob > upreckon > hgweb
annotate 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 |
rev | line source |
---|---|
21 | 1 #! /usr/bin/env python |
16 | 2 # Copyright (c) 2010 Chortos-2 <chortos@inbox.lv> |
3 | |
21 | 4 from __future__ import division, with_statement |
5 | |
6 try: | |
25
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
7 from compat import * |
21 | 8 import files |
9 except ImportError: | |
10 import __main__ | |
11 __main__.import_error(sys.exc_info()[1]) | |
12 else: | |
13 from __main__ import options | |
14 | |
15 if files.ZipArchive: | |
16 try: | |
17 import zipimport | |
18 except ImportError: | |
19 zipimport = None | |
20 else: | |
21 zipimport = None | |
22 | |
22 | 23 import imp, os, sys, tempfile |
21 | 24 |
25 __all__ = 'load_problem', 'load_global', 'globalconf' | |
26 | |
74 | 27 defaults_problem = {'kind': 'batch', |
28 'usegroups': False, | |
21 | 29 'maxtime': None, |
30 'maxmemory': None, | |
31 'dummies': {}, | |
32 'testsexcluded': (), | |
33 'padtests': 0, | |
34 'paddummies': 0, | |
35 'taskweight': 100, | |
76
0e5ae28e0b2b
Points are now weighted on a test context basis
Oleg Oshmyan <chortos@inbox.lv>
parents:
74
diff
changeset
|
36 'groupweight': {}, |
21 | 37 'pointmap': {}, |
38 'stdio': False, | |
39 'dummyinname': '', | |
40 'dummyoutname': '', | |
41 'tester': None, | |
42 'maxexitcode': 0, | |
43 'inname': '', | |
44 'ansname': ''} | |
22 | 45 defaults_global = {'tasknames': None, |
46 'force_zero_exitcode': True} | |
43 | 47 defaults_noerase = {'inname': '%.in', |
48 'outname': '%.out', | |
49 'ansname': '%.ans'} | |
21 | 50 patterns = ('inname', 'outname', 'ansname', 'testcaseinname', |
51 'testcaseoutname', 'dummyinname', 'dummyoutname') | |
52 | |
53 class Config(object): | |
54 __slots__ = 'modules', '__dict__' | |
55 | |
56 def __init__(self, *modules): | |
57 self.modules = modules | |
58 | |
59 def __getattr__(self, name): | |
60 for module in self.modules: | |
61 try: | |
62 return getattr(module, name) | |
63 except AttributeError: | |
64 pass | |
65 # TODO: provide a message | |
66 raise AttributeError(name) | |
16 | 67 |
22 | 68 # A helper context manager |
69 class ReadDeleting(object): | |
25
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
70 __slots__ = 'name', 'file' |
22 | 71 |
72 def __init__(self, name): | |
73 self.name = name | |
74 | |
75 def __enter__(self): | |
76 try: | |
25
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
77 self.file = open(self.name, 'rU') |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
78 return self.file |
22 | 79 except: |
80 try: | |
81 self.__exit__(None, None, None) | |
82 except: | |
83 pass | |
84 raise | |
85 | |
86 def __exit__(self, exc_type, exc_val, exc_tb): | |
25
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
87 self.file.close() |
22 | 88 os.remove(self.name) |
89 | |
21 | 90 def load_problem(problem_name): |
70
b9d5857f7b9a
Better emulation of built-ins for testconf
Oleg Oshmyan <chortos@inbox.lv>
parents:
60
diff
changeset
|
91 global builtins |
21 | 92 dwb = sys.dont_write_bytecode |
93 sys.dont_write_bytecode = True | |
94 metafile = files.File('/'.join((problem_name, 'testconf.py')), True, 'configuration') | |
95 module = None | |
70
b9d5857f7b9a
Better emulation of built-ins for testconf
Oleg Oshmyan <chortos@inbox.lv>
parents:
60
diff
changeset
|
96 with CompatBuiltins() as builtins: |
25
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
97 if zipimport and isinstance(metafile.archive, files.ZipArchive): |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
98 try: |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
99 module = zipimport.zipimporter(os.path.dirname(metafile.full_real_path)).load_module('testconf') |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
100 except zipimport.ZipImportError: |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
101 pass |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
102 else: |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
103 del sys.modules['testconf'] |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
104 if not module: |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
105 try: |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
106 with metafile.open() as f: |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
107 module = imp.load_module('testconf', f, metafile.full_real_path, ('.py', 'r', imp.PY_SOURCE)) |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
108 # Handle the case when f is not a true file object but imp requires one |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
109 except ValueError: |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
110 # FIXME: 2.5 lacks the delete parameter |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
111 with tempfile.NamedTemporaryFile(delete=False) as f: |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
112 inputdatafname = f.name |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
113 metafile.copy(inputdatafname) |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
114 with ReadDeleting(inputdatafname) as f: |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
115 module = imp.load_module('testconf', f, metafile.full_real_path, ('.py', 'r', imp.PY_SOURCE)) |
21 | 116 del sys.modules['testconf'] |
117 if hasattr(module, 'padwithzeroestolength'): | |
118 if not hasattr(module, 'padtests'): | |
119 try: | |
120 module.padtests = module.padwithzeroestolength[0] | |
121 except TypeError: | |
122 module.padtests = module.padwithzeroestolength | |
123 if not hasattr(module, 'paddummies'): | |
124 try: | |
125 module.paddummies = module.padwithzeroestolength[1] | |
126 except TypeError: | |
127 module.paddummies = module.padwithzeroestolength | |
128 for name in defaults_problem: | |
129 if not hasattr(globalconf, name): | |
130 setattr(module, name, getattr(module, name, defaults_problem[name])) | |
38
a6d554679ce8
Fixed a bug with nested configuration namespaces in config.py
Oleg Oshmyan <chortos@inbox.lv>
parents:
27
diff
changeset
|
131 module = Config(module, globalconf) |
27 | 132 if not module.dummyinname: |
133 module.dummyinname = getattr(module, 'testcaseinname', module.dummyinname) | |
134 if not module.dummyoutname: | |
135 module.dummyoutname = getattr(module, 'testcaseoutname', module.dummyoutname) | |
21 | 136 if not hasattr(module, 'path'): |
137 if hasattr(module, 'name'): | |
138 module.path = module.name | |
139 elif sys.platform != 'win32': | |
140 module.path = os.path.join(os.path.curdir, problem_name) | |
141 else: | |
142 module.path = problem_name | |
76
0e5ae28e0b2b
Points are now weighted on a test context basis
Oleg Oshmyan <chortos@inbox.lv>
parents:
74
diff
changeset
|
143 for name in 'pointmap', 'groupweight': |
0e5ae28e0b2b
Points are now weighted on a test context basis
Oleg Oshmyan <chortos@inbox.lv>
parents:
74
diff
changeset
|
144 oldmap = getattr(module, name) |
0e5ae28e0b2b
Points are now weighted on a test context basis
Oleg Oshmyan <chortos@inbox.lv>
parents:
74
diff
changeset
|
145 if isinstance(oldmap, dict): |
0e5ae28e0b2b
Points are now weighted on a test context basis
Oleg Oshmyan <chortos@inbox.lv>
parents:
74
diff
changeset
|
146 newmap = {} |
0e5ae28e0b2b
Points are now weighted on a test context basis
Oleg Oshmyan <chortos@inbox.lv>
parents:
74
diff
changeset
|
147 for key in oldmap: |
0e5ae28e0b2b
Points are now weighted on a test context basis
Oleg Oshmyan <chortos@inbox.lv>
parents:
74
diff
changeset
|
148 if not options.legacy and isinstance(key, basestring): |
0e5ae28e0b2b
Points are now weighted on a test context basis
Oleg Oshmyan <chortos@inbox.lv>
parents:
74
diff
changeset
|
149 newmap[key] = oldmap[key] |
0e5ae28e0b2b
Points are now weighted on a test context basis
Oleg Oshmyan <chortos@inbox.lv>
parents:
74
diff
changeset
|
150 else: |
0e5ae28e0b2b
Points are now weighted on a test context basis
Oleg Oshmyan <chortos@inbox.lv>
parents:
74
diff
changeset
|
151 try: |
0e5ae28e0b2b
Points are now weighted on a test context basis
Oleg Oshmyan <chortos@inbox.lv>
parents:
74
diff
changeset
|
152 for k in key: |
0e5ae28e0b2b
Points are now weighted on a test context basis
Oleg Oshmyan <chortos@inbox.lv>
parents:
74
diff
changeset
|
153 newmap[k] = oldmap[key] |
0e5ae28e0b2b
Points are now weighted on a test context basis
Oleg Oshmyan <chortos@inbox.lv>
parents:
74
diff
changeset
|
154 except TypeError: |
0e5ae28e0b2b
Points are now weighted on a test context basis
Oleg Oshmyan <chortos@inbox.lv>
parents:
74
diff
changeset
|
155 newmap[key] = oldmap[key] |
0e5ae28e0b2b
Points are now weighted on a test context basis
Oleg Oshmyan <chortos@inbox.lv>
parents:
74
diff
changeset
|
156 setattr(module, name, newmap) |
21 | 157 if options.no_maxtime: |
158 module.maxtime = 0 | |
159 sys.dont_write_bytecode = dwb | |
24
c23d81f4a1a3
Score returned by TestCase.__call__() is now normalized to 0..1
Oleg Oshmyan <chortos@inbox.lv>
parents:
22
diff
changeset
|
160 for name in patterns: |
c23d81f4a1a3
Score returned by TestCase.__call__() is now normalized to 0..1
Oleg Oshmyan <chortos@inbox.lv>
parents:
22
diff
changeset
|
161 if hasattr(module, name): |
c23d81f4a1a3
Score returned by TestCase.__call__() is now normalized to 0..1
Oleg Oshmyan <chortos@inbox.lv>
parents:
22
diff
changeset
|
162 setattr(module, name, getattr(module, name).replace('%', problem_name)) |
c23d81f4a1a3
Score returned by TestCase.__call__() is now normalized to 0..1
Oleg Oshmyan <chortos@inbox.lv>
parents:
22
diff
changeset
|
163 return module |
21 | 164 |
165 def load_global(): | |
70
b9d5857f7b9a
Better emulation of built-ins for testconf
Oleg Oshmyan <chortos@inbox.lv>
parents:
60
diff
changeset
|
166 global builtins |
21 | 167 dwb = sys.dont_write_bytecode |
168 sys.dont_write_bytecode = True | |
169 metafile = files.File('testconf.py', True, 'configuration') | |
170 module = None | |
70
b9d5857f7b9a
Better emulation of built-ins for testconf
Oleg Oshmyan <chortos@inbox.lv>
parents:
60
diff
changeset
|
171 with CompatBuiltins() as builtins: |
25
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
172 if zipimport and isinstance(metafile.archive, files.ZipArchive): |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
173 try: |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
174 module = zipimport.zipimporter(os.path.dirname(metafile.full_real_path)).load_module('testconf') |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
175 except zipimport.ZipImportError: |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
176 pass |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
177 else: |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
178 del sys.modules['testconf'] |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
179 if not module: |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
180 try: |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
181 with metafile.open() as f: |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
182 module = imp.load_module('testconf', f, metafile.full_real_path, ('.py', 'r', imp.PY_SOURCE)) |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
183 # Handle the case when f is not a true file object but imp requires one |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
184 except ValueError: |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
185 # FIXME: 2.5 lacks the delete parameter |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
186 with tempfile.NamedTemporaryFile(delete=False) as f: |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
187 inputdatafname = f.name |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
188 metafile.copy(inputdatafname) |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
189 with ReadDeleting(inputdatafname) as f: |
b500e117080e
Bug fixes and overhead reduction
Oleg Oshmyan <chortos@inbox.lv>
parents:
24
diff
changeset
|
190 module = imp.load_module('testconf', f, metafile.full_real_path, ('.py', 'r', imp.PY_SOURCE)) |
21 | 191 del sys.modules['testconf'] |
192 for name in defaults_global: | |
193 setattr(module, name, getattr(module, name, defaults_global[name])) | |
43 | 194 if not options.erase: |
195 for name in defaults_noerase: | |
196 setattr(module, name, getattr(module, name, defaults_noerase[name])) | |
21 | 197 global globalconf |
198 globalconf = module | |
199 sys.dont_write_bytecode = dwb | |
200 return module |