Skip to content
Merged
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
2 changes: 1 addition & 1 deletion Doc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ build:
@if [ -f ../Misc/NEWS ] ; then \
echo "Using existing Misc/NEWS file"; \
cp ../Misc/NEWS build/NEWS; \
elif $(BLURB) help >/dev/null 2>&1 && $(SPHINXBUILD) --version >/dev/null 2>&1; then \
elif $(BLURB) --version && $(SPHINXBUILD) --version ; then \
if [ -d ../Misc/NEWS.d ]; then \
echo "Building NEWS from Misc/NEWS.d with blurb"; \
$(BLURB) merge -f build/NEWS; \
Expand Down
61 changes: 61 additions & 0 deletions Lib/test/test_memoryview.py
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,67 @@ def test_array_assign(self):
m[:] = new_a
self.assertEqual(a, new_a)

def test_compare_equal(self):
# A memoryview is equal to itself: there is no need to compare
# individual values. This is not true for float values since they can
# be NaN, and NaN is not equal to itself.

def check_equal(view, is_equal):
self.assertEqual(view == view, is_equal)
self.assertEqual(view != view, not is_equal)

# Comparison with a different memoryview doesn't use
# the optimization and should give the same result.
view2 = memoryview(view)
self.assertEqual(view2 == view, is_equal)
self.assertEqual(view2 != view2, not is_equal)

# Test integer formats
for int_format in 'bBhHiIlLqQ':
with self.subTest(format=int_format):
a = array.array(int_format, [1, 2, 3])
m = memoryview(a)
check_equal(m, True)

if int_format in 'bB':
m2 = m.cast('@' + m.format)
check_equal(m2, True)

# Test 'c' format
a = array.array('B', [1, 2, 3])
m = memoryview(a.tobytes()).cast('c')
check_equal(m, True)

# Test 'n' and 'N' formats
if struct.calcsize('L') == struct.calcsize('N'):
int_format = 'L'
elif struct.calcsize('Q') == struct.calcsize('N'):
int_format = 'Q'
else:
int_format = None
if int_format:
a = array.array(int_format, [1, 2, 3])
m = memoryview(a.tobytes()).cast('N')
check_equal(m, True)
m = memoryview(a.tobytes()).cast('n')
check_equal(m, True)

# Test '?' format
m = memoryview(b'\0\1\2').cast('?')
check_equal(m, True)

# Test float formats
for float_format in 'fd':
with self.subTest(format=float_format):
a = array.array(float_format, [1.0, 2.0, float('nan')])
m = memoryview(a)
# nan is not equal to nan
check_equal(m, False)

a = array.array(float_format, [1.0, 2.0, 3.0])
m = memoryview(a)
check_equal(m, True)


class BytesMemorySliceTest(unittest.TestCase,
BaseMemorySliceTests, BaseBytesMemoryTests):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Optimize :class:`memoryview` comparison: a :class:`memoryview` is equal to
itself, there is no need to compare values. Patch by Victor Stinner.
24 changes: 24 additions & 0 deletions Objects/memoryobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -3122,6 +3122,30 @@ memory_richcompare(PyObject *v, PyObject *w, int op)
}
vv = VIEW_ADDR(v);

// For formats supported by the struct module a memoryview is equal to
// itself: there is no need to compare individual values.
// This is not true for float values since they can be NaN, and NaN
// is not equal to itself. So only use this optimization on format known to
// not use floats.
if (v == w) {
const char *format = vv->format;
if (format != NULL) {
if (*format == '@') {
format++;
}
// Include only formats known by struct, exclude float formats
// "d" (double), "f" (float) and "e" (16-bit float).
// Do not optimize "P" format.
if (format[0] != 0
&& strchr("bBchHiIlLnNqQ?", format[0]) != NULL
&& format[1] == 0)
{
equal = 1;
goto result;
}
}
}

if (PyMemoryView_Check(w)) {
if (BASE_INACCESSIBLE(w)) {
equal = (v == w);
Expand Down
Loading