view upreckon/config.py @ 246:1bc89faac941 2.04

Fixed: match='re' could produce duplicate test identifiers files.Files.regexp(pattern) now makes sure to return only one metafile for each matching virtual path, namely, the one that would be returned for that virtual path by files.Files.from_virtual_path.
author Oleg Oshmyan <chortos@inbox.lv>
date Thu, 03 Oct 2013 01:19:09 +0300
parents 715e3525a904
children
line wrap: on
line source

# Copyright (c) 2010-2012 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,
                    'match': 'literal',
                    'dummies': (),
                    'testsexcluded': (),
                    'padtests': 0,
                    'paddummies': 0,
                    'taskweight': 100,
                    'groupweight': {},
                    'pointmap': {},
                    'stdio': False,
                    'binary': False,
                    'dummyinname': '',
                    'dummyoutname': '',
                    'tester': None,
                    'maxexitcode': 0,
                    'okexitcodeflag': 0,
                    'inname': '',
                    'ansname': '',
                    'force_zero_exitcode': True}
defaults_global = {'problems': None}
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.from_virtual_path(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
			# TypeError on Python 3, ValueError on Python 2
			except (TypeError, 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)
		elif not callable(module.tester):
			tester = tuple(module.tester)
			module.tester = (nativize_path(tester[0]),) + tester[1:]
	if not isinstance(module.taskweight, dict):
		try:
			module.taskweight = dict(zip(module.problems, module.taskweight))
		except TypeError:
			pass
	try:
		module.taskweight = module.taskweight[problem_name]
	except KeyError:
		module.taskweight = defaults_problem['taskweight']
	except TypeError:
		pass
	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.match = 'literal'
		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.from_virtual_path('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
			# TypeError on Python 3, ValueError on Python 2
			except (TypeError, 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
	if module.problems is not None:
		# Iterable and mapping taskweights cause re-iteration over problems
		try:
			len(module.problems)
		except Exception:
			module.problems = tuple(module.problems)
	global globalconf
	globalconf = module
	try:
		sys.dont_write_bytecode = dwb
	except NameError:
		pass
	return module