@@ -116,6 +116,13 @@ def is_homogeneous_array(v):
116116 (pd and isinstance (v , pd .Series )))
117117
118118
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+
119126def is_simple_array (v ):
120127 """
121128 Return whether a value is considered to be an simple array
@@ -984,56 +991,78 @@ def description(self):
984991
985992 return valid_color_description
986993
987- def validate_coerce (self , v ):
994+ def validate_coerce (self , v , should_raise = True ):
988995 if v is None :
989996 # Pass None through
990997 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+
9921002 v_array = copy_to_readonly_numpy_array (v )
9931003 if (self .numbers_allowed () and
9941004 v_array .dtype .kind in ['u' , 'i' , 'f' ]):
9951005 # Numbers are allowed and we have an array of numbers.
9961006 # All good
9971007 v = v_array
9981008 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 ]
10001012
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 :
10061016 self .raise_invalid_elements (invalid_els )
10071017
10081018 # ### Check that elements have valid colors types ###
1009- if self .numbers_allowed ():
1019+ elif self .numbers_allowed () or invalid_els :
10101020 v = copy_to_readonly_numpy_array (
10111021 validated_v , dtype = 'object' )
10121022 else :
10131023 v = copy_to_readonly_numpy_array (
10141024 validated_v , dtype = 'unicode' )
10151025 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 ]
10171029
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 )
10221031
1023- if invalid_els :
1032+ if invalid_els and should_raise :
10241033 self .raise_invalid_elements (invalid_els )
1025-
1026- v = validated_v
1034+ else :
1035+ v = validated_v
10271036 else :
10281037 # Validate scalar color
10291038 validated_v = self .vc_scalar (v )
1030- if validated_v is None :
1039+ if validated_v is None and should_raise :
10311040 self .raise_invalid_val (v )
10321041
10331042 v = validated_v
10341043
10351044 return v
10361045
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+
10371066 def vc_scalar (self , v ):
10381067 """ Helper to validate/coerce a scalar color """
10391068 return ColorValidator .perform_validate_coerce (
0 commit comments