@@ -116,6 +116,13 @@ def is_homogeneous_array(v):
116
116
(pd and isinstance (v , pd .Series )))
117
117
118
118
119
+ def is_homogeneous_ndarray (v ):
120
+ """
121
+ Return whether a value is considered to be a homogeneous array
122
+ """
123
+ return np and isinstance (v , np .ndarray )
124
+
125
+
119
126
def is_simple_array (v ):
120
127
"""
121
128
Return whether a value is considered to be an simple array
@@ -984,56 +991,78 @@ def description(self):
984
991
985
992
return valid_color_description
986
993
987
- def validate_coerce (self , v ):
994
+ def validate_coerce (self , v , should_raise = True ):
988
995
if v is None :
989
996
# Pass None through
990
997
pass
991
- elif self .array_ok and is_homogeneous_array (v ):
998
+ elif self .array_ok and (
999
+ is_homogeneous_array (v ) or
1000
+ is_homogeneous_ndarray (v )):
1001
+
992
1002
v_array = copy_to_readonly_numpy_array (v )
993
1003
if (self .numbers_allowed () and
994
1004
v_array .dtype .kind in ['u' , 'i' , 'f' ]):
995
1005
# Numbers are allowed and we have an array of numbers.
996
1006
# All good
997
1007
v = v_array
998
1008
else :
999
- validated_v = [self .vc_scalar (e ) for e in v ]
1009
+ validated_v = [
1010
+ self .validate_coerce (e , should_raise = False )
1011
+ for e in v ]
1000
1012
1001
- invalid_els = [
1002
- el for el , validated_el in zip (v , validated_v )
1003
- if validated_el is None
1004
- ]
1005
- if invalid_els :
1013
+ invalid_els = self .find_invalid_els (v , validated_v )
1014
+
1015
+ if invalid_els and should_raise :
1006
1016
self .raise_invalid_elements (invalid_els )
1007
1017
1008
1018
# ### Check that elements have valid colors types ###
1009
- if self .numbers_allowed ():
1019
+ elif self .numbers_allowed () or invalid_els :
1010
1020
v = copy_to_readonly_numpy_array (
1011
1021
validated_v , dtype = 'object' )
1012
1022
else :
1013
1023
v = copy_to_readonly_numpy_array (
1014
1024
validated_v , dtype = 'unicode' )
1015
1025
elif self .array_ok and is_simple_array (v ):
1016
- validated_v = [self .vc_scalar (e ) for e in v ]
1026
+ validated_v = [
1027
+ self .validate_coerce (e , should_raise = False )
1028
+ for e in v ]
1017
1029
1018
- invalid_els = [
1019
- el for el , validated_el in zip (v , validated_v )
1020
- if validated_el is None
1021
- ]
1030
+ invalid_els = self .find_invalid_els (v , validated_v )
1022
1031
1023
- if invalid_els :
1032
+ if invalid_els and should_raise :
1024
1033
self .raise_invalid_elements (invalid_els )
1025
-
1026
- v = validated_v
1034
+ else :
1035
+ v = validated_v
1027
1036
else :
1028
1037
# Validate scalar color
1029
1038
validated_v = self .vc_scalar (v )
1030
- if validated_v is None :
1039
+ if validated_v is None and should_raise :
1031
1040
self .raise_invalid_val (v )
1032
1041
1033
1042
v = validated_v
1034
1043
1035
1044
return v
1036
1045
1046
+ def find_invalid_els (self , orig , validated , invalid_els = None ):
1047
+ """
1048
+ Helper method to find invalid elements in orig array.
1049
+ Elements are invalid if their corresponding element in
1050
+ the validated array is None.
1051
+
1052
+ This method handles deeply nested list structures
1053
+ """
1054
+ if invalid_els is None :
1055
+ invalid_els = []
1056
+
1057
+ for orig_el , validated_el in zip (orig , validated ):
1058
+ if is_array (orig_el ):
1059
+ self .find_invalid_els (orig_el , validated_el , invalid_els )
1060
+ else :
1061
+ if validated_el is None :
1062
+ invalid_els .append (orig_el )
1063
+
1064
+ return invalid_els
1065
+
1037
1066
def vc_scalar (self , v ):
1038
1067
""" Helper to validate/coerce a scalar color """
1039
1068
return ColorValidator .perform_validate_coerce (
0 commit comments