view upreckon/config.py @ 174:e0b2fbd7ebe0

Improved built-in output validator; added conf. var. binary The built-in output validator now reads blocks rather than lines, which should make it faster. There is also a new configuration variable called binary, which defaults to False and specifies whether the built-in output validator should not try to translate line breaks. Finally, when binary is false, the built-in output validator now translates line breaks even if it the reference output is in a tape archive.
author Oleg Oshmyan <chortos@inbox.lv>
date Sat, 18 Jun 2011 02:55:17 +0100
parents b375daa74371
children 35d59ba0e27c
line wrap: on
line source

# 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, args

if files.ZipArchive:
	try:
		import zipimport
	except ImportError:
		zipimport = None
else:
	zipimport = None

import imp, os, posixpath, sys, tempfile

__all__ = 'load_problem', 'load_global', 'globalconf', 'nativize_path'

defaults_problem = {'kind': 'batch',
                    'usegroups': False,
                    'maxcputime': None,
                    'maxwalltime': None,
                    'maxmemory': None,
                    'dummies': (),
                    'testsexcluded': (),
                    'padtests': 0,
                    'paddummies': 0,
                    'taskweight': 100,
                    'groupweight': {},
                    'pointmap': {},
                    'stdio': False,
                    'binary': 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 nativize_path(portable_path):
	if portable_path.startswith('//:'):
		return portable_path[3:]
	comps = portable_path.split('/')
	for i, comp in enumerate(comps):
		if comp == '..':
			comps[i] = os.path.pardir
		elif comp == '.':
			comps[i] = os.path.curdir
	native_path = os.path.join(*comps)
	if posixpath.isabs(portable_path) and not os.path.isabs(native_path):
		abspath = os.path.abspath(native_path)
		parent = os.path.dirname(abspath)
		while parent != abspath:
			abspath, parent = parent, os.path.dirname(parent)
		native_path = os.path.join(parent, native_path)
	elif not posixpath.isabs(portable_path) and os.path.isabs(native_path):
		native_path = os.path.sep + native_path
	if posixpath.isabs(portable_path) != os.path.isabs(native_path):
		raise ValueError('cannot make native path relative/absolute')
	return native_path

def load_problem(problem_name):
	global builtins
	try:
		dwb = sys.dont_write_bytecode
		sys.dont_write_bytecode = True
	except AttributeError:
		pass
	metafile = files.File(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 hasattr(module, 'testee'):
		if isinstance(module.testee, basestring):
			module.path = nativize_path(module.testee)
		else:
			testee = tuple(module.testee)
			module.path = (nativize_path(testee[0]),) + testee[1:]
	elif 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
	if module.tester:
		if isinstance(module.tester, basestring):
			module.tester = nativize_path(module.tester)
		else:
			tester = tuple(module.tester)
			module.tester = (nativize_path(tester[0]),) + tester[1:]
	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
	if args:
		module.usegroups = False
		module.tests = args
		module.dummies = ()
	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