1670 lines
65 KiB
Python
1670 lines
65 KiB
Python
|
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
# Copyright 2009 California Institute of Technology. ALL RIGHTS RESERVED.
|
||
|
#
|
||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
# you may not use this file except in compliance with the License.
|
||
|
# You may obtain a copy of the License at
|
||
|
#
|
||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||
|
#
|
||
|
# Unless required by applicable law or agreed to in writing, software
|
||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
# See the License for the specific language governing permissions and
|
||
|
# limitations under the License.
|
||
|
#
|
||
|
# United States Government Sponsorship acknowledged. This software is subject to
|
||
|
# U.S. export control laws and regulations and has been classified as 'EAR99 NLR'
|
||
|
# (No [Export] License Required except when exporting to an embargoed country,
|
||
|
# end user, or in support of a prohibited end use). By downloading this software,
|
||
|
# the user agrees to comply with all applicable U.S. export laws and regulations.
|
||
|
# The user has the responsibility to obtain export licenses, or other export
|
||
|
# authority as may be required before exporting this software to any 'EAR99'
|
||
|
# embargoed foreign country or citizen of those countries.
|
||
|
#
|
||
|
# Author: Giangi Sacco
|
||
|
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
from __future__ import print_function
|
||
|
import os
|
||
|
import sys
|
||
|
import operator
|
||
|
import logging
|
||
|
import logging.config
|
||
|
logging.config.fileConfig(os.path.join(os.environ['ISCE_HOME'], 'defaults',
|
||
|
'logging', 'logging.conf'))
|
||
|
from iscesys.DictUtils.DictUtils import DictUtils as DU
|
||
|
from iscesys.Compatibility import Compatibility
|
||
|
Compatibility.checkPythonVersion()
|
||
|
from isceobj.Util import key_of_same_content
|
||
|
|
||
|
|
||
|
## Flag to (dis/en)- able exec statements for True/False- this is for
|
||
|
## development, since the "exec" imports certainly work, while the
|
||
|
## __import__() calls *should* work, though they whole thing should
|
||
|
## use 2.7's importlib module (I don't have it- JEB).
|
||
|
EXEC = False
|
||
|
|
||
|
from iscesys.Traits import traits
|
||
|
|
||
|
def containerize(a, ttyp, ctyp):
|
||
|
"""
|
||
|
Convert a string version of a list, tuple, or comma-/space-separated
|
||
|
string into a Python list of ttyp objects.
|
||
|
"""
|
||
|
if not isinstance(a, str):
|
||
|
return a
|
||
|
|
||
|
#strip off the container indicator ('[', ']' for list, '(', ')' for tuple)
|
||
|
if '[' in a:
|
||
|
a = a.split('[')[1].split(']')[0]
|
||
|
elif '(' in a:
|
||
|
a = a.split('(')[1].split(')')[0]
|
||
|
|
||
|
#At this point a is a string of one item or several items separated by
|
||
|
#commas or spaces. This is converted to a list of one or more items
|
||
|
#of type ttyp and then cast to the container type (ctyp). It is
|
||
|
#required that the constructor of the container type takes a list
|
||
|
#as argument (as is the case for list, tuple, numpy.array to name a few).
|
||
|
if ',' in a:
|
||
|
return ctyp([ttyp(x.strip()) for x in a.split(',')])
|
||
|
else:
|
||
|
return ctyp([ttyp(x.strip()) for x in a.split()])
|
||
|
|
||
|
|
||
|
def apply_type(value, dtype, ctype=None):
|
||
|
'''
|
||
|
Function to convert a string representation of an entity's
|
||
|
value to the dtype given as input. Handles an optional
|
||
|
argument named 'container' to convert the input string
|
||
|
value into a list of entities of the type 'dtype'.
|
||
|
'''
|
||
|
if isinstance(dtype, str):
|
||
|
dtype = traits[dtype]
|
||
|
|
||
|
#Check if container is defined
|
||
|
if ctype:
|
||
|
if isinstance(ctype, str):
|
||
|
#if ctype is a string get the actual trait
|
||
|
ctype = traits[ctype]
|
||
|
return containerize(value, dtype, ctype)
|
||
|
else:
|
||
|
return dtype(value)
|
||
|
|
||
|
print("dtype {} not in known traits".format(dtype))
|
||
|
return
|
||
|
|
||
|
|
||
|
## A metaclass for all configurables
|
||
|
class configurable(type):
|
||
|
|
||
|
## Bitwise verbose flag
|
||
|
VERBOSE = 0
|
||
|
|
||
|
## All Configurable class creations go through this method-- ALL of them
|
||
|
def __new__(mcs, *args, **kwargs):
|
||
|
if mcs.VERBOSE & 1: print("NEW:", mcs, args, kwargs)
|
||
|
cls = type.__new__(mcs, *args, **kwargs)
|
||
|
|
||
|
# ## Experimental dictionaryOfVariables manipulation,
|
||
|
# ToDO: build deriviative dictionaries here
|
||
|
if ( 0 and
|
||
|
hasattr(cls, 'dictionaryOfVariables') and
|
||
|
not isinstance(cls.dictionaryOfVariables, dict)
|
||
|
):
|
||
|
cls.dictionaryOfVariables = DictionaryOfVariables(
|
||
|
dict_ = cls.dictionaryOfVariables
|
||
|
)
|
||
|
|
||
|
return cls
|
||
|
|
||
|
|
||
|
## All Configurable instantiations go through this method-- ALL of them
|
||
|
def __call__(cls, *args, **kwargs):
|
||
|
if cls.VERBOSE & 2: print("CALL:", cls, args, kwargs)
|
||
|
inst = super(configurable, cls).__call__(*args, **kwargs)
|
||
|
return inst
|
||
|
pass
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
class EmptyFacility(object):
|
||
|
"""EmptyFacility used in initial component creation of a component declared
|
||
|
as a facility so that its type at least indicates what is intended until the
|
||
|
actual component is created"""
|
||
|
pass
|
||
|
|
||
|
|
||
|
## class for the ubiquitous dictionaryOfVariables-- it emulates the dictionary
|
||
|
## format (mostly) in place now, and add functionality and structure that should
|
||
|
## make usage clearer.
|
||
|
class DictionaryOfVariables(object):
|
||
|
"""DictionaryOfVariables(var1, var2, ..., varN, dict_={})
|
||
|
|
||
|
makes a dictionary of variables.
|
||
|
"""
|
||
|
## Construct from a dictionary (dict=...) or from a variable argument
|
||
|
## list (*args)-- but they better be Variables -or else
|
||
|
def __init__(self, *args, **kwargs):
|
||
|
self.dict_ = kwargs.get("dict_") or {}
|
||
|
try:
|
||
|
for item in args:
|
||
|
self.dict_.update(item.to_dict())
|
||
|
except (AttributeError, TypeError) as err:
|
||
|
if not hasattr(self.dict_, 'update'):
|
||
|
raise TypeError("dict_ keyword is not a dictionary")
|
||
|
else:
|
||
|
if not isinstance(item, Configurable.Variable):
|
||
|
raise TypeError("argument is not a Variable instance")
|
||
|
raise err("Undiagnosed Error in __init__")
|
||
|
return None
|
||
|
|
||
|
## Trivial extensions pass behavior to dict_
|
||
|
def __iter__(self): return iter(self.dict_)
|
||
|
def __eq__(self, other): return self.dict_ == other.dict_
|
||
|
def __getitem__(self, index): return self.dict_.__getitem__(index)
|
||
|
def __setitem__(self, index, value):
|
||
|
return self.dict_.__setitem__(index, value)
|
||
|
def itervalues(self): return self.dict_.itervalues()
|
||
|
def iterkeys(self): return self.dict_.iterkeys()
|
||
|
def iteritem(self): return self.dict_.iteritem()
|
||
|
def values(self): return self.dict_.values()
|
||
|
def keys(self): return self.dict_.keys()
|
||
|
def item(self): return self.dict_.item()
|
||
|
|
||
|
|
||
|
## Private filter of dict_'s items with a function, func.
|
||
|
def _filter(self, func):
|
||
|
result = {}
|
||
|
for key, value in self.dict_.iteritems():
|
||
|
if func(value):
|
||
|
result.update({key:value})
|
||
|
pass
|
||
|
pass
|
||
|
return self.__class__(dict_=result)
|
||
|
|
||
|
## get a DictionaryOfVariables of mandatory variables
|
||
|
def mandatory(self):
|
||
|
return self._filter(bool)
|
||
|
|
||
|
## get a DictionaryOfVariables of optional variables
|
||
|
def optional(self):
|
||
|
return self._filter(operator.not_)
|
||
|
|
||
|
pass
|
||
|
|
||
|
|
||
|
class SELF():
|
||
|
"""
|
||
|
A class to use for Facility declaration to indicate
|
||
|
that an argument is self. A kludge to work with
|
||
|
_RunWrapper class objects that are Facilities.
|
||
|
"""
|
||
|
def __init__(self):
|
||
|
pass
|
||
|
|
||
|
## The base Framework object that implements confugurability.
|
||
|
class Configurable(object):
|
||
|
|
||
|
|
||
|
## A Parameter class- supports all types, not just primiatives.
|
||
|
class Parameter(object):
|
||
|
'''Parameter( attrname,
|
||
|
public_name="",
|
||
|
default=None,
|
||
|
container=None,
|
||
|
type=type,
|
||
|
mandatory=False,
|
||
|
units=None,
|
||
|
doc="""Please provide a docstring""",
|
||
|
private=False):
|
||
|
'''
|
||
|
## if True, do type checking in __init__().
|
||
|
warn = False
|
||
|
def __init__(self, attrname,
|
||
|
public_name="",
|
||
|
default=None,
|
||
|
container=None,
|
||
|
type=type,
|
||
|
mandatory=False,
|
||
|
units=None,
|
||
|
doc="""Please provide a docstring""",
|
||
|
private=False,
|
||
|
intent='input'):
|
||
|
|
||
|
if self.__class__.warn:
|
||
|
raise NotImplementedError
|
||
|
|
||
|
## This name will be assigned to the Configurable's subclass's
|
||
|
## __dict__ -- it *will* be an instance attribute.
|
||
|
self.attrname = str(attrname)
|
||
|
## This name will be used at the command line or xml interface
|
||
|
## to indentify the parameter
|
||
|
self.public_name = public_name or attrname
|
||
|
## Handle container option. The container attribute is a container
|
||
|
## type that can cast a list into an instance of the container.
|
||
|
## The elements of the list are specified by the 'type' attribute.
|
||
|
self.container = container
|
||
|
## This is the paramater's data type-- or a tuple of allowable
|
||
|
## data types, though that is not fully implemented
|
||
|
self.type = type
|
||
|
## This is the default value - should be of type self.type,
|
||
|
## in theory
|
||
|
self.default = default
|
||
|
## units may be used someday
|
||
|
self.units = units
|
||
|
## if private = False -> Parameter is mandatory iff True. It's optional (i.e. if not provided is set
|
||
|
# to a default iff False). It still must be set before running
|
||
|
# if private = True -> Parameter COULD be provided by the user if mandatory is False but it does not
|
||
|
#need to be set before running
|
||
|
# User cannot set it if mandatory = True and private is True
|
||
|
self.mandatory = mandatory
|
||
|
## A helpful docstring for the user, check PEP257.
|
||
|
self.doc = doc
|
||
|
self.private = private
|
||
|
self.intent = intent
|
||
|
return None
|
||
|
|
||
|
## Calling a parameter makes an instance of its type
|
||
|
def __call__(self, *args, **kwargs):
|
||
|
return self.type(*args, **kwargs)
|
||
|
|
||
|
## str is attrname
|
||
|
def __str__(self):
|
||
|
return self.attrname
|
||
|
|
||
|
def __repr__(self):
|
||
|
result = self.__class__.__name__ + "('" + str(self) + "'"
|
||
|
result += ", public_name='%s'" % self.public_name
|
||
|
result += ", default=%s" % str(self.default)
|
||
|
try:
|
||
|
s = self.type.__name__
|
||
|
except AttributeError:
|
||
|
s = str(self.type)
|
||
|
result += ", type=%s" % s
|
||
|
result +=", units=%s" % str(self.units)
|
||
|
result +=", mandatory=%s" % str(self.mandatory)
|
||
|
result +=", private=%s" % str(self.private)
|
||
|
|
||
|
return result + ")"
|
||
|
|
||
|
## bool is mandatory
|
||
|
def __nonzero__(self):
|
||
|
return self.mandatory
|
||
|
|
||
|
## A way to map camelCase to CAMEL_CASE (well, all caps).
|
||
|
def upper(self):
|
||
|
result = ""
|
||
|
for n, c in enumerate(str(self)):
|
||
|
result += "_"+c if n and c.isupper() else c.upper()
|
||
|
return result
|
||
|
|
||
|
## is default even the right type?
|
||
|
def test_default(self):
|
||
|
return isinstance(self.default, self.type)
|
||
|
|
||
|
pass
|
||
|
|
||
|
|
||
|
## Facility imports itself (module) and sets up its own function (factory)
|
||
|
## and then can execute it self with a list of parameters (__call__),
|
||
|
## finally, it can assign it self to a component-- or is that an
|
||
|
## antipattern?
|
||
|
class Facility(object):
|
||
|
'''Parameter(attrname,
|
||
|
public_name="",
|
||
|
module=,
|
||
|
factory=,
|
||
|
args=(),
|
||
|
kwargs={},
|
||
|
mandatory=False,
|
||
|
doc="""Please provide a docstring""",
|
||
|
private=False):
|
||
|
'''
|
||
|
## if True, do type checking in __init__().
|
||
|
warn = False
|
||
|
|
||
|
## The callable factory is None, until it is set.
|
||
|
factory = None
|
||
|
|
||
|
def __init__(self,
|
||
|
attrname,
|
||
|
public_name="",
|
||
|
module=None,
|
||
|
factory=None,
|
||
|
parameter_names=(),
|
||
|
args=(),
|
||
|
kwargs={},
|
||
|
mandatory=False,
|
||
|
doc="""Please provide a docstring""",
|
||
|
private=False):
|
||
|
|
||
|
if self.__class__.warn:
|
||
|
raise NotImplementedError
|
||
|
|
||
|
if args and parameter_names:
|
||
|
message = "Cannot set args keyword if parameter_names is set"
|
||
|
raise ValueError(message)
|
||
|
|
||
|
## This name will be assigned to the Configurable's subclass's
|
||
|
## __dict__ -- it *will* be an instance attribute.
|
||
|
self.attrname = str(attrname)
|
||
|
## This name will be used at the command line or xml interface
|
||
|
## to indentify the parameter
|
||
|
self.public_name = public_name or attrname
|
||
|
|
||
|
self.args = args
|
||
|
self.parameter_names = parameter_names
|
||
|
|
||
|
self.module_name = module
|
||
|
self.factory_name = factory
|
||
|
|
||
|
## Parameter is (not) mandatory iff True (False).
|
||
|
self.mandatory = bool(mandatory)
|
||
|
## A helpful docstring for the user, check PEP257.
|
||
|
self.doc = doc
|
||
|
|
||
|
self.private = private
|
||
|
|
||
|
return None
|
||
|
|
||
|
## Got get the factory in the name
|
||
|
def import_factory(self, fromlist=None):
|
||
|
self.factorymodule = __import__(self.module_name,
|
||
|
fromlist=fromlist or [''])
|
||
|
self.factory = getattr(self.factorymodule, self.factory_name)
|
||
|
return None
|
||
|
|
||
|
|
||
|
## Get arguments from the component's parameters
|
||
|
def extract_args_from_component(self, component):
|
||
|
return [
|
||
|
getattr(component, attr) for attr in self.arg_names
|
||
|
]
|
||
|
|
||
|
## get args- however they are defined
|
||
|
def _get_args(self, component=None):
|
||
|
return self.args or self.extract_args_from_component(component)
|
||
|
|
||
|
## Calling a facility runs it with its arguments
|
||
|
def __call__(self, component=None):
|
||
|
if not self.factory:
|
||
|
self.import_factory()
|
||
|
result = self.factory(*self._get_args(component))
|
||
|
return result
|
||
|
|
||
|
## call it and assign it to component--whether this is good idea is
|
||
|
## TBD-- maybe the componenet's method should modify itself?
|
||
|
def execute(self, component=None):
|
||
|
try:
|
||
|
result = setattr(component, str(self), self(component))
|
||
|
except AttributeError:
|
||
|
## Since this is wrapped in a sys.exit call, this should
|
||
|
## give it a meaningful number. maybe.
|
||
|
import errno
|
||
|
result = errno.EOPNOTSUPP
|
||
|
return result
|
||
|
|
||
|
## str is attrname
|
||
|
def __str__(self):
|
||
|
return self.attrname
|
||
|
|
||
|
## bool is mandatory
|
||
|
def __nonzero__(self):
|
||
|
return self.mandatory
|
||
|
|
||
|
## A way to map camelCase to CAMEL_CASE (well, all caps).
|
||
|
def upper(self):
|
||
|
result = ""
|
||
|
for n, c in enumerate(str(self)):
|
||
|
result += "_"+c if n and c.isupper() else c.upper()
|
||
|
return result
|
||
|
pass
|
||
|
|
||
|
## A way to hold variables in the dictionary of variables
|
||
|
## experimental implementation in Formslc.
|
||
|
class Variable(object):
|
||
|
"""Variable(name, dtype, mandatory, key=None)
|
||
|
|
||
|
name is a sting
|
||
|
dtype is a type
|
||
|
mandatory is a bool
|
||
|
|
||
|
If key is set to a name, then that name will be associated
|
||
|
with the variable; otherwise, it is computed from "name"
|
||
|
in the NAME method.
|
||
|
"""
|
||
|
selfPos = 0
|
||
|
typePos = 2
|
||
|
|
||
|
## name is attrname, key is public name
|
||
|
def __init__(self, name, dtype, mandatory, key=None):
|
||
|
self.name = str(name)
|
||
|
self.dtype = dtype
|
||
|
self.mandatory = bool(mandatory)
|
||
|
self.key = key
|
||
|
return None
|
||
|
|
||
|
## Create object, of any type dtype.
|
||
|
def __call__(self, *args, **kwargs):
|
||
|
return self.dtype(*args, **kwargs)
|
||
|
|
||
|
## Bool is the mandatory flag None case is not supported
|
||
|
def __nonzero__(self):
|
||
|
return self.mandatory
|
||
|
|
||
|
## String is the name.
|
||
|
def __str__(self):
|
||
|
return self.name
|
||
|
|
||
|
## repr is like a tuple
|
||
|
def __repr__(self):
|
||
|
return repr(self.to_tuple())
|
||
|
|
||
|
## iter is like a tuple
|
||
|
def __iter__(self):
|
||
|
return iter(self.to_tuple())
|
||
|
|
||
|
## like a tuple
|
||
|
def __getitem__(self, index):
|
||
|
return self.to_tuple()[index]
|
||
|
|
||
|
## a tuple
|
||
|
def to_tuple(self):
|
||
|
return (self.name, self.dtype, self.mandatory)
|
||
|
|
||
|
## Default name convention:
|
||
|
## camelCase --> CAMEL_CASE
|
||
|
def NAME(self):
|
||
|
if self.key:
|
||
|
return self.key
|
||
|
result = ""
|
||
|
for n, c in enumerate(self.name):
|
||
|
result += "_"+c if n and c.isupper() else c.upper()
|
||
|
return result
|
||
|
|
||
|
## Make self into a dictionary item
|
||
|
def to_dict(self):
|
||
|
return {self.NAME(): self}
|
||
|
|
||
|
pass
|
||
|
#has to be inside the class in this case since the Parameter is defined inside
|
||
|
METADATA_LOCATION = Parameter('metadatalocation',
|
||
|
public_name='METADATA_LOCATION',
|
||
|
default='',
|
||
|
type=str,
|
||
|
mandatory=False,
|
||
|
private=True,
|
||
|
doc='Location of the metadata file where the instance was defined')
|
||
|
## Metaclass allows control of Class/Instance creation.
|
||
|
__metaclass__ = configurable
|
||
|
|
||
|
## A configurable objects parameter list
|
||
|
parameter_list = (METADATA_LOCATION,)
|
||
|
## A configurable objects facilities list (TBD)
|
||
|
facility_list = ()
|
||
|
## A configurable objects facilities list (TBD)
|
||
|
port_list = ()
|
||
|
def get_parameter_names(self, func=lambda x: True):
|
||
|
return map(str, filter(func, self.parameter_list))
|
||
|
|
||
|
def get_parameter_values(self, func=lambda x: True):
|
||
|
return map(self.attrgetter, self.get_parameter_names(func=func))
|
||
|
|
||
|
## Build a dictionary of {attrname:value} -- basically a __dict__
|
||
|
def get_parameter_dictionary(self, func=lambda x:True):
|
||
|
return dict(
|
||
|
zip(self.get_parameter_names(func=func),
|
||
|
self.get_parameter_values(func=func))
|
||
|
)
|
||
|
|
||
|
def get_mandatory_parameters(self):
|
||
|
return filter(bool, self.parameter_list)
|
||
|
|
||
|
def get_optional_parameters(self):
|
||
|
return filter(operator.not_, self.parameter_list)
|
||
|
|
||
|
|
||
|
## TBD method: passing a facility to youself should call it
|
||
|
## and assign it? Thus, I think,
|
||
|
#map(self.set_facility_attr, self.facility_list) #should run everything?
|
||
|
def set_facility_attr(self, facility):
|
||
|
result = facility(self)
|
||
|
setattr(self, str(facility), result)
|
||
|
return result
|
||
|
|
||
|
##
|
||
|
# The object to be initialized calls this inherited method, passing the
|
||
|
# initializer object (see initFromFile.py or InitFromDictionary.py as
|
||
|
# examples of initializers). and gets initialized.
|
||
|
# @param initObject instance of a particular initializer class.
|
||
|
##
|
||
|
def initComponent(self,initObject):
|
||
|
retDict = initObject.init(self)
|
||
|
self.init(retDict)
|
||
|
|
||
|
##
|
||
|
# This method extracts the information returned in a dictionary of dictionaries
|
||
|
# by the "init(): method of the initializer object. If for example the returned
|
||
|
# dictionary is:
|
||
|
#\verbatim
|
||
|
#{'SPACECRAFT_NAME':{'value':'ERS1','doc': 'European Remote Sensing Satellite'}, 'BODY_FIXED_VELOCITY',:{'value':7552.60745017,'doc':'velocity of the spacecraft','units':'m/s'}}
|
||
|
#\endverbatim
|
||
|
|
||
|
# and the self.dictionaryOfVariables is:
|
||
|
#\verbatim
|
||
|
# self.dictionaryOfVariables = {'SPACECRAFT_NAME':{'attrname':'spacecraftName','type':'str','mandatory':True},
|
||
|
# 'BODY_FIXED_VELOCITY':{'attrname':'bodyFixedVelocity', 'type':'float','mandatory':True]}
|
||
|
#\endverbatim
|
||
|
# the self.spacecraftName is set to 'ERS1' and self.bodyFixedVelocity is set to 7552.60745017, while the self.descriptionOfVariables will be set to
|
||
|
#\verbatim
|
||
|
#self.descriptionOfVariables = {'SPACECRAFT_NAME':{'doc': 'European Remote Sensing Satellite'}, 'BODY_FIXED_VELOCITY',:{'doc':'velocity of the spacecraft','units':'m/s'}}
|
||
|
#\endverbatim
|
||
|
|
||
|
from datetime import datetime as dt
|
||
|
|
||
|
def renderToDictionary(self,obj,propDict,factDict,miscDict):
|
||
|
obj.reformatDictionaryOfVariables()
|
||
|
|
||
|
#remove meaningless values from the dictionaries
|
||
|
for k,v in obj.dictionaryOfVariables.items():
|
||
|
val = getattr(obj, v['attrname'])
|
||
|
#Ignore the EmptyFacilities
|
||
|
if isinstance(val,EmptyFacility):
|
||
|
continue
|
||
|
if v['type'] == 'component':#variable type
|
||
|
propDict[k] = {}
|
||
|
miscDict[k] = {}
|
||
|
#check if the key are equivalent and possible replace the one in the dict with k
|
||
|
if DU.keyIsIn(k, obj._dictionaryOfFacilities, True):
|
||
|
factDict[k] = obj._dictionaryOfFacilities[k]
|
||
|
if factDict[k]['factoryname'] == 'default':
|
||
|
module,factory = self._getFacilityInfoFromObject(val)
|
||
|
factDict[k] = {
|
||
|
'factorymodule':module,
|
||
|
'factoryname':factory
|
||
|
}
|
||
|
else:
|
||
|
factDict[k] = {}
|
||
|
|
||
|
#see method comment for detail
|
||
|
if val is not None:
|
||
|
val.adaptToRender()
|
||
|
self.renderToDictionary(val,propDict[k],factDict[k],miscDict[k])
|
||
|
val.restoreAfterRendering()
|
||
|
else:
|
||
|
if self.logger:
|
||
|
self.logger.warning(
|
||
|
"component {} is empty in object of type {}".format(
|
||
|
v['attrname'], type(obj))
|
||
|
)
|
||
|
else:
|
||
|
print(("***information: "+
|
||
|
"component {} is empty in object of type {}").format(
|
||
|
v['attrname'], type(obj))
|
||
|
)
|
||
|
else:
|
||
|
|
||
|
propDict.update({k:val})
|
||
|
if k in obj.unitsOfVariables:
|
||
|
miscDict[k] = {'units':obj.unitsOfVariables[k]['units']}
|
||
|
if k in obj.descriptionOfVariables:
|
||
|
try:
|
||
|
miscDict[k].update({'doc':obj.descriptionOfVariables[k]['doc']})
|
||
|
except KeyError:
|
||
|
miscDict[k] = {'doc':obj.descriptionOfVariables[k]['doc']}
|
||
|
|
||
|
|
||
|
def _getFacilityInfoFromObject(self,obj):
|
||
|
module = obj.__module__
|
||
|
fact = obj.__class__.__name__
|
||
|
return module,fact
|
||
|
#abstract method if the object needs to do some reformatting
|
||
|
#which might be needed if some of the attributes cannot be serialized correctly
|
||
|
def adaptToRender(self):
|
||
|
pass
|
||
|
|
||
|
#abstract method to be called after adaptToRender to repristinate the original format
|
||
|
def restoreAfterRendering(self):
|
||
|
pass
|
||
|
def reformatDictionaryOfVariables(self):
|
||
|
newDict = {}
|
||
|
for k,v in self.dictionaryOfVariables.items():
|
||
|
if isinstance(v,list):
|
||
|
if k in self.dictionaryOfOutputVariables:
|
||
|
intent = 'output'
|
||
|
else:
|
||
|
intent = 'input'
|
||
|
newDict[k] = {'attrname':v[0].replace('self.',''),'type':v[1],
|
||
|
'mandatory':True if v[2] == 'mandatory' else False,'private':False,
|
||
|
'intent':intent}
|
||
|
elif isinstance(v, dict):
|
||
|
newDict[k] = v
|
||
|
else:
|
||
|
continue
|
||
|
self.dictionaryOfVariables = newDict
|
||
|
|
||
|
|
||
|
def init(self,propDict=None,factDict=None,docDict=None,unitsDict=None):
|
||
|
|
||
|
if propDict is None:
|
||
|
propDict = {}
|
||
|
else:
|
||
|
propDict = DU.renormalizeKeys(propDict)
|
||
|
|
||
|
if factDict is None:
|
||
|
factDict = {}
|
||
|
else:
|
||
|
factDict = DU.renormalizeKeys(factDict)
|
||
|
|
||
|
if docDict is None:
|
||
|
docDict = {}
|
||
|
else:
|
||
|
docDict = DU.renormalizeKeys(docDict)
|
||
|
|
||
|
if unitsDict is None:
|
||
|
unitsDict = {}
|
||
|
else:
|
||
|
unitsDict = DU.renormalizeKeys(unitsDict)
|
||
|
|
||
|
self.catalog = DU.renormalizeKeys(self.catalog)
|
||
|
self._dictionaryOfFacilities = DU.renormalizeKeys(
|
||
|
self._dictionaryOfFacilities
|
||
|
)
|
||
|
|
||
|
self.descriptionOfVariables = DU.renormalizeKeys(
|
||
|
self.descriptionOfVariables
|
||
|
)
|
||
|
|
||
|
self.unitsOfVariables = DU.renormalizeKeys(self.unitsOfVariables)
|
||
|
|
||
|
#update the various dictionaries with what was read from command line
|
||
|
if not propDict == {}:
|
||
|
# the top level has only one key that is the appicaltion name
|
||
|
DU.updateDictionary(self.catalog,propDict,replace=True)
|
||
|
|
||
|
if not factDict == {}:
|
||
|
# the top level has only one key that is the appicaltion name
|
||
|
#note: the _dictionaryOfFacilities has also a doc str. add this as a spare keyword so the
|
||
|
# content will be appended instead of replaced
|
||
|
DU.updateDictionary(self._dictionaryOfFacilities,factDict,replace=True,spare='doc')
|
||
|
|
||
|
if not docDict == {}:
|
||
|
#The top level has only one key that is the appicaltion name.
|
||
|
#the update does a append if there is already an entry with a particular key
|
||
|
DU.updateDictionary(self.descriptionOfVariables,docDict)
|
||
|
|
||
|
if not unitsDict == {}:
|
||
|
#The top level has only one key, the application name. In this case replace and hopefully they put the same units!!!
|
||
|
DU.updateDictionary(self.unitsOfVariables,unitsDict,replace = True)
|
||
|
|
||
|
#init recursively
|
||
|
self.initRecursive(self.catalog,self._dictionaryOfFacilities)
|
||
|
|
||
|
|
||
|
def initProperties(self,dictProp):
|
||
|
""" as for calling _facilities, if we make sure that this method is
|
||
|
called in the contructure we don't have to worry about this part
|
||
|
#set the defaults first and then overwrite with the values in dictProp
|
||
|
if property present
|
||
|
try:
|
||
|
self._parameters()
|
||
|
except:# not implemented
|
||
|
pass
|
||
|
"""
|
||
|
|
||
|
self.reformatDictionaryOfVariables()
|
||
|
from iscesys.Parsers.Parser import const_key
|
||
|
for k,v in dictProp.items():
|
||
|
if k == const_key:
|
||
|
continue
|
||
|
#if it is a property than it should be in dictionary of variables
|
||
|
try:
|
||
|
#pure property are only present in dictProp
|
||
|
if(k.upper() not in list(map(str.upper, list(self._dictionaryOfFacilities.keys())))):
|
||
|
kp, vp = key_of_same_content(k, self.dictionaryOfVariables)
|
||
|
compName = vp['attrname']
|
||
|
dtype = vp['type']
|
||
|
ctype = vp['container'] if 'container' in vp.keys() else None
|
||
|
v = apply_type(v, dtype, ctype)
|
||
|
setattr(self, compName, v)
|
||
|
|
||
|
except:#if it is not try to see if it implements a the _parameter
|
||
|
if k not in self._parametersExceptions:
|
||
|
warnOrErr = 'Error'
|
||
|
if self._ignoreMissing:
|
||
|
warnOrErr = 'Warning'
|
||
|
message='%s. The attribute corresponding to the key '%warnOrErr + \
|
||
|
'"%s" is not present in the object "%s".\nPossible causes are the definition'%(str(k),str(self.__class__)) + \
|
||
|
' in the xml file of such attribute that is no longer defined \nin the '+ \
|
||
|
'object "%s" or a spelling error'%str(self.__class__)
|
||
|
|
||
|
if self.logger:
|
||
|
if self._ignoreMissing:
|
||
|
self.logger.warning(message)
|
||
|
else:
|
||
|
self.logger.error(message)
|
||
|
else:
|
||
|
print(message)
|
||
|
if not self._ignoreMissing:
|
||
|
sys.exit(1)
|
||
|
|
||
|
def initRecursive(self,dictProp,dictFact):
|
||
|
#separate simple properties from factories.
|
||
|
#first init the properties since some time they might be used by the factories
|
||
|
|
||
|
self.initProperties(dictProp)
|
||
|
|
||
|
for k, dFk in dictFact.items():
|
||
|
#create an instance of the object
|
||
|
factorymodule = ''
|
||
|
factoryname = ''
|
||
|
args = ()
|
||
|
kwargs = {}
|
||
|
mandatory = ''
|
||
|
|
||
|
# try:
|
||
|
# kp, dFk = key_of_same_content(k,dictFact)
|
||
|
# except:
|
||
|
# if self.logger:
|
||
|
# self.logger.error('No entry in the factory dictionary for %s. Cannot create the object.' % k)
|
||
|
# else:
|
||
|
# print('No entry in the factory dictionary for %s. Cannot create the object.' % k)
|
||
|
# raise Exception
|
||
|
|
||
|
try:
|
||
|
factorymodule = dFk['factorymodule']
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
try:
|
||
|
factoryname = dFk['factoryname']
|
||
|
#factoryname = default means that the object is private, it does not need
|
||
|
#to be initialized and when dumped the factory info will be extracted from
|
||
|
#the object itself
|
||
|
if(factoryname == 'default'):
|
||
|
continue
|
||
|
except:
|
||
|
if self.logger:
|
||
|
self.logger.error('Cannot create object without a factory method.')
|
||
|
else:
|
||
|
print('Cannot create object without a factory method.')
|
||
|
raise Exception
|
||
|
|
||
|
try:
|
||
|
args = dFk['args']
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
try:
|
||
|
kwargs = dFk['kwargs']
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
if factorymodule:
|
||
|
statement= 'from ' + factorymodule + ' import ' + factoryname
|
||
|
# raw_input("1:"+statement)
|
||
|
if EXEC:
|
||
|
exec(statement)
|
||
|
else:
|
||
|
factoryobject = getattr(
|
||
|
__import__(factorymodule, fromlist=['']),
|
||
|
factoryname
|
||
|
)
|
||
|
pass
|
||
|
pass
|
||
|
|
||
|
# raw_input("2:"+statement)
|
||
|
if EXEC:
|
||
|
factoryMethod = factoryname + '(*args,**kwargs)'
|
||
|
statement = 'comp = ' + factoryMethod
|
||
|
exec(statement)
|
||
|
else:
|
||
|
# raw_input("1:"+str(factoryobject))
|
||
|
comp = factoryobject(*args, **kwargs)
|
||
|
pass
|
||
|
|
||
|
try:
|
||
|
p, v = key_of_same_content(k,dictProp)
|
||
|
except:
|
||
|
v = {} # no property for this object. eventually the default will be set in initProperties
|
||
|
|
||
|
if not isinstance(v,dict):
|
||
|
# something wrong since it should be a complex object and therefore should be defined by a dict.
|
||
|
if self.logger:
|
||
|
self.logger.error('Expecting a dictionary for the attribute',k,'. Instead received',v)
|
||
|
else:
|
||
|
print('Expecting a dictionary for the attribute',k,'. Instead received',v)
|
||
|
|
||
|
#now look for all the complex objects that are in dictFact and extract the factory
|
||
|
nextDict = {}
|
||
|
keyList = ['attrname','factorymodule','factoryname','kwargs','doc','args','mandatory','private']
|
||
|
for k1, v1 in dFk.items():
|
||
|
#check that it is not one of the reserved factory keys
|
||
|
isReserved = False
|
||
|
for k2check in keyList:
|
||
|
if k1 == k2check:
|
||
|
isReserved = True
|
||
|
break
|
||
|
if not isReserved:
|
||
|
nextDict.update({k1:v1})
|
||
|
|
||
|
# update with what has been set into _configureThis. Notice that some are not real Configurable, such as the runMethods
|
||
|
# so they don't have catalog and _dictionaryOfFacilities
|
||
|
if(hasattr(comp,'catalog') and hasattr(comp,'_dictionaryOfFacilities')):
|
||
|
#configure the component first
|
||
|
comp._configureThis()
|
||
|
falseList = [True]*2
|
||
|
self._updateFromDicts([comp.catalog,comp._dictionaryOfFacilities],[v,nextDict],falseList)
|
||
|
v = comp.catalog
|
||
|
nextDict = comp._dictionaryOfFacilities
|
||
|
if not (v == {} and nextDict == {}):#if they are both empty don't do anything
|
||
|
comp.initRecursive(v,nextDict)
|
||
|
|
||
|
# now the component is initialized. let's set it into the comp object giving the prescribed name
|
||
|
kp, vp = key_of_same_content(k,self._dictionaryOfFacilities)
|
||
|
|
||
|
try:
|
||
|
#try the dictionaryOfFacilities to see if it is defined
|
||
|
#and has the attrname.
|
||
|
#for private parameters that are object the facility method in
|
||
|
#not implemented, just the property. When reloding the dictionaryOfFacility
|
||
|
#is updated with the info from the xml file but the 'attrname' is missing
|
||
|
#so check is tha k was defined in the dictionaryOfVariables since it contains
|
||
|
#all the parameters
|
||
|
try:
|
||
|
compName = vp['attrname']
|
||
|
except Exception:
|
||
|
if kp in [x.lower() for x in self.dictionaryOfVariables.keys()]:
|
||
|
compName = k
|
||
|
|
||
|
compName = compName.replace('self.','')# the dictionary of variables used to contain the self.
|
||
|
setattr(self, compName, comp)
|
||
|
except:
|
||
|
if self.logger:
|
||
|
self.logger.error('The attribute',k,',is not present in the _dictionaryOfFacilities.')
|
||
|
else:
|
||
|
print('The attribute',k,',is not present in the _dictionaryOfFacilities.')
|
||
|
|
||
|
##
|
||
|
# This method checks if all the variables are initialized to a meaningful value. It throws an exception if at least one variable is not properly initialzed.
|
||
|
##
|
||
|
def checkInitialization(self):
|
||
|
self.reformatDictionaryOfVariables()
|
||
|
for key , val in self.dictionaryOfVariables.items():
|
||
|
#when private or when intent is output (which defaults to private False and mandatory False)
|
||
|
#do not check
|
||
|
if val['private'] == True or val['type'] == 'component' or val['intent'] == 'output':
|
||
|
continue
|
||
|
attrName = val['attrname']
|
||
|
valNow = getattr(self,attrName)
|
||
|
if not valNow and not (valNow == 0):
|
||
|
raise Exception('The variable %s must be initialized'%key)
|
||
|
|
||
|
def _parameters(self):
|
||
|
"""Define the user configurable parameters for this application"""
|
||
|
|
||
|
for item in self.__class__.parameter_list:
|
||
|
try:
|
||
|
try:
|
||
|
from copy import deepcopy
|
||
|
default = deepcopy(item.default)
|
||
|
except:
|
||
|
default = item.default
|
||
|
|
||
|
setattr(self,
|
||
|
item.attrname,
|
||
|
self.parameter(item.attrname,
|
||
|
public_name=item.public_name,
|
||
|
default=default,
|
||
|
units=None,
|
||
|
doc=item.doc,
|
||
|
container=item.container,
|
||
|
type=item.type,
|
||
|
mandatory=item.mandatory,
|
||
|
private=item.private,
|
||
|
intent=item.intent
|
||
|
)
|
||
|
)
|
||
|
except AttributeError:
|
||
|
message = (
|
||
|
"Failed to set parameter %s type %s in %s" %
|
||
|
(str(item), item.__class__.__name__, repr(self))
|
||
|
)
|
||
|
raise AttributeError(message)
|
||
|
pass
|
||
|
return None
|
||
|
|
||
|
def _facilitiesEmpty(self):
|
||
|
"""
|
||
|
First pass in configuring a Component requires placeholder facilities
|
||
|
to be defined before running the _parameters method to create the
|
||
|
dictionaryOfVariables from Parameters. This method will do this with
|
||
|
the EmptyFacility class.
|
||
|
"""
|
||
|
|
||
|
#Check if the facility_list tuple is empty
|
||
|
if not self.facility_list:
|
||
|
#If so, then let _facilities handle this case
|
||
|
#in case the component redefined _facilities
|
||
|
self._facilities()
|
||
|
|
||
|
#Create the facilities as attributes of the component
|
||
|
#without unpacking the arguments; that will happen in
|
||
|
#_facilities after the parameters are handled
|
||
|
for item in self.__class__.facility_list:
|
||
|
try:
|
||
|
setattr(self,
|
||
|
item.attrname,
|
||
|
self.facility(
|
||
|
item.attrname,
|
||
|
public_name=item.public_name,
|
||
|
module=item.module_name,
|
||
|
factory=item.factory_name,
|
||
|
args=item.args,
|
||
|
mandatory=item.mandatory,
|
||
|
doc=item.doc
|
||
|
)
|
||
|
)
|
||
|
except AttributeError:
|
||
|
message = (
|
||
|
"Failed to set facility %s type %s in %s" %
|
||
|
(str(item), item.__class__.__name__, repr(self))
|
||
|
)
|
||
|
raise AttributeError(message)
|
||
|
pass
|
||
|
|
||
|
return
|
||
|
|
||
|
def _facilities(self):
|
||
|
"""
|
||
|
Method that the developer should replace in order to define the facilities of the application
|
||
|
"""
|
||
|
|
||
|
#Don't do anything if the facility_list is empty
|
||
|
if not self.facility_list:
|
||
|
return
|
||
|
|
||
|
for item in self.__class__.facility_list:
|
||
|
try:
|
||
|
#convert item.args that are Parameter instances to the
|
||
|
#corresponding attribute value that was set in self_parameters
|
||
|
#also check if one of the args is an instance of SELF class
|
||
|
#which is sometimes required as an argument to the facility
|
||
|
#constructor
|
||
|
largs = list(item.args)
|
||
|
for i, arg in enumerate(largs):
|
||
|
if isinstance(arg, SELF):
|
||
|
largs[i] = self
|
||
|
elif isinstance(arg, Parameter):
|
||
|
largs[i] = getattr(self, arg.attrname)
|
||
|
else:
|
||
|
largs[i] = arg
|
||
|
targs = tuple(largs)
|
||
|
setattr(self,
|
||
|
item.attrname,
|
||
|
self.facility(
|
||
|
item.attrname,
|
||
|
public_name=item.public_name,
|
||
|
module=item.module_name,
|
||
|
factory=item.factory_name,
|
||
|
args=targs,
|
||
|
private=item.private,
|
||
|
mandatory=item.mandatory,
|
||
|
doc=item.doc
|
||
|
)
|
||
|
)
|
||
|
except AttributeError:
|
||
|
message = (
|
||
|
"Failed to set facility %s type %s in %s" %
|
||
|
(str(item), item.__class__.__name__, repr(self))
|
||
|
)
|
||
|
raise AttributeError(message)
|
||
|
pass
|
||
|
|
||
|
return
|
||
|
|
||
|
def _init(self):
|
||
|
"""
|
||
|
Method that the developer may replace in order to do anything after parameters are set and before facilities are created
|
||
|
"""
|
||
|
return
|
||
|
|
||
|
def _configure(self):
|
||
|
"""
|
||
|
Method that the developer may replace in order to do anything after facilities are created and before his main method is called.
|
||
|
"""
|
||
|
return
|
||
|
|
||
|
def _finalize(self):
|
||
|
"""
|
||
|
Method that the developer may replace in order to do anything after main is called such as finalizing objects that were created.
|
||
|
"""
|
||
|
return
|
||
|
|
||
|
def _processFacilities(self, cmdLineDict):
|
||
|
|
||
|
self._cmdLineDict = cmdLineDict
|
||
|
factDict = self._cmdLineDict[0]
|
||
|
docDict = self._cmdLineDict[1]
|
||
|
unitsDict = self._cmdLineDict[2]
|
||
|
#The first key is just the name of the top component, so pass the associated dictionary
|
||
|
if factDict:
|
||
|
passFact = factDict[list(factDict.keys())[0]]
|
||
|
else:
|
||
|
passFact = {}
|
||
|
if docDict:
|
||
|
passDoc = docDict[list(docDict.keys())[0]]
|
||
|
else:
|
||
|
passDoc = {}
|
||
|
if unitsDict:
|
||
|
passUnits = unitsDict[list(unitsDict.keys())[0]]
|
||
|
else:
|
||
|
passUnits = {}
|
||
|
self.init(self.catalog,passFact,passDoc,passUnits)
|
||
|
|
||
|
return
|
||
|
|
||
|
#Note: mandatory private
|
||
|
# True True -> must be set by the framework before running
|
||
|
# True False -> must be set by the user before running
|
||
|
# False True -> could be set by the user or framework but is not necessary
|
||
|
# False False -> could be set by user, if not the framework sets a default
|
||
|
|
||
|
|
||
|
def parameter(self,attrname,public_name=None,default=None,units=None,
|
||
|
doc=None,container=None,type=None,mandatory=False,
|
||
|
private=False,intent='input'):
|
||
|
public_name = DU.renormalizeKey(public_name)
|
||
|
if units:
|
||
|
# Required to be a dictionary of dictionaries in
|
||
|
# DictUtils.updateDictionary to match structure
|
||
|
# created from user inputs in Parser
|
||
|
self.unitsOfVariables[public_name] = {'units':units}
|
||
|
if doc:
|
||
|
# Required to be a dictionary of dictionaries in
|
||
|
# DictUtils.updateDictionary to match structure
|
||
|
# created from user inputs in Parser
|
||
|
self.descriptionOfVariables[public_name] = {'doc':doc}
|
||
|
if type:
|
||
|
self.typeOfVariables[public_name] = type
|
||
|
|
||
|
#for backward compatibility we need to map the mandatory/private to some string
|
||
|
if (mandatory is True or mandatory == 'True') and private is False:
|
||
|
mand = 'mandatory'
|
||
|
self.mandatoryVariables.append(public_name)
|
||
|
elif (mandatory is False or mandatory == 'False') and private is False:
|
||
|
mand = 'optional'
|
||
|
self.optionalVariables.append(public_name)
|
||
|
#need to add this case. optional means that is needed by if the user does not set it then a default result is used.
|
||
|
#None means that if not given then it is not used. For instance for the ImageAPI the Caster might not be needed when no casting is required
|
||
|
elif (mandatory is None or mandatory == 'None') or (mandatory is False and private is True):
|
||
|
mand = 'None'
|
||
|
elif (mandatory is True and private is True):
|
||
|
mand = 'private'
|
||
|
self.dictionaryOfVariables[public_name] = {'attrname':attrname,
|
||
|
'mandatory':mandatory,
|
||
|
'private':private,
|
||
|
'container':container,
|
||
|
'type':type,
|
||
|
'intent':intent
|
||
|
}
|
||
|
return default
|
||
|
|
||
|
|
||
|
def facility(self, attrname, public_name=None, module=None, factory=None,
|
||
|
doc=None, args=(), kwargs={}, mandatory=False, private=False):
|
||
|
|
||
|
public_name = DU.renormalizeKey(public_name)
|
||
|
|
||
|
#Enter the facility in the dictionaryOfFacilities
|
||
|
self._dictionaryOfFacilities[public_name] = {'attrname':attrname,
|
||
|
'factorymodule':module,
|
||
|
'factoryname':factory,
|
||
|
'args':args,
|
||
|
'kwargs':kwargs,
|
||
|
'mandatory':mandatory,
|
||
|
'private':private
|
||
|
}
|
||
|
|
||
|
#check also for string. should change it to make it consistent between
|
||
|
#parameter and facility
|
||
|
if (mandatory is True or mandatory == 'True') and private is False:
|
||
|
mand = 'mandatory'
|
||
|
self.mandatoryVariables.append(public_name)
|
||
|
elif (mandatory is False or mandatory == 'False') and private is False:
|
||
|
mand = 'optional'
|
||
|
self.optionalVariables.append(public_name)
|
||
|
|
||
|
#need to add this case. optional means that is needed by if the user
|
||
|
#does not set it then a default result is used.
|
||
|
#None means that if not given then it is not used. For instance for the
|
||
|
#ImageAPI the Cater might not be needed when no casting is required
|
||
|
elif ((mandatory is None or mandatory == 'None') or
|
||
|
(mandatory is False and private is True)):
|
||
|
mand = 'None'
|
||
|
elif (mandatory is True and private is True):
|
||
|
mand = 'private'
|
||
|
|
||
|
#Add to dictionaryOfVariables
|
||
|
self.dictionaryOfVariables[public_name] = {'attrname':attrname,
|
||
|
'mandatory':mandatory,
|
||
|
'private':private,
|
||
|
'type':'component'
|
||
|
}
|
||
|
#Add doc string if given
|
||
|
if doc:
|
||
|
self._dictionaryOfFacilities[public_name].update({'doc':doc})
|
||
|
|
||
|
#Delay creating the instance until we parse the command line and check
|
||
|
#for alternate factory
|
||
|
return EmptyFacility()
|
||
|
|
||
|
|
||
|
def _instanceInit(self):
|
||
|
# each class has to call this method after calling the super __init__
|
||
|
# in case of many level of inheritance
|
||
|
self._parameters()
|
||
|
self._facilities()
|
||
|
|
||
|
#init with what we have so far. other callers might overwrite some
|
||
|
#parameters.
|
||
|
#note self.catalog is empty. any empty dict would do it
|
||
|
self.init(self.catalog, self._dictionaryOfFacilities,
|
||
|
self.descriptionOfVariables, self.unitsOfVariables)
|
||
|
self.initOptionalAndMandatoryLists()
|
||
|
|
||
|
## Given an application the we expose only the "mandatory" attribute which could be True or False.
|
||
|
# In order to take care of the fact that mandatory = False consists of two cases, i.e. private = False
|
||
|
# or private = True, we convine that if private is False than the attributes only appears in the
|
||
|
# application file. If private is True than a parameter with the same name appears also in
|
||
|
# the private file which is a file with the same name as the application but preceeded by the underscores (like Foo and __Foo)
|
||
|
# If mandatory = True and private = False it only needs to appear in the appication file
|
||
|
# without specifying the private attribute since is False by default.
|
||
|
# Finally for the case mandatory = True and private = True the attribute only appears in the
|
||
|
# private file (like __Foo) and it's not exposed to the user
|
||
|
|
||
|
def updatePrivate(self):
|
||
|
#Not all instances need to call this, so try
|
||
|
try:
|
||
|
import importlib
|
||
|
module = '.'.join(self.__module__.split('.')[0:-1])
|
||
|
imp = importlib.import_module(module + '.__' + self.__class__.__name__)
|
||
|
#truth table for mandatary, private attributes
|
||
|
#False,False = attribute could be set by user and if not the system must sets it
|
||
|
#True,False = attribute must be set by user
|
||
|
#False,True = attribute could be set by user, if not no one sets it because not needed (like Caster)
|
||
|
#True,True = attribute must be set and the system and not the user i responsible for that
|
||
|
|
||
|
#if a parameter appears in both lists then sets private = True otherwise add it to the
|
||
|
# object parameter_list
|
||
|
toAdd = []
|
||
|
#NOTE: the import is not a class so no imp.__class__.parameter_list
|
||
|
for ppar in imp.parameter_list:
|
||
|
found = False
|
||
|
for par in self.__class__.parameter_list:
|
||
|
if par.attrname == ppar.attrname:
|
||
|
par.private = True
|
||
|
found = True
|
||
|
break
|
||
|
|
||
|
if not found:
|
||
|
toAdd.append(ppar)
|
||
|
self.__class__.parameter_list += tuple(toAdd)
|
||
|
#same for facilities
|
||
|
toAdd = []
|
||
|
for ppar in imp.facility_list:
|
||
|
found = False
|
||
|
for par in self.__class__.facility_list:
|
||
|
if par.attrname == ppar.attrname:
|
||
|
par.private = True
|
||
|
found = True
|
||
|
break
|
||
|
|
||
|
if not found:
|
||
|
toAdd.append(ppar)
|
||
|
self.__class__.facility_list += tuple(toAdd)
|
||
|
except Exception:
|
||
|
pass
|
||
|
##
|
||
|
# This method sets self.warning = True. All the warnings are enabled.
|
||
|
#@see unsetWarnings()
|
||
|
#@see self.warnings
|
||
|
##
|
||
|
def setWarnings(self):
|
||
|
self.warnings = True
|
||
|
|
||
|
##
|
||
|
# This method sets self.warning = False. All the warnings are disabled.
|
||
|
#@see setWarnings()
|
||
|
#@see self.warnings
|
||
|
##
|
||
|
def unsetWarnings(self):
|
||
|
self.warnings = False
|
||
|
|
||
|
def initOptionalAndMandatoryLists(self):
|
||
|
|
||
|
self.reformatDictionaryOfVariables()
|
||
|
for key, val in self.dictionaryOfVariables.items():
|
||
|
if val['mandatory'] is True:
|
||
|
self.mandatoryVariables.append(key)
|
||
|
elif val['mandatory'] is False and val['private'] is False:
|
||
|
self.optionalVariables.append(key)
|
||
|
elif val['private'] is True:
|
||
|
continue
|
||
|
else:
|
||
|
if self.logger:
|
||
|
self.logger.error('Error. Variable can only be optional or mandatory or None')
|
||
|
else:
|
||
|
print('Error. Variable can only be optional or mandatory or None')
|
||
|
raise Exception
|
||
|
|
||
|
def _selectFromDicts(self,dblist):
|
||
|
''' Select all the relevant information for this instance from the
|
||
|
different dictionaries and merge them. Returns a tuple with
|
||
|
(propDict,factDict,miscDict,unitsDict,docDict)
|
||
|
with proctDict already with the top key removed
|
||
|
'''
|
||
|
#Parse the dblist into the separate configuration dictionaries
|
||
|
from iscesys.Parsers.Parser import Parser
|
||
|
PA = Parser()
|
||
|
propDict, factDict, miscDict, argopts = PA.commandLineParser(
|
||
|
dblist
|
||
|
)
|
||
|
#extract doc from miscDict
|
||
|
docDict = DU.extractDict(miscDict, 'doc')
|
||
|
#extract units from miscDict
|
||
|
unitsDict = DU.extractDict(miscDict, 'units')
|
||
|
from iscesys.Component.Application import CmdLinePropDict
|
||
|
from iscesys.Component.Application import CmdLineFactDict
|
||
|
from iscesys.Component.Application import CmdLineMiscDict
|
||
|
from iscesys.Component.Application import CmdLineDocDict
|
||
|
from iscesys.Component.Application import CmdLineUnitsDict
|
||
|
|
||
|
cmdLinePropDict = DU.renormalizeKeys(CmdLinePropDict())
|
||
|
cmdLineFactDict = DU.renormalizeKeys(CmdLineFactDict())
|
||
|
cmdLineMiscDict = DU.renormalizeKeys(CmdLineMiscDict())
|
||
|
cmdLineUnitsDict = DU.renormalizeKeys(CmdLineUnitsDict())
|
||
|
cmdLineDocDict = DU.renormalizeKeys(CmdLineDocDict())
|
||
|
|
||
|
|
||
|
propName = {}
|
||
|
factName = {}
|
||
|
miscName = {}
|
||
|
unitsName = {}
|
||
|
docName = {}
|
||
|
# NOTE: all dicts have the key used for search removed
|
||
|
|
||
|
#NOTE CmdLine... have highest priority
|
||
|
#extract everything that belongs to self.name from the command line.
|
||
|
#this has the top priority
|
||
|
if(self.keyname):
|
||
|
propName,factName,miscName,unitsName,docName = \
|
||
|
self._extractFromDicts([cmdLinePropDict,cmdLineFactDict,cmdLineMiscDict,
|
||
|
cmdLineUnitsDict,cmdLineDocDict],self.keyname)
|
||
|
|
||
|
#extract everything that belongs to self.family from the command line.
|
||
|
#this has the second highest priority
|
||
|
propFamily = {}
|
||
|
factFamily = {}
|
||
|
miscFamily = {}
|
||
|
unitsFamily = {}
|
||
|
docFamily = {}
|
||
|
if(self.keyfamily):
|
||
|
propFamily,factFamily,miscFamily,unitsFamily,docFamily =\
|
||
|
self._extractFromDicts([cmdLinePropDict,cmdLineFactDict,cmdLineMiscDict,
|
||
|
cmdLineUnitsDict,cmdLineDocDict],self.keyfamily)
|
||
|
|
||
|
|
||
|
propDictF = {}
|
||
|
factDictF = {}
|
||
|
miscDictF = {}
|
||
|
unitsDictF = {}
|
||
|
docDictF = {}
|
||
|
|
||
|
#extract everything that belongs to self.family from the dblist that include local and db directory files
|
||
|
#this has the second highest priority
|
||
|
if(self.keyfamily in propDict):
|
||
|
propDictF,factDictF,miscDictF,unitsDictF,docDictF =\
|
||
|
self._extractFromDicts(
|
||
|
[propDict,factDict,miscDict,unitsDict,docDict],self.keyfamily
|
||
|
)
|
||
|
|
||
|
propDictN = {}
|
||
|
factDictN = {}
|
||
|
miscDictN = {}
|
||
|
unitsDictN = {}
|
||
|
docDictN = {}
|
||
|
if(self.keyname in propDict):
|
||
|
propDictN,factDictN,miscDictN,unitsDictN,docDictN =\
|
||
|
self._extractFromDicts(
|
||
|
[propDict,factDict,miscDict,unitsDict,docDict],self.keyname
|
||
|
)
|
||
|
|
||
|
self._updateFromDicts([propDictF,factDictF,miscDictF,unitsDictF,docDictF],
|
||
|
[propDictN,factDictN,miscDictN,unitsDictN,docDictN],
|
||
|
[True,True,True,True,False])
|
||
|
|
||
|
self._updateFromDicts([propDictF,factDictF,miscDictF,unitsDictF,docDictF],
|
||
|
[propFamily,factFamily,miscFamily,unitsFamily,docFamily],
|
||
|
[True,True,True,True,False])
|
||
|
|
||
|
self._updateFromDicts([propDictF,factDictF,miscDictF,unitsDictF,docDictF],
|
||
|
[propName,factName,miscName,unitsName,docName],
|
||
|
[True,True,True,True,False])
|
||
|
|
||
|
|
||
|
return propDictF,factDictF,miscDictF,unitsDictF,docDictF
|
||
|
|
||
|
|
||
|
def help(self):
|
||
|
"""Method that the developer may replace in order to give a helpful
|
||
|
message to the user
|
||
|
|
||
|
"""
|
||
|
|
||
|
def dump(self,filename='',dumper='xml'):
|
||
|
#if not provided use self.name and if not
|
||
|
if not filename:
|
||
|
if not self.name:
|
||
|
if not self.family:
|
||
|
message = "Configurable.py:dump(). The filename is not specified"
|
||
|
if self.logger:
|
||
|
self.logger.error(message)
|
||
|
else:
|
||
|
print(message)
|
||
|
raise Exception
|
||
|
|
||
|
from iscesys.Dumpers.DumperFactory import createFileDumper
|
||
|
from isceobj.XmlUtil import xmlUtils as xml
|
||
|
odProp = xml.OrderedDict()
|
||
|
odFact = xml.OrderedDict()
|
||
|
odMisc = xml.OrderedDict()
|
||
|
dump = createFileDumper(dumper)
|
||
|
self.renderToDictionary(self, odProp,odFact,odMisc)
|
||
|
# remove key,value parir with empty value (except if value is zero)
|
||
|
DU.cleanDictionary(odProp)
|
||
|
DU.cleanDictionary(odFact)
|
||
|
DU.cleanDictionary(odMisc)
|
||
|
firstTag = self.name if self.name else self.family
|
||
|
dump.dump(filename, odProp, odFact, odMisc, firstTag)
|
||
|
|
||
|
def load(self,filename,parser='xml'):
|
||
|
if not filename:
|
||
|
if not self.name:
|
||
|
if not self.family:
|
||
|
message = "Configurable.py:load(). The filename is not specified"
|
||
|
if self.logger:
|
||
|
self.logger.error(message)
|
||
|
else:
|
||
|
print(message)
|
||
|
raise Exception
|
||
|
|
||
|
from iscesys.Parsers.FileParserFactory import createFileParser
|
||
|
FP = createFileParser(parser)
|
||
|
tmpProp, tmpFact, tmpMisc = FP.parse(filename)
|
||
|
docDict = DU.extractDict(tmpMisc, 'doc')
|
||
|
#extract units from miscDict
|
||
|
unitsDict = DU.extractDict(tmpMisc, 'units')
|
||
|
self._parameters()
|
||
|
self._updateFromDicts([self.catalog],[tmpProp],[True])
|
||
|
#just to be sure that the facilities, even if default ones,
|
||
|
#are defined so we can check against the dictionaryOfFacilities
|
||
|
#to make sure that a property is indeed a property and
|
||
|
#not a facility (used in initProperties to validate
|
||
|
#the property)
|
||
|
self._facilitiesEmpty()
|
||
|
self.initProperties(self.catalog)
|
||
|
|
||
|
self._init()
|
||
|
|
||
|
self._facilities()
|
||
|
self._dictionaryOfFacilities = DU.renormalizeKeys(self._dictionaryOfFacilities)
|
||
|
self._updateFromDicts([self._dictionaryOfFacilities],[tmpFact],[True])
|
||
|
self.init(self.catalog,self._dictionaryOfFacilities,docDict,unitsDict)
|
||
|
|
||
|
# Run the user's _configure to transfer user-configured facilities to
|
||
|
# the instance variables
|
||
|
self._configure()
|
||
|
|
||
|
def _extractFromDicts(self,listIn,name):
|
||
|
listOut = []
|
||
|
for dictIn in listIn:
|
||
|
listOut.append(DU.getDictWithKey(dictIn,name,False))
|
||
|
|
||
|
return tuple(listOut)
|
||
|
def _updateFromDicts(self,toUpgrade,upgrade,replace=None):
|
||
|
if not replace:
|
||
|
replace = [False]*len(toUpgrade)
|
||
|
for dictT,dictU,rep in zip(toUpgrade,upgrade,replace):
|
||
|
DU.updateDictionary(dictT,dictU, replace=rep)
|
||
|
##Method called by sub class to update the parameter_list
|
||
|
##@param supclass the super class
|
||
|
def updateParameters(self):
|
||
|
unique = {}
|
||
|
for par in self.__class__.parameter_list:
|
||
|
if par.attrname in unique:
|
||
|
continue
|
||
|
unique[par.attrname] = par
|
||
|
|
||
|
self.__class__.parameter_list = tuple(unique.values())
|
||
|
def extendParameterList(self,sup,sub):
|
||
|
if self.__class__ == sub:
|
||
|
self.__class__.parameter_list = self.__class__.parameter_list + sup.parameter_list
|
||
|
else:
|
||
|
self.__class__.parameter_list = self.__class__.parameter_list + sub.parameter_list + sup.parameter_list
|
||
|
## Call this function after creating the instance to initialize it
|
||
|
def configure(self):
|
||
|
""" Public alias to _configureThis"""
|
||
|
self._configureThis()
|
||
|
|
||
|
def _configureThis(self):
|
||
|
|
||
|
#temp hack when the instance does not support the configurability
|
||
|
#from local files
|
||
|
|
||
|
if(self.name or self.family or self.normname or self.normfamily):
|
||
|
#Determine the possible configuration file names.
|
||
|
#x refers to the files in the install directory where
|
||
|
#the component is installed.
|
||
|
#r refers to a directory defined through the
|
||
|
#environment variable ISCEDB.
|
||
|
#l refers to the local directory.
|
||
|
#family refers to the name given to a component in its definition.
|
||
|
#name refers to an instance name of the component.
|
||
|
|
||
|
xfamilydb = rfamilydb = lfamilydb = ''
|
||
|
xnamedb = rnamedb = lnamedb = ''
|
||
|
|
||
|
import inspect, os
|
||
|
xpath = os.path.split(inspect.getfile(self.__class__))[0]
|
||
|
lpath = os.curdir
|
||
|
|
||
|
#rpath, rafmilydb, and rnamedb are only used if environment
|
||
|
#variable ISCEDB is defined
|
||
|
rpath = ''
|
||
|
try:
|
||
|
rpath = os.environ['ISCEDB']
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
|
||
|
#the family name remote and local db filenames
|
||
|
if self.family:
|
||
|
familydb = self.family+self.ext
|
||
|
xfamilydb = os.path.join(xpath, familydb)
|
||
|
lfamilydb = os.path.join(lpath, familydb)
|
||
|
if rpath:
|
||
|
rfamilydb = os.path.join(rpath, familydb)
|
||
|
|
||
|
#the instance name remote and local db filenames
|
||
|
if self.name:
|
||
|
namedb = self.name+self.ext
|
||
|
xnamedb = os.path.join(xpath, namedb)
|
||
|
lnamedb = os.path.join(lpath, namedb)
|
||
|
if rpath:
|
||
|
rnamedb = os.path.join(rpath, namedb)
|
||
|
|
||
|
#Build the configuration data base list
|
||
|
#ordered in increasing order of priorities.
|
||
|
dblist = []
|
||
|
|
||
|
#Lowest priority: from the install directory
|
||
|
#family-name db
|
||
|
if os.path.exists(xfamilydb):
|
||
|
dblist.append(xfamilydb)
|
||
|
|
||
|
#instance-name db
|
||
|
if os.path.exists(xnamedb):
|
||
|
dblist.append(xnamedb)
|
||
|
|
||
|
#Second priority: remote ISCEDB directory
|
||
|
#family-name db
|
||
|
if os.path.exists(rfamilydb):
|
||
|
dblist.append(rfamilydb)
|
||
|
|
||
|
#instance-name db
|
||
|
if os.path.exists(rnamedb):
|
||
|
dblist.append(rnamedb)
|
||
|
|
||
|
#Third priority: current directory
|
||
|
#family-name db
|
||
|
if os.path.exists(lfamilydb):
|
||
|
dblist.append(lfamilydb)
|
||
|
|
||
|
#instance-name db
|
||
|
if os.path.exists(lnamedb):
|
||
|
dblist.append(lnamedb)
|
||
|
self._parameters()
|
||
|
|
||
|
propDict,factDict,miscDict, unitsDict,docDict = self._selectFromDicts(dblist)
|
||
|
propDict = DU.renormalizeKeys(propDict)
|
||
|
factDict = DU.renormalizeKeys(factDict)
|
||
|
miscDict = DU.renormalizeKeys(miscDict)
|
||
|
unitsDict = DU.renormalizeKeys(unitsDict)
|
||
|
docDict = DU.renormalizeKeys(docDict)
|
||
|
self.catalog = DU.renormalizeKeys(self.catalog)
|
||
|
self._updateFromDicts([self.catalog],[propDict],[True])
|
||
|
#just to be sure that the facilities, even if default ones,
|
||
|
#are defined so we can check against the dictionaryOfFacilities
|
||
|
#to make sure that a property is indeed a property and
|
||
|
#not a facilities (used in initProperties to validate
|
||
|
#the property)
|
||
|
self._facilitiesEmpty()
|
||
|
self.initProperties(self.catalog)
|
||
|
self.dictionaryOfVariables = DU.renormalizeKeys(self.dictionaryOfVariables)
|
||
|
|
||
|
self._init()
|
||
|
self._facilities()
|
||
|
|
||
|
self._dictionaryOfFacilities = DU.renormalizeKeys(self._dictionaryOfFacilities)
|
||
|
self._updateFromDicts([self._dictionaryOfFacilities],[factDict],[True])
|
||
|
self.init(self.catalog,self._dictionaryOfFacilities,unitsDict,docDict)
|
||
|
|
||
|
# Run the user's _configure to transfer user-configured facilities to
|
||
|
# the instance variables
|
||
|
self._configure()
|
||
|
return
|
||
|
|
||
|
|
||
|
|
||
|
def isAskingHelp(self):
|
||
|
import inspect
|
||
|
return (os.path.basename(inspect.getfile(self.__class__)) == os.path.basename(sys.argv[0]))
|
||
|
## Constructor
|
||
|
|
||
|
def __init__(self, family = None, name = None):
|
||
|
|
||
|
|
||
|
# bool variable set to True if the user wants to ignore warning for key specified in
|
||
|
# the xml that are not present in the dictionaryOfVariables. Default False
|
||
|
self._ignoreMissing = False
|
||
|
#Some parameters might not be defined in the class yet so if it does not exist, ignore it
|
||
|
self._parametersExceptions = ['metadata_location','delta_latitude','delta_longitude',
|
||
|
'first_latitude','first_longitude','width','length']
|
||
|
|
||
|
|
||
|
##
|
||
|
# bool variable set True by default. If True all warnings are enabled.
|
||
|
self.warnings = True
|
||
|
##
|
||
|
#
|
||
|
if not family:
|
||
|
family = '_family'
|
||
|
self.family = self.normfamily = self.keyfamily = family
|
||
|
#provide a non empty default otherwise the checkInitialization will complain
|
||
|
if not name:
|
||
|
name = family + '_name'
|
||
|
self.name = self.normname = self.keyname = name
|
||
|
from iscesys.Parsers.Parser import Parser
|
||
|
from iscesys.DictUtils.DictUtils import DictUtils
|
||
|
#####
|
||
|
#become hard to keep track of the name
|
||
|
####
|
||
|
if self.normfamily:
|
||
|
self.normfamily = Parser().normalize_comp_name(self.normfamily)
|
||
|
if self.normname:
|
||
|
self.normname = Parser().normalize_comp_name(self.normname)
|
||
|
if self.keyfamily:
|
||
|
self.keyfamily = DU.renormalizeKey(self.family)
|
||
|
if self.keyname:
|
||
|
self.keyname = DU.renormalizeKey(self.name)
|
||
|
|
||
|
self.ext = '.xml'
|
||
|
self.logger = None
|
||
|
self.catalog = {}
|
||
|
self.descriptionOfVariables = {}
|
||
|
self.descriptionOfFacilities = {}
|
||
|
self._dictionaryOfFacilities = {}
|
||
|
self._cmdLineDict = None
|
||
|
|
||
|
self.typeOfVariables = {}
|
||
|
self.unitsOfVariables = {}
|
||
|
self.dictionaryOfOutputVariables = {}
|
||
|
if not hasattr(self, 'dictionaryOfVariables'):
|
||
|
self.dictionaryOfVariables = {}
|
||
|
self.mandatoryVariables = []
|
||
|
self.optionalVariables = []
|
||
|
|
||
|
self.updatePrivate()
|
||
|
|
||
|
#Add the parameters and facilities to the instance
|
||
|
self._parameters()
|
||
|
#First pass add empty facilities
|
||
|
self._facilitiesEmpty()
|
||
|
|
||
|
self.initOptionalAndMandatoryLists()
|
||
|
#add the family and name as parameters so they get registered into the
|
||
|
#dictionaryOfVariables
|
||
|
self.family = self.parameter('family',public_name='family',default=self.family,
|
||
|
type=str,mandatory=False,doc='Instance family name')
|
||
|
self.name = self.parameter('name',public_name='name',default=self.name,
|
||
|
type=str,mandatory=False,doc='Instance name')
|
||
|
|
||
|
if (("--help" in sys.argv or "-h" in sys.argv) and self.isAskingHelp()):
|
||
|
#assume that the factory for which we want to get the help
|
||
|
# is after the keyword --help or -h
|
||
|
from iscehelp import Helper
|
||
|
help = Helper()
|
||
|
if ("--steps" or "-s") in sys.argv:
|
||
|
help.askHelp(self, steps=True)
|
||
|
else:
|
||
|
help.askHelp(self, steps=False)
|
||
|
|
||
|
#Parameter = Configurable.Parameter
|
||
|
|
||
|
#if __name__ == "__main__":
|
||
|
# import sys
|
||
|
# sys.exit(main())
|
||
|
|
||
|
|
||
|
|
||
|
Variable = Configurable.Variable
|
||
|
Parameter = Configurable.Parameter
|
||
|
Facility = Configurable.Facility
|