Issue #14181: Allow memoryview construction from an object that uses the
getbuffer redirection scheme.
diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py
index 25324ef..e532460 100644
--- a/Lib/test/test_buffer.py
+++ b/Lib/test/test_buffer.py
@@ -3373,6 +3373,15 @@
         del nd
         m.release()
 
+        a = bytearray([1,2,3])
+        m = memoryview(a)
+        nd1 = ndarray(m, getbuf=PyBUF_FULL_RO, flags=ND_REDIRECT)
+        nd2 = ndarray(nd1, getbuf=PyBUF_FULL_RO, flags=ND_REDIRECT)
+        self.assertIs(nd2.obj, m)
+        self.assertRaises(BufferError, m.release)
+        del nd1, nd2
+        m.release()
+
         # chained views
         a = bytearray([1,2,3])
         m1 = memoryview(a)
@@ -3383,6 +3392,17 @@
         del nd
         m2.release()
 
+        a = bytearray([1,2,3])
+        m1 = memoryview(a)
+        m2 = memoryview(m1)
+        nd1 = ndarray(m2, getbuf=PyBUF_FULL_RO, flags=ND_REDIRECT)
+        nd2 = ndarray(nd1, getbuf=PyBUF_FULL_RO, flags=ND_REDIRECT)
+        self.assertIs(nd2.obj, m2)
+        m1.release()
+        self.assertRaises(BufferError, m2.release)
+        del nd1, nd2
+        m2.release()
+
         # Allow changing layout while buffers are exported.
         nd = ndarray([1,2,3], shape=[3], flags=ND_VAREXPORT)
         m1 = memoryview(nd)
@@ -3418,12 +3438,82 @@
             catch22(m1)
             self.assertEqual(m1[0], ord(b'1'))
 
+        x = ndarray(list(range(12)), shape=[2,2,3], format='l')
+        y = ndarray(x, getbuf=PyBUF_FULL_RO, flags=ND_REDIRECT)
+        z = ndarray(y, getbuf=PyBUF_FULL_RO, flags=ND_REDIRECT)
+        self.assertIs(z.obj, x)
+        with memoryview(z) as m:
+            catch22(m)
+            self.assertEqual(m[0:1].tolist(), [[[0, 1, 2], [3, 4, 5]]])
+
+        # Test garbage collection.
+        for flags in (0, ND_REDIRECT):
+            x = bytearray(b'123')
+            with memoryview(x) as m1:
+                del x
+                y = ndarray(m1, getbuf=PyBUF_FULL_RO, flags=flags)
+                with memoryview(y) as m2:
+                    del y
+                    z = ndarray(m2, getbuf=PyBUF_FULL_RO, flags=flags)
+                    with memoryview(z) as m3:
+                        del z
+                        catch22(m3)
+                        catch22(m2)
+                        catch22(m1)
+                        self.assertEqual(m1[0], ord(b'1'))
+                        self.assertEqual(m2[1], ord(b'2'))
+                        self.assertEqual(m3[2], ord(b'3'))
+                        del m3
+                    del m2
+                del m1
+
+            x = bytearray(b'123')
+            with memoryview(x) as m1:
+                del x
+                y = ndarray(m1, getbuf=PyBUF_FULL_RO, flags=flags)
+                with memoryview(y) as m2:
+                    del y
+                    z = ndarray(m2, getbuf=PyBUF_FULL_RO, flags=flags)
+                    with memoryview(z) as m3:
+                        del z
+                        catch22(m1)
+                        catch22(m2)
+                        catch22(m3)
+                        self.assertEqual(m1[0], ord(b'1'))
+                        self.assertEqual(m2[1], ord(b'2'))
+                        self.assertEqual(m3[2], ord(b'3'))
+                        del m1, m2, m3
+
         # XXX If m1 has exports, raise BufferError.
         # x = bytearray(b'123')
         # with memoryview(x) as m1:
         #     ex = ndarray(m1)
         #     m1[0] == ord(b'1')
 
+    def test_memoryview_redirect(self):
+
+        nd = ndarray([1.0 * x for x in range(12)], shape=[12], format='d')
+        a = array.array('d', [1.0 * x for x in range(12)])
+
+        for x in (nd, a):
+            y = ndarray(x, getbuf=PyBUF_FULL_RO, flags=ND_REDIRECT)
+            z = ndarray(y, getbuf=PyBUF_FULL_RO, flags=ND_REDIRECT)
+            m = memoryview(z)
+
+            self.assertIs(y.obj, x)
+            self.assertIs(z.obj, x)
+            self.assertIs(m.obj, x)
+
+            self.assertEqual(m, x)
+            self.assertEqual(m, y)
+            self.assertEqual(m, z)
+
+            self.assertEqual(m[1:3], x[1:3])
+            self.assertEqual(m[1:3], y[1:3])
+            self.assertEqual(m[1:3], z[1:3])
+            del y, z
+            self.assertEqual(m[1:3], x[1:3])
+
     def test_issue_7385(self):
         x = ndarray([1,2,3], shape=[3], flags=ND_GETBUF_FAIL)
         self.assertRaises(BufferError, memoryview, x)