@@ -43,12 +43,26 @@ def fullmatch(regex, string, flags=0):
43
43
# Utility functions
44
44
# -----------------
45
45
def to_scalar_or_list (v ):
46
+ # Handle the case where 'v' is a non-native scalar-like type,
47
+ # such as numpy.float32. Without this case, the object might be
48
+ # considered numpy-convertable and therefore promoted to a
49
+ # 0-dimensional array, but we instead want it converted to a
50
+ # Python native scalar type ('float' in the example above).
51
+ # We explicitly check if is has the 'item' method, which conventionally
52
+ # converts these types to native scalars. This guards against 'v' already being
53
+ # a Python native scalar type since `numpy.isscalar` would return
54
+ # True but `numpy.asscalar` will (oddly) raise an error is called with a
55
+ # a native Python scalar object.
56
+ if np and np .isscalar (v ) and hasattr (v , 'item' ):
57
+ return np .asscalar (v )
46
58
if isinstance (v , (list , tuple )):
47
59
return [to_scalar_or_list (e ) for e in v ]
48
60
elif np and isinstance (v , np .ndarray ):
49
61
return [to_scalar_or_list (e ) for e in v ]
50
62
elif pd and isinstance (v , (pd .Series , pd .Index )):
51
63
return [to_scalar_or_list (e ) for e in v ]
64
+ elif is_numpy_convertable (v ):
65
+ return to_scalar_or_list (np .array (v ))
52
66
else :
53
67
return v
54
68
@@ -101,16 +115,19 @@ def copy_to_readonly_numpy_array(v, kind=None, force_numeric=False):
101
115
else :
102
116
# DatetimeIndex
103
117
v = v .to_pydatetime ()
104
-
105
118
if not isinstance (v , np .ndarray ):
106
- # v is not homogenous array
107
- v_list = [to_scalar_or_list (e ) for e in v ]
119
+ # v has its own logic on how to convert itself into a numpy array
120
+ if is_numpy_convertable (v ):
121
+ return copy_to_readonly_numpy_array (np .array (v ), kind = kind , force_numeric = force_numeric )
122
+ else :
123
+ # v is not homogenous array
124
+ v_list = [to_scalar_or_list (e ) for e in v ]
108
125
109
- # Lookup dtype for requested kind, if any
110
- dtype = kind_default_dtypes .get (first_kind , None )
126
+ # Lookup dtype for requested kind, if any
127
+ dtype = kind_default_dtypes .get (first_kind , None )
111
128
112
- # construct new array from list
113
- new_v = np .array (v_list , order = 'C' , dtype = dtype )
129
+ # construct new array from list
130
+ new_v = np .array (v_list , order = 'C' , dtype = dtype )
114
131
elif v .dtype .kind in numeric_kinds :
115
132
# v is a homogenous numeric array
116
133
if kind and v .dtype .kind not in kind :
@@ -148,12 +165,29 @@ def copy_to_readonly_numpy_array(v, kind=None, force_numeric=False):
148
165
return new_v
149
166
150
167
168
+ def is_numpy_convertable (v ):
169
+ """
170
+ Return whether a value is meaningfully convertable to a numpy array
171
+ via 'numpy.array'
172
+ """
173
+ return hasattr (v , '__array__' ) or hasattr (v , '__array_interface__' )
174
+
175
+
151
176
def is_homogeneous_array (v ):
152
177
"""
153
178
Return whether a value is considered to be a homogeneous array
154
- """
155
- return ((np and isinstance (v , np .ndarray )) or
156
- (pd and isinstance (v , (pd .Series , pd .Index ))))
179
+ """
180
+ if ((np and isinstance (v , np .ndarray ) or
181
+ (pd and isinstance (v , (pd .Series , pd .Index ))))):
182
+ return True
183
+ if is_numpy_convertable (v ):
184
+ v_numpy = np .array (v )
185
+ # v is essentially a scalar and so shouldn't count as an array
186
+ if v_numpy .shape == ():
187
+ return False
188
+ else :
189
+ return True
190
+ return False
157
191
158
192
159
193
def is_simple_array (v ):
@@ -1097,13 +1131,12 @@ def validate_coerce(self, v, should_raise=True):
1097
1131
# Pass None through
1098
1132
pass
1099
1133
elif self .array_ok and is_homogeneous_array (v ):
1100
-
1101
- v_array = copy_to_readonly_numpy_array (v )
1134
+ v = copy_to_readonly_numpy_array (v )
1102
1135
if (self .numbers_allowed () and
1103
- v_array .dtype .kind in ['u' , 'i' , 'f' ]):
1136
+ v .dtype .kind in ['u' , 'i' , 'f' ]):
1104
1137
# Numbers are allowed and we have an array of numbers.
1105
1138
# All good
1106
- v = v_array
1139
+ pass
1107
1140
else :
1108
1141
validated_v = [
1109
1142
self .validate_coerce (e , should_raise = False )
0 commit comments