# -*- coding: utf-8 -*-
#The MIT License (MIT)
#
#Copyright (c) 2014 Lukasz Mentel
#
#Permission is hereby granted, free of charge, to any person obtaining a copy
#of this software and associated documentation files (the "Software"), to deal
#in the Software without restriction, including without limitation the rights
#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#copies of the Software, and to permit persons to whom the Software is
#furnished to do so, subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in all
#copies or substantial portions of the Software.
#
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#SOFTWARE.
'''
An abstract class that should be subclassed when addding a new code interface.
All the methods should be implemented for the basisopt module to work with the
new code.
'''
import os
import re
from string import Template
from abc import ABCMeta, abstractmethod
[docs]class Calculator():
'''
Abstract class that should be subclassed when adding a new code interace.
'''
__metaclass__ = ABCMeta
def __init__(self, exevar=None, executable=None, runopts=None,
scratch=None):
self.exevar = exevar
if executable is None and exevar is None:
self.exe_found = False
elif executable is None and os.getenv(self.exevar) is not None:
self.executable = os.getenv(self.exevar)
self.exe_found = True
elif executable is not None and exevar is None:
self.executable = executable
self.exe_found = True
elif executable is not None and exevar is not None:
exefromvar = os.getenv(self.exevar)
if os.path.abspath(exefromvar) == os.path.abspath(executable):
self.executable = exefromvar
self.exe_found = True
else:
raise ValueError('conflicting <executable> and <exevar> options specified: '\
'{0:s} != {1:s}'.format(executable, exefromvar))
self.runopts = runopts
self.scratch = scratch
@property
def executable(self):
'Set the executable'
return self._executable
@executable.setter
def executable(self, value):
if os.path.exists(value):
if os.path.isdir(value):
raise OSError('{0} is a directory, not executable'.format(value))
elif os.path.isfile(value):
if os.access(value, os.X_OK):
self._executable = value
else:
raise OSError('file: {0} is not executable'.format(value))
else:
raise OSError('unknown type of the executable: {0}'.format(value))
else:
raise ValueError("executable: '{0}' does not exists".format(value))
@property
def scratch(self):
'Return scratch'
return self._scratch
@scratch.setter
def scratch(self, path):
if path is None:
self._scratch = os.path.join(os.getenv('HOME'), 'scratch')
elif os.path.exists(path):
self._scratch = path
else:
raise ValueError("Scratch directory doesn't exist: {}".format(path))
[docs] @abstractmethod
def parse(self, fname, objective, regexp=None):
'''
Parse the value of the ``objective`` from the file ``fname``
'''
pass
[docs] @abstractmethod
def run(self, *args):
'''
run a single job
'''
pass
[docs] @abstractmethod
def run_multiple(self, *args):
'''
Run multiple jobs
'''
pass
[docs] def accomplished(self, fname):
'''
Return True if the job completed without errors
'''
return self.parse(fname, 'accomplished') is True
def parse_objective(fname, regexp, reflags=re.I):
'''
Wrapper for the parse methods
Args:
fname : :py:class:`str`
Name of the file to be parsed
regexp : :py:class:`str`
Regular expression as raw string
reflags :
Flags for the regular expression compilation
'''
if not os.path.exists(fname):
raise ValueError("Output file: {0:s} doesn't exist in {1:s}".format(fname, os.getcwd()))
rec = re.compile(regexp, flags=reflags)
with open(fname, 'r') as fobj:
for line in fobj:
match = rec.search(line)
if match:
# check if a group was captured or not
if match.lastindex is not None and match.lastindex > 0:
return float(match.group(1))
else:
return True
else:
return None
class InputTemplate(Template):
'Modified string.Template class to be used for input rendering'
delimiter = '%'
idpattern = r'[a-z][_a-z0-9]*'
def get_keys(self):
'''
Parse the string and return a dict with possible keys to substitute.
For most use case only the `named` fields are interesting.
'''
keys = {}
match = self.pattern.findall(self.template)
for key, val in self.pattern.groupindex.items():
keys[key] = [x[val - 1] for x in match if x[val - 1] != '']
return keys