Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Fix for issue 6061: unsafe casting of int/bool to str #6067

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 0 additions & 48 deletions numpy/core/src/multiarray/convert_datatype.c
Original file line number Diff line number Diff line change
Expand Up @@ -510,54 +510,6 @@ PyArray_CanCastTo(PyArray_Descr *from, PyArray_Descr *to)
return can_cast_timedelta64_metadata(meta1, meta2,
NPY_SAFE_CASTING);
}
/*
* If to_type_num is STRING or unicode
* see if the length is long enough to hold the
* stringified value of the object.
*/
else if (to_type_num == NPY_STRING || to_type_num == NPY_UNICODE) {
/*
* Boolean value cast to string type is 5 characters max
* for string 'False'.
*/
int char_size = 1;
if (to_type_num == NPY_UNICODE) {
char_size = 4;
}

ret = 0;
if (to->elsize == 0) {
ret = 1;
}
/*
* Need at least 5 characters to convert from boolean
* to 'True' or 'False'.
*/
else if (from->kind == 'b' && to->elsize >= 5 * char_size) {
ret = 1;
}
else if (from->kind == 'u') {
/* Guard against unexpected integer size */
if (from->elsize > 8 || from->elsize < 0) {
ret = 0;
}
else if (to->elsize >=
REQUIRED_STR_LEN[from->elsize] * char_size) {
ret = 1;
}
}
else if (from->kind == 'i') {
/* Guard against unexpected integer size */
if (from->elsize > 8 || from->elsize < 0) {
ret = 0;
}
/* Extra character needed for sign */
else if (to->elsize >=
(REQUIRED_STR_LEN[from->elsize] + 1) * char_size) {
ret = 1;
}
}
}
}
return ret;
}
Expand Down
6 changes: 4 additions & 2 deletions numpy/core/src/multiarray/scalartypes.c.src
Original file line number Diff line number Diff line change
Expand Up @@ -3825,6 +3825,8 @@ initialize_casting_tables(void)

_npy_can_cast_safely_table[NPY_STRING][NPY_UNICODE] = 1;
_npy_can_cast_safely_table[NPY_BOOL][NPY_TIMEDELTA] = 1;
_npy_can_cast_safely_table[NPY_BOOL][NPY_STRING] = 0;
_npy_can_cast_safely_table[NPY_BOOL][NPY_UNICODE] = 0;

#ifndef NPY_SIZEOF_BYTE
#define NPY_SIZEOF_BYTE 1
Expand Down Expand Up @@ -3858,8 +3860,8 @@ initialize_casting_tables(void)
#define _FROM_BSIZE NPY_SIZEOF_@FROM_BASENAME@
#define _FROM_NUM (NPY_@FROM_NAME@)

_npy_can_cast_safely_table[_FROM_NUM][NPY_STRING] = 1;
_npy_can_cast_safely_table[_FROM_NUM][NPY_UNICODE] = 1;
_npy_can_cast_safely_table[_FROM_NUM][NPY_STRING] = 0;
_npy_can_cast_safely_table[_FROM_NUM][NPY_UNICODE] = 0;

/* Allow casts from any integer to the TIMEDELTA type */
#if @from_isint@ || @from_isuint@
Expand Down
49 changes: 28 additions & 21 deletions numpy/core/tests/test_numeric.py
Original file line number Diff line number Diff line change
Expand Up @@ -766,7 +766,7 @@ def test_can_cast(self):

assert_(np.can_cast('i8', 'f8'))
assert_(not np.can_cast('i8', 'f4'))
assert_(np.can_cast('i4', 'S11'))
assert_(np.can_cast('i4', 'S11', 'unsafe'))

assert_(np.can_cast('i8', 'i8', 'no'))
assert_(not np.can_cast('<i8', '>i8', 'no'))
Expand All @@ -782,57 +782,64 @@ def test_can_cast(self):

assert_(np.can_cast('<i8', '>u4', 'unsafe'))

assert_(np.can_cast('bool', 'S5'))
assert_(np.can_cast('bool', 'S5', 'unsafe'))

assert_(not np.can_cast('bool', 'S4'))

assert_(np.can_cast('b', 'S4'))
assert_(np.can_cast('b', 'S4', 'unsafe'))
assert_(not np.can_cast('b', 'S3'))

