diff upreckon/config.py @ 146:d5b6708c1955

Distutils support, reorganization and cleaning up * Removed command-line options -t and -u. * Reorganized code: o all modules are now in package upreckon; o TestCaseNotPassed and its descendants now live in a separate module exceptions; o load_problem now lives in module problem. * Commented out mentions of command-line option -c in --help. * Added a distutils-based setup.py.
author Oleg Oshmyan <chortos@inbox.lv>
date Sat, 28 May 2011 14:24:25 +0100
parents config.py@6589511f5418
children 006dce02752c
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/upreckon/config.py	Sat May 28 14:24:25 2011 +0100
@@ -0,0 +1,212 @@
+# Copyright (c) 2010-2011 Chortos-2 <chortos@inbox.lv>
+
+from __future__ import division, with_statement
+
+from .compat import *
+from . import files
+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,
+                    'maxcputime': None,
+                    'maxwalltime': 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 = {'problems': 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
+	try:
+		dwb = sys.dont_write_bytecode
+		sys.dont_write_bytecode = True
+	except AttributeError:
+		pass
+	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']
+	module = Config(module, globalconf)
+	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
+	if (not hasattr(module, 'maxcputime') and
+	    not hasattr(module, 'maxwalltime') and
+	    hasattr(module, 'maxtime')):
+		module.maxcputime = module.maxtime
+	for name in defaults_problem:
+		setattr(module, name, getattr(module, name, defaults_problem[name]))
+	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.maxcputime = module.maxwalltime = 0
+	try:
+		sys.dont_write_bytecode = dwb
+	except NameError:
+		pass
+	for name in patterns:
+		if hasattr(module, name):
+			setattr(module, name, getattr(module, name).replace('%', problem_name))
+	return module
+
+def load_global():
+	global builtins
+	try:
+		dwb = sys.dont_write_bytecode
+		sys.dont_write_bytecode = True
+	except AttributeError:
+		pass
+	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]))
+	if hasattr(module, 'tasknames'):
+		module.problems = module.tasknames
+	global globalconf
+	globalconf = module
+	try:
+		sys.dont_write_bytecode = dwb
+	except NameError:
+		pass
+	return module
\ No newline at end of file