# $SnapHashLicense:
#
# SnapLogic - Open source data services
#
# Copyright (C) 2009, SnapLogic, Inc. All rights reserved.
#
# See http://www.snaplogic.org for more information about
# the SnapLogic project.
#
# This program is free software, distributed under the terms of
# the GNU General Public License Version 2. See the LEGAL file
# at the top of the source tree.
#
# "SnapLogic" is a trademark of SnapLogic, Inc.
#
#
# $
# $Id: prop_err.py 10183 2009-12-16 23:58:55Z grisha $
import re
from snaplogic.common.snap_exceptions import *
from snaplogic.snapi_base.resdef import ResDef,list_of_field_types
from snaplogic.snapi_base import keys
from snaplogic.cc import prop
from snaplogic.common import component_prop_defs
FIELD_AND_VIEW_NAME_PATTERN = re.compile(r'[^a-zA-Z0-9_]+')
class SimplePropErr(object):
"""
This class is used in reporting error about simple property type.
It provides a method for setting an error message and retrieving the error message.
"""
def __init__(self, property_label = None):
"""
Initializes a simple type error object.
"""
self._err_msg = None
self._property_label = property_label
def __str__(self):
return "%s" % self._err_msg
def set_message(self, err_msg = None):
"""
Sets an error message.
@param err_msg: Error message
@type err_msg: str
"""
self._err_msg = err_msg
def get_error(self):
"""
Returns error message currently set. Can be None or a string
@return: Error message
@rtype: str
"""
return self._err_msg
def _to_resdef(self):
if self._err_msg is not None:
return {keys.LABEL: self._property_label, keys.ERROR_MESSAGE: self._err_msg}
else:
return None
class ListPropErr(list):
"""
A list property error object is used for reporting errors on a list property.
This class provides a container for holding error object for each entry in the list property.
"""
def __init__(self, read_only, property_label = None):
"""
Initializes a list type error object.
"""
list.__init__(self)
self._read_only = read_only
self._err_msg = None
self._property_label = property_label
def __str__(self):
s = []
s.append("List_err(%s) [ " % self._err_msg)
for v in self:
s.append("%s, " % v)
s.append("]")
return "".join(s)
def set_message(self, err_msg = None):
"""
Sets an error message.
@param err_msg: Error message
@type err_msg: str
"""
self._err_msg = err_msg
def get_error(self):
"""
Returns error message currently set. Can be None or a string
@return: Error message
@rtype: str
"""
return self._err_msg
def __setitem__(self, index, value):
"""
Intercept the l[index]=value operations.
@param index: The index value
@type index: int or long
@param value: The SimplePropErr or ListPropErr or DictPropErr definition
@type value: L{SimplePropErr} or L{ListPropErr} or L{DictPropErr} or None
"""
if self._read_only[0]:
raise SnapObjPermissionError("The list membership cannot be modified")
if (value != None and (not isinstance(value, SimplePropErr)) and (not isinstance(value, DictPropErr))
and (not isinstance(value, ListPropErr))):
raise SnapObjTypeError("The value needs to be a SimplePropErr/ListPropErr/DictPropErr object")
list.__setitem__(self, index, value)
def __setslice__(self, i, j, sequence):
if self._read_only[0]:
raise SnapObjPermissionError("The list membership cannot be modified")
c = 1
for s in sequence:
if ((not isinstance(s, SimplePropErr)) and (not isinstance(s, DictPropErr))
and (not isinstance(s, ListPropErr))):
raise SnapObjTypeError(
"The value #%s in the sequence is not a SimplePropErr/ListPropErr/DictPropErr object" % c)
c += 1
list.__setslice__(self, i, j, sequence)
def __getslice__(self, i, j):
"""
Returns ListPropErr with the specified slice range.
@param i: Beginning of the index range
@type i: int or long
@param j: End of the index range
@type j: int or long
@return: Returns ListPropErr defintion for the specified slice.
@rtype: L{ListPropErr}
"""
# The slice has to be same type as the sequence from which it is derived
slice_val = ListPropErr()
slice_val.extend(list.__getslice__(self, i, j))
return slice_val
def append(self, value):
"""
Append error object to list error object.
@param value: New entry for the list
@param value: Either L{SimplePropErr} or L{ListPropErr} or L{DictPropErr} or None
"""
if self._read_only[0]:
raise SnapObjPermissionError("The list membership cannot be modified")
if (value != None and (not isinstance(value, SimplePropErr)) and (not isinstance(value, DictPropErr))
and (not isinstance(value, ListPropErr))):
raise SnapObjTypeError("The value needs to be a SimplePropErr/ListPropErr/DictPropErr object")
list.append(self,value)
def extend(self, new_vals):
"""
Append list of new error objects to list property.
@param new_vals: List of new error objects.
@type new_vals: List of L{SimplePropErr}, L{ListPropErr} and L{DictPropErr} or None
"""
if self._read_only[0]:
raise SnapObjPermissionError("The list membership cannot be modified")
i = 1
for s in new_vals:
if (s != None and (not isinstance(s, SimplePropErr)) and (not isinstance(s, DictPropErr))
and (not isinstance(s, ListPropErr))):
raise SnapObjTypeError(
"The value #%s in the iterable is not a SimplePropErr/ListPropErr/DictPropErr object" % i)
i += 1
list.extend(self, new_vals)
def insert(self, i, value):
"""
Insert new error object at the specified index.
@param i: Index number
@type i: int or long
@param value: Definition to be inserted.
@type value: L{SimplePropErr}, L{ListPropErr}, L{DictPropErr} or None
"""
if self._read_only[0]:
raise SnapObjPermissionError("The list membership cannot be modified")
if (value != None and (not isinstance(value, SimplePropErr)) and (not isinstance(value, DictPropErr))
and (not isinstance(value, ListPropErr))):
raise SnapObjTypeError("The entry needs to be a SimplePropErr/ListPropErr/DictPropErr object")
list.insert(self, i, value)
def __delitem__(self, key):
if self._read_only[0]:
raise SnapObjPermissionError("The list membership cannot be modified")
list.__delitem__(self, key)
def __delslice__(self, i, j):
if self._read_only[0]:
raise SnapObjPermissionError("The list membership cannot be modified")
list.__delslice__(self, i, j)
def pop(self):
if self._read_only[0]:
raise SnapObjPermissionError("The list membership cannot be modified")
return list.pop(self)
def remove(self, i):
if self._read_only[0]:
raise SnapObjPermissionError("The list membership cannot be modified")
list.remove(self, i)
def reverse(self):
if self._read_only[0]:
raise SnapObjPermissionError("The list membership cannot be modified")
list.reverse(self)
def sort(self, cmpfunc=None):
if self._read_only[0]:
raise SnapObjPermissionError("The list membership cannot be modified")
list.sort(self, cmpfunc)
def _to_resdef(self):
l = []
err_flag = False
for i in self:
ret = i._to_resdef()
l.append(ret)
if ret is not None:
err_flag = True
if err_flag or self._err_msg is not None:
return {keys.LABEL: self._property_label, keys.ERROR_MESSAGE : self._err_msg, keys.LIST_ERROR: l}
else:
return None
class DictPropErr(dict):
"""
A dict property error object is used for reporting errors on a dict property.
This class provides a container for holding error object for each entry in the dict property.
"""
def __init__ (self, read_only, property_label = None):
"""
Initializes a dict type property defintion.
"""
dict.__init__(self)
self._read_only = read_only
self._err_msg = None
self._property_label = property_label
def __str__(self):
s = []
s.append("Dict_err(%s) { " % self._err_msg)
for k in self:
s.append("\"%s\" : %s, " % (k, self[k]))
s.append("}")
return "".join(s)
def set_message(self, err_msg = None):
"""
Sets an error message.
@param err_msg: Error message
@type err_msg: str
"""
self._err_msg = err_msg
def get_error(self):
"""
Returns error message currently set. Can be None or a string
@return: Error message
@rtype: str
"""
return self._err_msg
def __setitem__(self, key, value):
"""
Intercept the l[key]=value operations.
@param key: Key for the entry
@type key: str
@param value: Value for the entry
@type value: Varies, can be SimpleProp or a more complex property like DictProp or ListProp.
"""
if self._read_only[0]:
raise SnapObjPermissionError("The dictionary membership cannot be modified")
if ((not isinstance(value, SimplePropErr)) and (not isinstance(value, DictPropErr))
and (not isinstance(value, ListPropErr))):
raise SnapObjTypeError("The value needs to be a SimplePropErr/ListPropErr/DictPropErr object")
dict.__setitem__(self, key, value)
def update(self, update_dict):
"""
Update method of a dictionary container.
@param update_dict: The dictionary being used for update
@type update_dict: L{DictPropErr}
"""
if self._read_only[0]:
raise SnapObjPermissionError("The dictionary membership cannot be modified")
i = 0
for key, value in update_dict.items():
if key not in self:
i += 1
if ((not isinstance(value, SimplePropErr)) and (not isinstance(value, DictPropErr))
and (not isinstance(value, ListPropErr))):
raise SnapObjTypeError("The value needs to be a SimplePropErr/ListPropErr/DictPropErr object")
dict.update(self, update_dict)
def setdefault(self, key, value):
"""
The setdefault functionality of a dictionary.
Unlike normal dictionary setdefault() method, value param cannot be None. It has to be
object of type SimplePropErr, DictPropErr or ListPropErr.
@param key: Key for the setdefault call
@type key: str
@param value: The default value. If not specified or set to None, then the default_entry_type is
used.
@type value: None or L{SimplePropErr}, L{DictPropErr} or L{ListPropErr}
@return: The value at the key of default entry.
@rtype: L{SimplePropErr} or L{DictPropErr} or L{ListPropErr}
"""
if self._read_only[0]:
raise SnapObjPermissionError("The dictionary membership cannot be modified")
if value is None:
raise SnapValueError("Default value cannot be None")
elif ((not isinstance(value, SimplePropErr)) and (not isinstance(value, DictPropErr))
and (not isinstance(value, ListPropErr))):
raise SnapObjTypeError("The value needs to be a SimplePropErr/ListPropErr/DictPropErr object")
return dict.setdefault(self, key, value)
def __delitem__(self, key):
if self._read_only[0]:
raise SnapObjPermissionError("The dictionary membership cannot be modified")
dict.__delitem__(self, key)
def clear(self):
if self._read_only[0]:
raise SnapObjPermissionError("The dictionary membership cannot be modified")
dict.clear(self)
def pop(self, k, x=None):
if self._read_only[0]:
raise SnapObjPermissionError("The dictionary membership cannot be modified")
return dict.pop(self, k, x)
def popitem(self):
if self._read_only[0]:
raise SnapObjPermissionError("The dictionary membership cannot be modified")
return dict.popitem(self)
def _to_resdef(self):
d = {}
for i in self:
if self[i]._to_resdef() is not None:
d[i] = self[i]._to_resdef()
if len(d) or self._err_msg is not None:
return {keys.LABEL: self._property_label, keys.ERROR_MESSAGE : self._err_msg, keys.DICT_ERROR: d}
else:
None
def create_err_obj(inp_prop, read_only, prop_def = None, partial_data = False, dynamic_constraints = None):
"""
Create error object corresponding to the structure of the property.
@param inp_prop: Property for which the error object must be created.
@type inp_prop: simple value/list/dict.
@param read_only: A list that should have a single entry, with the value False. This list entry can
later be set to True, to prevent users from modifying the structure of the error objects.
@type read_only: list
@param prop_def: If we want to validate the property value against a property definition, as we create
the error object, then the property definition should be provided in this param
@type prop_def: SimpleProp/ListProp/DictProp
@param partial_data: If set to True, then don't return error if all the required values have not
been provided.
@type partial_data: bool
@param dynamic_constraints: Dictionary mapping dynamic constraint name to constraint value.
For example, dynamic constraint "input field" may look like:
{keys.CONSTRAINT_LOV_INPUT_FIELD : ['city', 'state', 'zip'] }
Meaning that there are three input fields: city, state, and zip.
@type all_dynamic_constraints: dict
@return: Error object which corresponds in structure to the input property.
@rtype: L{SimplePropErr}, L{ListPropErr} or L{DictPropErr}
"""
prop_stack = []
prop_stack.append((None, None, inp_prop, prop_def))
root_err_obj = None
while len(prop_stack) > 0:
(parent_err_obj, my_key, prop_val, prop_def) = prop_stack.pop()
if prop_def is not None:
property_label = prop_def.label_str
else:
property_label = None
if isinstance(prop_val, dict):
err_obj = DictPropErr(read_only, property_label)
# If a prop_def is not available, then it is assumed that there is no predefined
# structure for that branch of the property definition (and hence not much validation that
# can be done.)
if prop_def is not None:
ret = prop_def.validate(prop_val, my_key, partial_data)
if ret is not None:
err_obj.set_message(ret)
# We won't dive into a dict, if it has already been found to be structurally unsound.
err = err_obj._to_resdef()
if err is None:
for k in prop_val:
# Again, we do not expect propdef to be present. If it is
# available, we will walk the definition.
if prop_def is not None:
elem_def = prop_def.get_item_type(k)
else:
elem_def = None
prop_stack.append((err_obj, k, prop_val[k], elem_def))
elif isinstance(prop_val, list) or isinstance(prop_val, tuple):
err_obj = ListPropErr(read_only, property_label)
if prop_def is not None:
ret = prop_def.validate(prop_val, my_key, partial_data)
if ret is not None:
err_obj.set_message(ret)
# We won't dive into a list, if it has already been found to be structurally unsound.
err = err_obj._to_resdef()
if err is None:
for i in range(len(prop_val)):
# Prefill the list with empty slots.
err_obj.append(None)
# Again, we do not expect propdef to be present. If it is
# available, we will walk the definition.
if prop_def is not None:
elem_def = prop_def.get_item_type(i)
else:
elem_def = None
prop_stack.append((err_obj, i, prop_val[i], elem_def))
else:
err_obj = SimplePropErr(property_label)
if prop_def is not None:
ret = prop_def.validate(prop_val, my_key, partial_data, dynamic_constraints)
if ret is not None:
err_obj.set_message(ret)
if parent_err_obj is not None:
# This setitem should work for both dict and list, since list has been pre-filled with empty slots.
parent_err_obj[my_key] = err_obj
else:
root_err_obj = err_obj
return root_err_obj
class ComponentResourceErr(object):
"""
This class provides an interface for setting validation errors on a component's resdef.
"""
def __init__(self, resdef_dict, partial_data = False):
"""
Initialize error object for resource defintion.
"""
self._err_msg = None
self._read_only = [False]
# Populate the dictionary mapping dynamic constraint names to actual constraint values.
# LOVs can be a static list of values or can be a more dynamic list, consist of list of view names or
# list of field names. Here we prepare those dynamic lists to support proper validation of property values
# against such dynamic LOVs.
#
# For example, dynamic constraint "input field" may look like:
# {keys.CONSTRAINT_LOV_INPUT_FIELD : ['city', 'state', 'zip'] }
# Meaning that there are three input fields: city, state, and zip.
dynamic_constraints = {}
# Expand the dynamic constraints
for (view_type, view_constraint, field_constraint) in \
[[keys.INPUT_VIEWS, keys.CONSTRAINT_LOV_INPUT_VIEW, keys.CONSTRAINT_LOV_INPUT_FIELD],
[keys.OUTPUT_VIEWS, keys.CONSTRAINT_LOV_OUTPUT_VIEW, keys.CONSTRAINT_LOV_OUTPUT_FIELD]]:
# Collect view names in an array and sort them alphabetically, so it's easier to write QA tests
view_names = resdef_dict[view_type].keys()
view_names.sort()
# Store the list of views constraint
dynamic_constraints[view_constraint] = view_names
# Do we have multiple views? If so view name prefix is required.
multiple_views = len(view_names) > 1
field_names = []
# Collect all views' fields
for view_name in view_names:
# If there are no fields (e.g. binary view) move on
if keys.VIEW_FIELDS not in resdef_dict[view_type][view_name]:
continue
# If there are multiple input views, or multiple output views, the field name
# must be prefixed with the view name.
if multiple_views:
prefix = view_name + "."
else:
prefix = ""
for field in resdef_dict[view_type][view_name][keys.VIEW_FIELDS]:
field_name = field[keys.FIELD_NAME]
if field_name is not None:
field_names.append(prefix + field[keys.FIELD_NAME])
else:
# Special treatment for None (can't append anything to it)
field_names.append(field[keys.FIELD_NAME])
# Store the list of fields constraint
dynamic_constraints[field_constraint] = field_names
d = prop.DictProp("Property")
self._prop_err = DictPropErr(self._read_only, "Properties")
for prop_name in resdef_dict[keys.PROPERTY_DEFS]:
d[prop_name] = prop.create_property_from_resdef(
resdef_dict[keys.PROPERTY_DEFS][prop_name][keys.PROPERTY_DEFINITION])
if prop_name not in resdef_dict[keys.PROPERTY_VALS]:
resdef_dict[keys.PROPERTY_VALS][prop_name] = None
self._prop_err[prop_name] = create_err_obj(resdef_dict[keys.PROPERTY_VALS][prop_name],
self._read_only, d[prop_name], partial_data,
dynamic_constraints)
prop.validate_param_notations(resdef_dict[keys.PARAMS].keys(), resdef_dict[keys.PROPERTY_VALS],
resdef_dict[keys.PROPERTY_DEFS], self._prop_err)
# We won't be setting partial_data flag for these objects, since they must have a valid structure
# at all times.
self._input_view_err = create_err_obj(resdef_dict[keys.INPUT_VIEWS], self._read_only,
component_prop_defs.INPUT_VIEWS_PROP)
self._output_view_err = create_err_obj(resdef_dict[keys.OUTPUT_VIEWS], self._read_only,
component_prop_defs.OUTPUT_VIEWS_PROP)
self._param_err = create_err_obj(resdef_dict[keys.PARAMS], self._read_only,
component_prop_defs.PARAMS_PROP)
self._pass_through_err = create_err_obj(resdef_dict[keys.OUTPUT_VIEW_PASSTHROUGH], self._read_only,
component_prop_defs.PASSTHROUGH_PROP)
categories = resdef_dict.get(keys.RESOURCE_CATEGORY)
if categories is not None:
self._category_err = create_err_obj(categories, self._read_only, component_prop_defs.CATEGORIES_PROP)
else:
self._category_err = None
res_ref = resdef_dict.get(keys.RESOURCE_REF)
if res_ref is not None:
self._resource_ref_err = create_err_obj(res_ref, self._read_only, component_prop_defs.RESOURCE_REF_PROP)
else:
self._resource_ref_err = None
# Capabilities check needs to be mindful of partial data
self._validate_resdef_capabilities(resdef_dict, partial_data)
self._validate_views(resdef_dict)
self._read_only[0] = True
def _validate_resdef_capabilities(self, res_dict, partial_data = False):
"""
Validates that the resdef does not violate any constraints specified in capabilities dict.
@param res_dict: Resdef as a dictionary.
@type res_dict: dict
@param partial_data: If set to True, then don't return error if all the required values have not
been provided.
@type partial_data: bool
"""
cap = res_dict[keys.CAPABILITIES]
for k in [keys.CAPABILITY_INPUT_VIEW_LOWER_LIMIT, keys.CAPABILITY_INPUT_VIEW_UPPER_LIMIT,
keys.CAPABILITY_OUTPUT_VIEW_LOWER_LIMIT, keys.CAPABILITY_OUTPUT_VIEW_UPPER_LIMIT,
keys.CAPABILITY_INPUT_VIEW_ALLOW_BINARY, keys.CAPABILITY_OUTPUT_VIEW_ALLOW_BINARY,
keys.CAPABILITY_ALLOW_PASS_THROUGH]:
if k not in cap:
self.set_message("Capability '%s' not specified in the resource definition" % k)
return
l = len(res_dict[keys.INPUT_VIEWS])
if (not partial_data) and l < cap[keys.CAPABILITY_INPUT_VIEW_LOWER_LIMIT]:
self._input_view_err.set_message(
"Number of input views '%s' is less than the component lower limit for input views '%s'" %
(l, cap[keys.CAPABILITY_INPUT_VIEW_LOWER_LIMIT]))
elif l > cap[keys.CAPABILITY_INPUT_VIEW_UPPER_LIMIT]:
self._input_view_err.set_message(
"Number of input views '%s' is greater than the component upper limit for input views '%s'"
% (l, cap[keys.CAPABILITY_INPUT_VIEW_UPPER_LIMIT]))
if not cap[keys.CAPABILITY_INPUT_VIEW_ALLOW_BINARY]:
# Make sure that none of the input views violate this binary restriction
for inp_name in res_dict[keys.INPUT_VIEWS]:
if not res_dict[keys.INPUT_VIEWS][inp_name][keys.VIEW_IS_RECORD]:
self._input_view_err[inp_name].set_message(
"Component does not support binary input views, but input view '%s' is in binary mode" %
inp_name)
l = len(res_dict[keys.OUTPUT_VIEWS])
if (not partial_data) and l < cap[keys.CAPABILITY_OUTPUT_VIEW_LOWER_LIMIT]:
self._output_view_err.set_message(
"Number of output views '%s' is less than the component lower limit for output views '%s'"
% (l, cap[keys.CAPABILITY_OUTPUT_VIEW_LOWER_LIMIT]))
elif l > cap[keys.CAPABILITY_OUTPUT_VIEW_UPPER_LIMIT]:
self._output_view_err.set_message(
"Number of output views '%s' is greater than the component upper limit for output views '%s'"
% (l, cap[keys.CAPABILITY_OUTPUT_VIEW_UPPER_LIMIT]))
if not cap[keys.CAPABILITY_OUTPUT_VIEW_ALLOW_BINARY]:
# Make sure that none of the output views violate this binary restriction
for out_name in res_dict[keys.OUTPUT_VIEWS]:
if not res_dict[keys.OUTPUT_VIEWS][out_name][keys.VIEW_IS_RECORD]:
self._output_view_err[out_name].set_message(
"Component does not support binary output views, but output view '%s' is in binary mode" %
out_name)
if (not cap[keys.CAPABILITY_ALLOW_PASS_THROUGH]):
if keys.OUTPUT_VIEW_PASSTHROUGH in res_dict and len(res_dict[keys.OUTPUT_VIEW_PASSTHROUGH]) > 0:
self._pass_through_err.set_message(
"Component does not support pass-through, but output view(s) '%s' have been set as pass-through"
% ", ".join(res_dict[keys.OUTPUT_VIEW_PASSTHROUGH].keys()))
def _validate_views(self, res_dict):
"""
Validate that the views meet certain requirements like no duplicate field names.
@param res_dict: Resdef as a dictionary.
@type res_dict: dict
"""
for inp_name in res_dict[keys.INPUT_VIEWS]:
m = FIELD_AND_VIEW_NAME_PATTERN.search(inp_name)
if m is not None:
self._input_view_err[inp_name].set_message("Input view name '%s' has invalid character(s) '%s'" %
(inp_name, m.group(0)))
view = res_dict[keys.INPUT_VIEWS][inp_name]
if view[keys.VIEW_IS_RECORD]:
fields = view[keys.VIEW_FIELDS]
l = []
for (i, f) in enumerate(fields):
if f[1] not in list_of_field_types:
self._input_view_err[inp_name][keys.VIEW_FIELDS][i][1].set_message(
"Field type '%s' is invalid" % f[1])
if f[0] is None or len(f[0]) == 0:
self._input_view_err[inp_name][keys.VIEW_FIELDS][i][0].set_message(
"Field name '%s' is invalid" % f[0])
else:
m = FIELD_AND_VIEW_NAME_PATTERN.search(f[0])
if m is not None:
self._input_view_err[inp_name][keys.VIEW_FIELDS][i][0].set_message(
"Field name '%s' has invalid character(s) '%s'" % (f[0], m.group(0)))
elif f[0] in l:
self._input_view_err[inp_name][keys.VIEW_FIELDS][i].set_message(
"Field '%s' specified more than once" % f[0])
l.append(f[0])
for out_name in res_dict[keys.OUTPUT_VIEWS]:
m = FIELD_AND_VIEW_NAME_PATTERN.search(out_name)
if m is not None:
self._output_view_err[out_name].set_message("Output view name '%s' has invalid character(s) '%s'" %
(out_name, m.group(0)))
view = res_dict[keys.OUTPUT_VIEWS][out_name]
if view[keys.VIEW_IS_RECORD]:
fields = view[keys.VIEW_FIELDS]
l = []
for (i, f) in enumerate(fields):
if f[1] not in list_of_field_types:
self._output_view_err[out_name][keys.VIEW_FIELDS][i][1].set_message(
"Field type '%s' is invalid" % f[1])
if f[0] is None or len(f[0]) == 0:
self._output_view_err[out_name][keys.VIEW_FIELDS][i][0].set_message(
"Field name '%s' is invalid" % f[0])
else:
m = FIELD_AND_VIEW_NAME_PATTERN.search(f[0])
if m is not None:
self._output_view_err[out_name][keys.VIEW_FIELDS][i][0].set_message(
"Field name '%s' has invalid character(s) '%s'" % (f[0], m.group(0)))
elif f[0] in l:
self._output_view_err[out_name][keys.VIEW_FIELDS][i].set_message(
"Field '%s' specified more than once" % f[0])
l.append(f[0])
# Validate pass-through settings in the resdef dictionary.
output_view_names = res_dict[keys.OUTPUT_VIEWS].keys()
input_view_names = res_dict[keys.INPUT_VIEWS]
for out_name in res_dict[keys.OUTPUT_VIEW_PASSTHROUGH]:
input_views = res_dict[keys.OUTPUT_VIEW_PASSTHROUGH][out_name][keys.INPUT_VIEWS]
pass_err = self._pass_through_err[out_name]
if out_name not in output_view_names:
pass_err.set_message(
"Nonexistent output view '%s' specified in pass-through section of resource definition" % out_name)
continue
view = res_dict[keys.OUTPUT_VIEWS][out_name]
if not view[keys.VIEW_IS_RECORD]:
pass_err.set_message("Output view '%s' specified in pass-through is not a record mode view" % out_name)
if len(input_views) == 0:
pass_err.set_message("Input view names list in pass-through entry for output view '%s' is empty" %
out_name)
continue
for (i, inp_name) in enumerate(input_views):
if inp_name not in input_view_names:
pass_err[keys.PT_INPUT_VIEWS][i].set_message(
"Nonexistent input view '%s' specified in pass-through entry for output view '%s'"
% (inp_name, out_name))
continue
def __str__(self):
l = []
s = str(self._prop_err)
if s:
l.append('Property Error(s):\n%s\n' % s)
s = str(self._input_view_err)
if s:
l.append('Input View Error(s):\n%s\n' % s)
s = str(self._output_view_err)
if s:
l.append('Output View Error(s):\n%s\n' % s)
s = str(self._param_err)
if s:
l.append('Parameter Error(s):\n%s\n' % s)
s = str(self._pass_through_err)
if s:
l.append('Parameter Error(s):\n%s\n' % s)
s = str(self._category_err)
if s:
l.append('Category Error(s):\n%s\n' % s)
s = str(self._resource_ref_err)
if s:
l.append('Resource Ref Error(s):\n%s\n' % s)
return "".join(l)
def _to_resdef(self):
"""
Gathers all the error messages set and returns them in a dictionary.
@return: Error message dictionary.
@rtype: dict
"""
ret_dict = {}
err_dict = self._prop_err._to_resdef()
if err_dict is not None:
ret_dict[keys.PROPERTY_VALS] = err_dict
err_dict = self._input_view_err._to_resdef()
if err_dict is not None:
ret_dict[keys.INPUT_VIEWS] = err_dict
err_dict = self._output_view_err._to_resdef()
if err_dict is not None:
ret_dict[keys.OUTPUT_VIEWS] = err_dict
err_dict = self._param_err._to_resdef()
if err_dict is not None:
ret_dict[keys.PARAMS] = err_dict
err_dict = self._pass_through_err._to_resdef()
if err_dict is not None:
ret_dict[keys.OUTPUT_VIEW_PASSTHROUGH] = err_dict
if self._category_err is not None:
err_dict = self._category_err._to_resdef()
if err_dict is not None:
ret_dict[keys.RESOURCE_CATEGORY] = err_dict
if self._resource_ref_err is not None:
err_dict = self._resource_ref_err._to_resdef()
if err_dict is not None:
ret_dict[keys.RESOURCE_REF] = err_dict
if not len(ret_dict):
ret_dict = None
if ret_dict is None and self._err_msg is None:
return None
return {keys.LABEL: "Resource", keys.ERROR_MESSAGE : self._err_msg, keys.DICT_ERROR : ret_dict}
def set_message(self, err_msg = None):
"""
Sets an error message.
@param err_msg: Error message
@type err_msg: str
"""
self._err_msg = err_msg
def get_property_err(self, prop_name):
"""
This function returns an error object which reflects the current contents of the property.
If the property is a dictionary, then the error object has one too, with the current key names.
If the property is a list, then the error object has a corresponding list with same number of entries.
This error object will allow error message to be associated with the whole property or even with
specific list/dictionary entry within the property via set_message() methods.
For example, consider a complex property called 'Company' which has nested dictionaries and lists:
v = {}
v['name'] = 'Acme'
v['Employees'] = {}
v['Employees']['John Doe'] = {'Sex':'M',
'Age':39
'Phone': ['408-555-1234', '650-555-12']
}
v['Employees']['Jane 9 Doe'] = {'Sex':'F',
'Age':25
'Phone': ['408-555-4567']
}
self.set_property_value('Company', v)
During validation, one can set error for this complex property as follows:
# Retrieve the error object for the property
err_obj = self.get_property_err('Company')
# Set an error for the whole property if you like
err_obj.set_message('This company information is wrong')
# Or, one can set very specific error messages for a nested value in the property
err_obj['Employees']['John Doe']['Phone'][1].set_message('Phone number must have 10 digits')
# Here is another example
err_obj['Employees']['Jane 9 Doe'].set_message('Name cannot have a numeric value')
In this manner, one can associate error messages not only with the whole property, but also
with specific values inside a complex property. This capability is especially useful for
displaying error message in the GUI, right next to the incorrect value.
@param property_name: Name of the property
@type property_name: str
@return: Property error object
@rtype: Error object
@param prop_name: Property name
@type prop_name: str
@return: Error object
@rtype: L{SimplePropErr}, L{ListPropErr} or L{DictPropErr}
"""
if prop_name not in self._prop_err:
raise SnapObjNotFoundError("Property '%s' not found" % prop_name)
return self._prop_err[prop_name]
def get_input_view_err(self, view_name = None):
"""
Get error object for the specified input view name or input views in general.
If no view name is specified, error object for all views is returned.
@param view_name: View name for which error object is being requested.
@type prop_name: str
@return: Error object
@rtype: L{DictPropErr}
"""
if view_name is None:
return self._input_view_err
elif view_name in self._input_view_err:
return self._input_view_err[view_name]
else:
raise SnapObjNotFoundError("Input view '%s' not found" % view_name)
def get_output_view_err(self, view_name = None):
"""
Get error object for the specified output view name or output views in general.
If no view name is specified, error object for all output views is returned.
@param view_name: View name for which error object is being requested.
@type view_name: str
@return: Error object
@rtype: L{DictPropErr}
"""
if view_name is None:
return self._output_view_err
elif view_name in self._output_view_err:
return self._output_view_err[view_name]
else:
raise SnapObjNotFoundError("Output view '%s' not found" % view_name)
def get_pass_through_err(self, output_view_name = None):
"""
Get error object for pass through setting of the specified output view or pass through in general.
If no output view name is specified, then error object for pass through in general is returned.
@param output_view_name: Output view name for which error object is being requested.
@type output_view_name: str
@return: Error object
@rtype: L{DictPropErr}
"""
if output_view_name is None:
return self._pass_through_err
elif output_view_name in self._pass_through_err:
return self._pass_through_err[output_view_name]
else:
raise SnapObjNotFoundError("Output view '%s' not found" % output_view_name)
def get_parameter_err(self, param_name = None):
"""
Get error object for specified param or params in general.
If no param name is specified, then error object for params in general is returned.
@param param_name: Parma name for which error object is being requested.
@type param_name: str
@return: Error object
@rtype: L{DictPropErr}
"""
if param_name is None:
return self._param_err
elif param_name in self._param_err:
return self._param_err[param_name]
else:
raise SnapObjNotFoundError("Parameter '%s' not found" % param_name)
|