assert_(np.can_cast('u1', 'S3'))
assert_(np.can_cast('u1', 'S3', 'unsafe'))
assert_(not np.can_cast('u1', 'S2'))
assert_(np.can_cast('u2', 'S5'))
assert_(np.can_cast('u2', 'S5', 'unsafe'))
assert_(not np.can_cast('u2', 'S4'))
assert_(np.can_cast('u4', 'S10'))
assert_(np.can_cast('u4', 'S10', 'unsafe'))
assert_(not np.can_cast('u4', 'S9'))
assert_(np.can_cast('u8', 'S20'))
assert_(np.can_cast('u8', 'S20', 'unsafe'))
assert_(not np.can_cast('u8', 'S19'))

assert_(np.can_cast('i1', 'S4'))
assert_(np.can_cast('i1', 'S4', 'unsafe'))
assert_(not np.can_cast('i1', 'S3'))
assert_(np.can_cast('i2', 'S6'))
assert_(np.can_cast('i2', 'S6', 'unsafe'))
assert_(not np.can_cast('i2', 'S5'))
assert_(np.can_cast('i4', 'S11'))
assert_(np.can_cast('i4', 'S11', 'unsafe'))
assert_(not np.can_cast('i4', 'S10'))
assert_(np.can_cast('i8', 'S21'))
assert_(np.can_cast('i8', 'S21', 'unsafe'))
assert_(not np.can_cast('i8', 'S20'))

assert_(np.can_cast('bool', 'S5'))
assert_(np.can_cast('bool', 'S5', 'unsafe'))
assert_(not np.can_cast('bool', 'S4'))

assert_(np.can_cast('b', 'U4'))
assert_(np.can_cast('b', 'U4', 'unsafe'))
assert_(not np.can_cast('b', 'U3'))

assert_(np.can_cast('u1', 'U3'))
assert_(np.can_cast('u1', 'U3', 'unsafe'))
assert_(not np.can_cast('u1', 'U2'))
assert_(np.can_cast('u2', 'U5'))
assert_(np.can_cast('u2', 'U5', 'unsafe'))
assert_(not np.can_cast('u2', 'U4'))
assert_(np.can_cast('u4', 'U10'))
assert_(np.can_cast('u4', 'U10', 'unsafe'))
assert_(not np.can_cast('u4', 'U9'))
assert_(np.can_cast('u8', 'U20'))
assert_(np.can_cast('u8', 'U20', 'unsafe'))
assert_(not np.can_cast('u8', 'U19'))

assert_(np.can_cast('i1', 'U4'))
assert_(np.can_cast('i1', 'U4', 'unsafe'))
assert_(not np.can_cast('i1', 'U3'))
assert_(np.can_cast('i2', 'U6'))
assert_(np.can_cast('i2', 'U6', 'unsafe'))
assert_(not np.can_cast('i2', 'U5'))
assert_(np.can_cast('i4', 'U11'))
assert_(np.can_cast('i4', 'U11', 'unsafe'))
assert_(not np.can_cast('i4', 'U10'))
assert_(np.can_cast('i8', 'U21'))
assert_(np.can_cast('i8', 'U21', 'unsafe'))
assert_(not np.can_cast('i8', 'U20'))

assert_raises(TypeError, np.can_cast, 'i4', None)
assert_raises(TypeError, np.can_cast, None, 'i4')

# build a list of all integer types, add bool type to that list
# then assert that none of those should be safe to cast to string
int_types = np.typecodes['AllInteger'] + '?'
for t in int_types:
assert_(not np.can_cast(t, np.str_))


# Custom exception class to test exception propagation in fromiter
class NIterError(Exception): pass
Expand Down
5 changes: 5 additions & 0 deletions numpy/core/tests/test_regression.py
Original file line number Diff line number Diff line change
Expand Up @@ -1953,6 +1953,11 @@ def test_search_sorted_invalid_arguments(self):
x = np.arange(0, 4, dtype='datetime64[D]')
assert_raises(TypeError, x.searchsorted, 1)

def test_search_sorted_unsafe_cast(self):
# Ticket #6061, should not allow safe casting from int to str.
x = np.arange(0, 10, dtype='int')
assert_raises(TypeError, x.searchsorted, '5')

def test_string_truncation(self):
# Ticket #1990 - Data can be truncated in creation of an array from a
# mixed sequence of numeric values and strings
Expand Down