Skip to content
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

python versions of bisect_right and bisect_left can fail guarantees #125889

Open
mike-neergaard opened this issue Oct 23, 2024 · 2 comments
Open
Labels
3.13 bugs and security fixes 3.14 new features, bugs and security fixes extension-modules C modules in the Modules dir stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error

Comments

@mike-neergaard
Copy link

mike-neergaard commented Oct 23, 2024

Bug report

Bug description:

The documentation for bisect.bisect_right and bisect.bisect_left states in both cases that

The returned insertion point ip partitions the array a into two slices such that
all(elem <= x for elem in a[lo : ip]) is true for the left slice and
all(elem > x for elem in a[ip : hi]) is true for the right slice.

But in the python version of bisect, that guarantee can fail:

import sys
import _bisect as c_bisect
sys.modules['_bisect'] = None
import bisect as py_bisect

def check_guarantees(bisect_func, a, x, lo, hi):
    ip = bisect_func(a, x, lo, hi)
    check_pass = True 
    if not all(elem <= x for elem in a[lo : ip]): 
        print("\tFAIL. Elements of",a[lo:ip]," are not <=",x)
        check_pass = False
    if not all(elem > x for elem in a[ip : hi]):
        print("\tFAIL. Elements of",a[ip:hi]," are not >",x)
        check_pass = False
    if  check_pass: print("\tOk")

#print(c_bisect.bisect_right) # <built-in function bisect_right>
#print(py_bisect.bisect_right) # <function bisect_right at 0x0000023164AE7C40>

a = [0,1,2,3,4]
x = 8
lo = 3
hi = -1

print("bisect_left from C")
check_guarantees(c_bisect.bisect_left, a, x, lo, hi)

print("bisect_left from python")
check_guarantees(py_bisect.bisect_left, a, x, lo, hi)

print("bisect_right from C")
check_guarantees(c_bisect.bisect_right, a, x, lo, hi)

print("bisect_right from python")
check_guarantees(py_bisect.bisect_right, a, x, lo, hi)

Attempt this online
Outputs :

bisect_left from C
	Ok
bisect_left from python
	FAIL. Elements of [3]  are not > 8
bisect_right from C
	Ok
bisect_right from python
	FAIL. Elements of [3]  are not > 8

I think there is an extra check in the C version for hi < 0.

CPython versions tested on:

3.13

Operating systems tested on:

Linux

@mike-neergaard mike-neergaard added the type-bug An unexpected behavior, bug, or error label Oct 23, 2024
@tomasr8 tomasr8 added stdlib Python modules in the Lib dir extension-modules C modules in the Modules dir 3.14 new features, bugs and security fixes 3.13 bugs and security fixes labels Oct 23, 2024
@tomasr8
Copy link
Member

tomasr8 commented Oct 23, 2024

I think neither the Python nor the C implementation are actually meant to work with a negative hi argument. The C implementation does special case hi == -1 but that's only because it is the default value produced by argument clinic when you omit hi (same for the Python case when hi is None).

We could either make it an error to pass a negative value as is the case with lo currently, or fix it to work with negative values.

cc @rhettinger since you last worked on this module

@mike-neergaard
Copy link
Author

Bounds checking sounds great to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.13 bugs and security fixes 3.14 new features, bugs and security fixes extension-modules C modules in the Modules dir stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

2 participants