Repository: numpy/numpy
Pull request: MAINT: Remove the RELAXED_STRIDES_CHECKING env variable
An error is for now raised in `setup.py` if this is set, eventually
we should just delete that.

Closes gh-20220

---

Thought I would do this now, might also help with finally fixing up those UH-OHs, if just a little.

Problem statement:
MAINT: Remove the `NPY_RELAXED_STRIDES=0` compilation option 
I think we should remove the `NPY_RELAXED_STRIDES=0` compilation option.  It has been many years that `1` was the default and it adds a bit of cruft in places.
Probably, should be done after branching NumPy 1.22.  But that is (as of writing) just a few weeks away.

Personally, I am not worried about this breaking anyone.  Relying on relaxed-strides to be disabled, would mean you are compiling NumPy yourself just to avoid fixing a pretty simple issue.

Hints from issue discussion:
Yes, I think it is about time.

Commit message: MAINT: Remove the RELAXED_STRIDES_CHECKING env variable

An error is for now raised in `setup.py` if this is set, eventually
we should just delete that.

Patch to review:
diff --git a/.github/actions/action.yml b/.github/actions/action.yml
index 43a7d0c7ac03..20a239026e4f 100644
--- a/.github/actions/action.yml
+++ b/.github/actions/action.yml
@@ -7,7 +7,6 @@ runs:
     shell: bash
     run: |
       echo NPY_RELAXED_STRIDES_DEBUG $NPY_RELAXED_STRIDES_DEBUG
-      echo NPY_RELAXED_STRIDES_CHECKING $NPY_RELAXED_STRIDES_CHECKING
       echo CHECK_BLAS $CHECK_BLAS
       echo DOWNLOAD_OPENBLAS $DOWNLOAD_OPENBLAS
       echo USE_DEBUG $USE_DEBUG
diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml
index 620d9c1efd36..76a0952d8a3b 100644
--- a/.github/workflows/build_test.yml
+++ b/.github/workflows/build_test.yml
@@ -136,13 +136,13 @@ jobs:
         python-version: ${{ env.PYTHON_VERSION }}
     - uses: ./.github/actions
 
-  no_relaxed_strides:
+  relaxed_strides_debug:
     needs: [smoke_test]
     runs-on: ubuntu-latest
     env:
-      NPY_RELAXED_STRIDES_CHECKING: 0
       CHECK_BLAS: 1
       NPY_USE_BLAS_ILP64: 1
+      NPY_RELAXED_STRIDES_DEBUG: 1
     steps:
     - uses: actions/checkout@v2
       with:
@@ -158,7 +158,6 @@ jobs:
     runs-on: ubuntu-latest
     env:
       USE_WHEEL: 1
-      NPY_RELAXED_STRIDES_DEBUG: 1
     steps:
     - uses: actions/checkout@v2
       with:
diff --git a/doc/release/upcoming_changes/20220.compatibility.rst b/doc/release/upcoming_changes/20220.compatibility.rst
new file mode 100644
index 000000000000..06e422875873
--- /dev/null
+++ b/doc/release/upcoming_changes/20220.compatibility.rst
@@ -0,0 +1,5 @@
+``NPY_RELAXED_STRIDES_CHECKING`` has been removed
+-------------------------------------------------
+NumPy cannot be compiled with ``NPY_RELAXED_STRIDES_CHECKING=0``
+anymore.  Relaxed strides have been the default for many years and
+the option was initially introduced to allow a smoother transition.
diff --git a/doc/source/reference/arrays.ndarray.rst b/doc/source/reference/arrays.ndarray.rst
index 66ebb66fbe46..985a11c8818e 100644
--- a/doc/source/reference/arrays.ndarray.rst
+++ b/doc/source/reference/arrays.ndarray.rst
@@ -161,26 +161,15 @@ An array is considered aligned if the memory offsets for all elements and the
 base offset itself is a multiple of `self.itemsize`. Understanding
 `memory-alignment` leads to better performance on most hardware.
 
-.. note::
-
-    Points (1) and (2) can currently be disabled by the compile time
-    environmental variable ``NPY_RELAXED_STRIDES_CHECKING=0``,
-    which was the default before NumPy 1.10.
-    No users should have to do this. ``NPY_RELAXED_STRIDES_DEBUG=1``
-    can be used to help find errors when incorrectly relying on the strides
-    in C-extension code (see below warning).
-
-    You can check whether this option was enabled when your NumPy was
-    built by looking at the value of ``np.ones((10,1),
-    order='C').flags.f_contiguous``. If this is ``True``, then your
-    NumPy has relaxed strides checking enabled.
-
 .. warning::
 
     It does *not* generally hold that ``self.strides[-1] == self.itemsize``
     for C-style contiguous arrays or ``self.strides[0] == self.itemsize`` for
     Fortran-style contiguous arrays is true.
 
+    ``NPY_RELAXED_STRIDES_DEBUG=1`` can be used to help find errors when
+    incorrectly relying on the strides in C-extension code (see below warning).
+
 Data in new :class:`ndarrays <ndarray>` is in the :term:`row-major`
 (C) order, unless otherwise specified, but, for example, :ref:`basic
 array slicing <arrays.indexing>` often produces :term:`views <view>`
diff --git a/doc/source/reference/global_state.rst b/doc/source/reference/global_state.rst
index 20874ceaae49..81685ec7d836 100644
--- a/doc/source/reference/global_state.rst
+++ b/doc/source/reference/global_state.rst
@@ -70,19 +70,15 @@ Debugging-Related Options
 Relaxed Strides Checking
 ------------------------
 
-The *compile-time* environment variables::
+The *compile-time* environment variable::
 
     NPY_RELAXED_STRIDES_DEBUG=0
-    NPY_RELAXED_STRIDES_CHECKING=1
-
-control how NumPy reports contiguity for arrays.
-The default that it is enabled and the debug mode is disabled.
-This setting should always be enabled. Setting the
-debug option can be interesting for testing code written
-in C which iterates through arrays that may or may not be
-contiguous in memory.
-Most users will have no reason to change these; for details
-see the :ref:`memory layout <memory-layout>` documentation.
+
+can be set to help debug code written in C which iteraters through arrays
+manually.  When an array is contiguous and iterated in a contiguous manner,
+its ``strides`` should not be queried.  This option can help find errors where
+the ``strides`` are incorrectly used.
+For details see the :ref:`memory layout <memory-layout>` documentation.
 
 
 Warn if no memory allocation policy when deallocating data
diff --git a/numpy/core/include/numpy/ndarraytypes.h b/numpy/core/include/numpy/ndarraytypes.h
index 35d82ec036bd..e8479e31696b 100644
--- a/numpy/core/include/numpy/ndarraytypes.h
+++ b/numpy/core/include/numpy/ndarraytypes.h
@@ -834,11 +834,9 @@ typedef int (PyArray_FinalizeFunc)(PyArrayObject *, PyObject *);
  * 1-d array is C_CONTIGUOUS it is also F_CONTIGUOUS. Arrays with
  * more then one dimension can be C_CONTIGUOUS and F_CONTIGUOUS
  * at the same time if they have either zero or one element.
- * If NPY_RELAXED_STRIDES_CHECKING is set, a higher dimensional
- * array is always C_CONTIGUOUS and F_CONTIGUOUS if it has zero elements
- * and the array is contiguous if ndarray.squeeze() is contiguous.
- * I.e. dimensions for which `ndarray.shape[dimension] == 1` are
- * ignored.
+ * A higher dimensional array is always C_CONTIGUOUS and F_CONTIGUOUS if it
+ * has zero elements or when `array.squeeze()` is contiguous.
+ * I.e. dimensions for which `array.shape[dimension] == 1` are ignored.
  */
 
 /*
diff --git a/numpy/core/setup.py b/numpy/core/setup.py
index f087c88269c9..af35b1322fb8 100644
--- a/numpy/core/setup.py
+++ b/numpy/core/setup.py
@@ -17,6 +17,10 @@
 # Set to True to enable relaxed strides checking. This (mostly) means
 # that `strides[dim]` is ignored if `shape[dim] == 1` when setting flags.
 NPY_RELAXED_STRIDES_CHECKING = (os.environ.get('NPY_RELAXED_STRIDES_CHECKING', "1") != "0")
+if not NPY_RELAXED_STRIDES_CHECKING:
+    raise SystemError(
+        "Support for NPY_RELAXED_STRIDES_CHECKING=0 has been remove as of "
+        "NumPy 1.23.  This error will eventually be removed entirely.")
 
 # Put NPY_RELAXED_STRIDES_DEBUG=1 in the environment if you want numpy to use a
 # bogus value for affected strides in order to help smoke out bad stride usage
@@ -481,13 +485,9 @@ def generate_config_h(ext, build_dir):
             if can_link_svml():
                 moredefs.append(('NPY_CAN_LINK_SVML', 1))
 
-            # Use relaxed stride checking
-            if NPY_RELAXED_STRIDES_CHECKING:
-                moredefs.append(('NPY_RELAXED_STRIDES_CHECKING', 1))
-            else:
-                moredefs.append(('NPY_RELAXED_STRIDES_CHECKING', 0))
-
-            # Use bogus stride debug aid when relaxed strides are enabled
+            # Use bogus stride debug aid to flush out bugs where users use
+            # strides of dimensions with length 1 to index a full contiguous
+            # array.
             if NPY_RELAXED_STRIDES_DEBUG:
                 moredefs.append(('NPY_RELAXED_STRIDES_DEBUG', 1))
             else:
@@ -583,9 +583,6 @@ def generate_numpyconfig_h(ext, build_dir):
             moredefs.extend(cocache.check_ieee_macros(config_cmd)[1])
             moredefs.extend(cocache.check_complex(config_cmd, mathlibs)[1])
 
-            if NPY_RELAXED_STRIDES_CHECKING:
-                moredefs.append(('NPY_RELAXED_STRIDES_CHECKING', 1))
-
             if NPY_RELAXED_STRIDES_DEBUG:
                 moredefs.append(('NPY_RELAXED_STRIDES_DEBUG', 1))
 
diff --git a/numpy/core/src/common/array_assign.c b/numpy/core/src/common/array_assign.c
index b7495fc09930..956e55d3067d 100644
--- a/numpy/core/src/common/array_assign.c
+++ b/numpy/core/src/common/array_assign.c
@@ -110,7 +110,6 @@ raw_array_is_aligned(int ndim, npy_intp const *shape,
         int i;
 
         for (i = 0; i < ndim; i++) {
-#if NPY_RELAXED_STRIDES_CHECKING
             /* skip dim == 1 as it is not required to have stride 0 */
             if (shape[i] > 1) {
                 /* if shape[i] == 1, the stride is never used */
@@ -120,9 +119,6 @@ raw_array_is_aligned(int ndim, npy_intp const *shape,
                 /* an array with zero elements is always aligned */
                 return 1;
             }
-#else /* not NPY_RELAXED_STRIDES_CHECKING */
-            align_check |= (npy_uintp)strides[i];
-#endif /* not NPY_RELAXED_STRIDES_CHECKING */
         }
 
         return npy_is_aligned((void *)align_check, alignment);
diff --git a/numpy/core/src/multiarray/buffer.c b/numpy/core/src/multiarray/buffer.c
index 13d7038d3448..0307d41a8b31 100644
--- a/numpy/core/src/multiarray/buffer.c
+++ b/numpy/core/src/multiarray/buffer.c
@@ -498,14 +498,11 @@ _buffer_info_new(PyObject *obj, int flags)
             assert((size_t)info->shape % sizeof(npy_intp) == 0);
             info->strides = info->shape + PyArray_NDIM(arr);
 
-#if NPY_RELAXED_STRIDES_CHECKING
             /*
-             * When NPY_RELAXED_STRIDES_CHECKING is used, some buffer users
-             * may expect a contiguous buffer to have well formatted strides
-             * also when a dimension is 1, but we do not guarantee this
-             * internally. Thus, recalculate strides for contiguous arrays.
-             * (This is unnecessary, but has no effect in the case where
-             * NPY_RELAXED_STRIDES CHECKING is disabled.)
+             * Some buffer users may expect a contiguous buffer to have well
+             * formatted strides also when a dimension is 1, but we do not
+             * guarantee this internally. Thus, recalculate strides for
+             * contiguous arrays.
              */
             int f_contiguous = (flags & PyBUF_F_CONTIGUOUS) == PyBUF_F_CONTIGUOUS;
             if (PyArray_IS_C_CONTIGUOUS(arr) && !(
@@ -526,11 +523,6 @@ _buffer_info_new(PyObject *obj, int flags)
                 }
             }
             else {
-#else  /* NPY_RELAXED_STRIDES_CHECKING */
-            /* We can always use the arrays strides directly */
-            {
-#endif
-
                 for (k = 0; k < PyArray_NDIM(arr); ++k) {
                     info->shape[k] = PyArray_DIMS(arr)[k];
                     info->strides[k] = PyArray_STRIDES(arr)[k];
@@ -708,8 +700,8 @@ _buffer_get_info(void **buffer_info_cache_ptr, PyObject *obj, int flags)
          if (info->ndim > 1 && next_info != NULL) {
              /*
               * Some arrays are C- and F-contiguous and if they have more
-              * than one dimension, the buffer-info may differ between
-              * the two due to RELAXED_STRIDES_CHECKING.
+              * than one dimension, the buffer-info may differ between the
+              * two because strides for length 1 dimension may be adjusted.
               * If we export both buffers, the first stored one may be
               * the one for the other contiguity, so check both.
               * This is generally very unlikely in all other cases, since
diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c
index c2842d7bacc7..58ba0c2a143b 100644
--- a/numpy/core/src/multiarray/ctors.c
+++ b/numpy/core/src/multiarray/ctors.c
@@ -4020,7 +4020,6 @@ _array_fill_strides(npy_intp *strides, npy_intp const *dims, int nd, size_t item
                     int inflag, int *objflags)
 {
     int i;
-#if NPY_RELAXED_STRIDES_CHECKING
     npy_bool not_cf_contig = 0;
     npy_bool nod = 0; /* A dim != 1 was found */
 
@@ -4034,7 +4033,6 @@ _array_fill_strides(npy_intp *strides, npy_intp const *dims, int nd, size_t item
             nod = 1;
         }
     }
-#endif /* NPY_RELAXED_STRIDES_CHECKING */
 
     /* Only make Fortran strides if not contiguous as well */
     if ((inflag & (NPY_ARRAY_F_CONTIGUOUS|NPY_ARRAY_C_CONTIGUOUS)) ==
@@ -4044,7 +4042,6 @@ _array_fill_strides(npy_intp *strides, npy_intp const *dims, int nd, size_t item
             if (dims[i]) {
                 itemsize *= dims[i];
             }
-#if NPY_RELAXED_STRIDES_CHECKING
             else {
                 not_cf_contig = 0;
             }
@@ -4054,13 +4051,8 @@ _array_fill_strides(npy_intp *strides, npy_intp const *dims, int nd, size_t item
                 strides[i] = NPY_MAX_INTP;
             }
 #endif /* NPY_RELAXED_STRIDES_DEBUG */
-#endif /* NPY_RELAXED_STRIDES_CHECKING */
         }
-#if NPY_RELAXED_STRIDES_CHECKING
         if (not_cf_contig) {
-#else /* not NPY_RELAXED_STRIDES_CHECKING */
-        if ((nd > 1) && ((strides[0] != strides[nd-1]) || (dims[nd-1] > 1))) {
-#endif /* not NPY_RELAXED_STRIDES_CHECKING */
             *objflags = ((*objflags)|NPY_ARRAY_F_CONTIGUOUS) &
                                             ~NPY_ARRAY_C_CONTIGUOUS;
         }
@@ -4074,7 +4066,6 @@ _array_fill_strides(npy_intp *strides, npy_intp const *dims, int nd, size_t item
             if (dims[i]) {
                 itemsize *= dims[i];
             }
-#if NPY_RELAXED_STRIDES_CHECKING
             else {
                 not_cf_contig = 0;
             }
@@ -4084,13 +4075,8 @@ _array_fill_strides(npy_intp *strides, npy_intp const *dims, int nd, size_t item
                 strides[i] = NPY_MAX_INTP;
             }
 #endif /* NPY_RELAXED_STRIDES_DEBUG */
-#endif /* NPY_RELAXED_STRIDES_CHECKING */
         }
-#if NPY_RELAXED_STRIDES_CHECKING
         if (not_cf_contig) {
-#else /* not NPY_RELAXED_STRIDES_CHECKING */
-        if ((nd > 1) && ((strides[0] != strides[nd-1]) || (dims[0] > 1))) {
-#endif /* not NPY_RELAXED_STRIDES_CHECKING */
             *objflags = ((*objflags)|NPY_ARRAY_C_CONTIGUOUS) &
                                             ~NPY_ARRAY_F_CONTIGUOUS;
         }
diff --git a/numpy/core/src/multiarray/flagsobject.c b/numpy/core/src/multiarray/flagsobject.c
index b5bd7c8c1b46..adbfb22e7f5d 100644
--- a/numpy/core/src/multiarray/flagsobject.c
+++ b/numpy/core/src/multiarray/flagsobject.c
@@ -105,20 +105,11 @@ PyArray_UpdateFlags(PyArrayObject *ret, int flagmask)
  *
  * According to these rules, a 0- or 1-dimensional array is either both
  * C- and F-contiguous, or neither; and an array with 2+ dimensions
- * can be C- or F- contiguous, or neither, but not both. Though there
- * there are exceptions for arrays with zero or one item, in the first
- * case the check is relaxed up to and including the first dimension
- * with shape[i] == 0. In the second case `strides == itemsize` will
- * can be true for all dimensions and both flags are set.
- *
- * When NPY_RELAXED_STRIDES_CHECKING is set, we use a more accurate
- * definition of C- and F-contiguity, in which all 0-sized arrays are
- * contiguous (regardless of dimensionality), and if shape[i] == 1
- * then we ignore strides[i] (since it has no affect on memory layout).
- * With these new rules, it is possible for e.g. a 10x1 array to be both
- * C- and F-contiguous -- but, they break downstream code which assumes
- * that for contiguous arrays strides[-1] (resp. strides[0]) always
- * contains the itemsize.
+ * can be C- or F- contiguous, or neither, but not both (unless it has only
+ * a single element).
+ * We correct this, however.  When a dimension has length 1, its stride is
+ * never used and thus has no effect on the  memory layout.
+ * The above rules thus only apply when ignorning all size 1 dimenions.
  */
 static void
 _UpdateContiguousFlags(PyArrayObject *ap)
@@ -131,7 +122,6 @@ _UpdateContiguousFlags(PyArrayObject *ap)
     sd = PyArray_ITEMSIZE(ap);
     for (i = PyArray_NDIM(ap) - 1; i >= 0; --i) {
         dim = PyArray_DIMS(ap)[i];
-#if NPY_RELAXED_STRIDES_CHECKING
         /* contiguous by definition */
         if (dim == 0) {
             PyArray_ENABLEFLAGS(ap, NPY_ARRAY_C_CONTIGUOUS);
@@ -144,17 +134,6 @@ _UpdateContiguousFlags(PyArrayObject *ap)
             }
             sd *= dim;
         }
-#else /* not NPY_RELAXED_STRIDES_CHECKING */
-        if (PyArray_STRIDES(ap)[i] != sd) {
-            is_c_contig = 0;
-            break;
-        }
-        /* contiguous, if it got this far */
-        if (dim == 0) {
-            break;
-        }
-        sd *= dim;
-#endif /* not NPY_RELAXED_STRIDES_CHECKING */
     }
     if (is_c_contig) {
         PyArray_ENABLEFLAGS(ap, NPY_ARRAY_C_CONTIGUOUS);
@@ -167,7 +146,6 @@ _UpdateContiguousFlags(PyArrayObject *ap)
     sd = PyArray_ITEMSIZE(ap);
     for (i = 0; i < PyArray_NDIM(ap); ++i) {
         dim = PyArray_DIMS(ap)[i];
-#if NPY_RELAXED_STRIDES_CHECKING
         if (dim != 1) {
             if (PyArray_STRIDES(ap)[i] != sd) {
                 PyArray_CLEARFLAGS(ap, NPY_ARRAY_F_CONTIGUOUS);
@@ -175,16 +153,6 @@ _UpdateContiguousFlags(PyArrayObject *ap)
             }
             sd *= dim;
         }
-#else /* not NPY_RELAXED_STRIDES_CHECKING */
-        if (PyArray_STRIDES(ap)[i] != sd) {
-            PyArray_CLEARFLAGS(ap, NPY_ARRAY_F_CONTIGUOUS);
-            return;
-        }
-        if (dim == 0) {
-            break;
-        }
-        sd *= dim;
-#endif /* not NPY_RELAXED_STRIDES_CHECKING */
     }
     PyArray_ENABLEFLAGS(ap, NPY_ARRAY_F_CONTIGUOUS);
     return;
diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c
index a7b6898e17c2..947e54d0a718 100644
--- a/numpy/core/src/multiarray/multiarraymodule.c
+++ b/numpy/core/src/multiarray/multiarraymodule.c
@@ -1533,8 +1533,9 @@ PyArray_EquivTypenums(int typenum1, int typenum2)
 
 /*** END C-API FUNCTIONS **/
 /*
- * NPY_RELAXED_STRIDES_CHECKING: If the strides logic is changed, the
- * order specific stride setting is not necessary.
+ * NOTE: The order specific stride setting is not necessary to preserve
+ *       contiguity and could be removed.  However, this way the resulting
+ *       strides do currently look clearer for fortran order inputs.
  */
 static NPY_STEALS_REF_TO_ARG(1) PyObject *
 _prepend_ones(PyArrayObject *arr, int nd, int ndmin, NPY_ORDER order)
diff --git a/numpy/core/src/multiarray/shape.c b/numpy/core/src/multiarray/shape.c
index 162abd6a49c8..98f65415b79f 100644
--- a/numpy/core/src/multiarray/shape.c
+++ b/numpy/core/src/multiarray/shape.c
@@ -244,11 +244,9 @@ PyArray_Newshape(PyArrayObject *self, PyArray_Dims *newdims,
      * in order to get the right orientation and
      * because we can't just re-use the buffer with the
      * data in the order it is in.
-     * NPY_RELAXED_STRIDES_CHECKING: size check is unnecessary when set.
      */
     Py_INCREF(self);
-    if ((PyArray_SIZE(self) > 1) &&
-        ((order == NPY_CORDER && !PyArray_IS_C_CONTIGUOUS(self)) ||
+    if (((order == NPY_CORDER && !PyArray_IS_C_CONTIGUOUS(self)) ||
          (order == NPY_FORTRANORDER && !PyArray_IS_F_CONTIGUOUS(self)))) {
         int success = 0;
         success = _attempt_nocopy_reshape(self, ndim, dimensions,
@@ -1000,7 +998,6 @@ PyArray_Flatten(PyArrayObject *a, NPY_ORDER order)
  *          If an axis flagged for removal has a shape larger than one,
  *          the aligned flag (and in the future the contiguous flags),
  *          may need explicit update.
- *          (check also NPY_RELAXED_STRIDES_CHECKING)
  *
  * For example, this can be used to remove the reduction axes
  * from a reduction result once its computation is complete.
@@ -1024,6 +1021,6 @@ PyArray_RemoveAxesInPlace(PyArrayObject *arr, const npy_bool *flags)
     /* The final number of dimensions */
     fa->nd = idim_out;
 
-    /* May not be necessary for NPY_RELAXED_STRIDES_CHECKING (see comment) */
+    /* NOTE: This is only necessary if a dimension with size != 1 was removed */
     PyArray_UpdateFlags(arr, NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_F_CONTIGUOUS);
 }
diff --git a/numpy/core/tests/test_api.py b/numpy/core/tests/test_api.py
index 22a06619c4f8..b3f3e947d87e 100644
--- a/numpy/core/tests/test_api.py
+++ b/numpy/core/tests/test_api.py
@@ -8,9 +8,6 @@
      HAS_REFCOUNT
     )
 
-# Switch between new behaviour when NPY_RELAXED_STRIDES_CHECKING is set.
-NPY_RELAXED_STRIDES_CHECKING = np.ones((10, 1), order='C').flags.f_contiguous
-
 
 def test_array_array():
     tobj = type(object)
@@ -482,13 +479,6 @@ def check_copy_result(x, y, ccontig, fcontig, strides=False):
         assert_equal(x, y)
         assert_equal(res.flags.c_contiguous, ccontig)
         assert_equal(res.flags.f_contiguous, fcontig)
-        # This check is impossible only because
-        # NPY_RELAXED_STRIDES_CHECKING changes the strides actively
-        if not NPY_RELAXED_STRIDES_CHECKING:
-            if strides:
-                assert_equal(x.strides, y.strides)
-            else:
-                assert_(x.strides != y.strides)
 
     # Validate the initial state of a, b, and c
     assert_(a.flags.c_contiguous)
@@ -542,8 +532,7 @@ def check_copy_result(x, y, ccontig, fcontig, strides=False):
 
 def test_contiguous_flags():
     a = np.ones((4, 4, 1))[::2,:,:]
-    if NPY_RELAXED_STRIDES_CHECKING:
-        a.strides = a.strides[:2] + (-123,)
+    a.strides = a.strides[:2] + (-123,)
     b = np.ones((2, 2, 1, 2, 2)).swapaxes(3, 4)
 
     def check_contig(a, ccontig, fcontig):
@@ -553,12 +542,8 @@ def check_contig(a, ccontig, fcontig):
     # Check if new arrays are correct:
     check_contig(a, False, False)
     check_contig(b, False, False)
-    if NPY_RELAXED_STRIDES_CHECKING:
-        check_contig(np.empty((2, 2, 0, 2, 2)), True, True)
-        check_contig(np.array([[[1], [2]]], order='F'), True, True)
-    else:
-        check_contig(np.empty((2, 2, 0, 2, 2)), True, False)
-        check_contig(np.array([[[1], [2]]], order='F'), False, True)
+    check_contig(np.empty((2, 2, 0, 2, 2)), True, True)
+    check_contig(np.array([[[1], [2]]], order='F'), True, True)
     check_contig(np.empty((2, 2)), True, False)
     check_contig(np.empty((2, 2), order='F'), False, True)
 
@@ -567,18 +552,11 @@ def check_contig(a, ccontig, fcontig):
     check_contig(np.array(a, copy=False, order='C'), True, False)
     check_contig(np.array(a, ndmin=4, copy=False, order='F'), False, True)
 
-    if NPY_RELAXED_STRIDES_CHECKING:
-        # Check slicing update of flags and :
-        check_contig(a[0], True, True)
-        check_contig(a[None, ::4, ..., None], True, True)
-        check_contig(b[0, 0, ...], False, True)
-        check_contig(b[:,:, 0:0,:,:], True, True)
-    else:
-        # Check slicing update of flags:
-        check_contig(a[0], True, False)
-        # Would be nice if this was C-Contiguous:
-        check_contig(a[None, 0, ..., None], False, False)
-        check_contig(b[0, 0, 0, ...], False, True)
+    # Check slicing update of flags and :
+    check_contig(a[0], True, True)
+    check_contig(a[None, ::4, ..., None], True, True)
+    check_contig(b[0, 0, ...], False, True)
+    check_contig(b[:, :, 0:0, :, :], True, True)
 
     # Test ravel and squeeze.
     check_contig(a.ravel(), True, True)
diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py
index 73bb5e2d82cb..4e23625470b1 100644
--- a/numpy/core/tests/test_multiarray.py
+++ b/numpy/core/tests/test_multiarray.py
@@ -3295,11 +3295,11 @@ def test_ravel(self):
         assert_equal(a.ravel('C'), [3, 2, 1, 0])
         assert_equal(a.ravel('K'), [3, 2, 1, 0])
 
-        # 1-element tidy strides test (NPY_RELAXED_STRIDES_CHECKING):
+        # 1-element tidy strides test:
         a = np.array([[1]])
         a.strides = (123, 432)
-        # If the stride is not 8, NPY_RELAXED_STRIDES_CHECKING is messing
-        # them up on purpose:
+        # If the following stride is not 8, NPY_RELAXED_STRIDES_DEBUG is
+        # messing them up on purpose:
         if np.ones(1).strides == (8,):
             assert_(np.may_share_memory(a.ravel('K'), a))
             assert_equal(a.ravel('K').strides, (a.dtype.itemsize,))
@@ -7540,7 +7540,7 @@ def test_export_record(self):
             assert_equal(y.format, 'T{b:a:=h:b:i:c:l:d:q:dx:B:e:@H:f:=I:g:L:h:Q:hx:f:i:d:j:^g:k:=Zf:ix:Zd:jx:^Zg:kx:4s:l:=4w:m:3x:n:?:o:@e:p:}')
         else:
             assert_equal(y.format, 'T{b:a:=h:b:i:c:q:d:q:dx:B:e:@H:f:=I:g:Q:h:Q:hx:f:i:d:j:^g:k:=Zf:ix:Zd:jx:^Zg:kx:4s:l:=4w:m:3x:n:?:o:@e:p:}')
-        # Cannot test if NPY_RELAXED_STRIDES_CHECKING changes the strides
+        # Cannot test if NPY_RELAXED_STRIDES_DEBUG changes the strides
         if not (np.ones(1).strides[0] == np.iinfo(np.intp).max):
             assert_equal(y.strides, (sz,))
         assert_equal(y.itemsize, sz)
@@ -7630,10 +7630,7 @@ def test_padded_struct_array(self):
     def test_relaxed_strides(self, c=np.ones((1, 10, 10), dtype='i8')):
         # Note: c defined as parameter so that it is persistent and leak
         # checks will notice gh-16934 (buffer info cache leak).
-
-        # Check for NPY_RELAXED_STRIDES_CHECKING:
-        if np.ones((10, 1), order="C").flags.f_contiguous:
-            c.strides = (-1, 80, 8)
+        c.strides = (-1, 80, 8)  # strides need to be fixed at export
 
         assert_(memoryview(c).strides == (800, 80, 8))
 
diff --git a/numpy/core/tests/test_regression.py b/numpy/core/tests/test_regression.py
index 21cc8c1595f6..e073df376f2b 100644
--- a/numpy/core/tests/test_regression.py
+++ b/numpy/core/tests/test_regression.py
@@ -658,10 +658,10 @@ def test_reshape_zero_size(self):
         a = np.ones((0, 2))
         a.shape = (-1, 2)
 
-    # Cannot test if NPY_RELAXED_STRIDES_CHECKING changes the strides.
-    # With NPY_RELAXED_STRIDES_CHECKING the test becomes superfluous.
+    # Cannot test if NPY_RELAXED_STRIDES_DEBUG changes the strides.
+    # With NPY_RELAXED_STRIDES_DEBUG the test becomes superfluous.
     @pytest.mark.skipif(np.ones(1).strides[0] == np.iinfo(np.intp).max,
-                        reason="Using relaxed stride checking")
+                        reason="Using relaxed stride debug")
     def test_reshape_trailing_ones_strides(self):
         # GitHub issue gh-2949, bad strides for trailing ones of new shape
         a = np.zeros(12, dtype=np.int32)[::2]  # not contiguous
@@ -918,11 +918,11 @@ def test_copy_detection_corner_case(self):
         # Ticket #658
         np.indices((0, 3, 4)).T.reshape(-1, 3)
 
-    # Cannot test if NPY_RELAXED_STRIDES_CHECKING changes the strides.
-    # With NPY_RELAXED_STRIDES_CHECKING the test becomes superfluous,
+    # Cannot test if NPY_RELAXED_STRIDES_DEBUG changes the strides.
+    # With NPY_RELAXED_STRIDES_DEBUG the test becomes superfluous,
     # 0-sized reshape itself is tested elsewhere.
     @pytest.mark.skipif(np.ones(1).strides[0] == np.iinfo(np.intp).max,
-                        reason="Using relaxed stride checking")
+                        reason="Using relaxed stride debug")
     def test_copy_detection_corner_case2(self):
         # Ticket #771: strides are not set correctly when reshaping 0-sized
         # arrays
diff --git a/tox.ini b/tox.ini
index 9bc2bbac36dd..ddab072857dc 100644
--- a/tox.ini
+++ b/tox.ini
@@ -26,18 +26,13 @@
 
 [tox]
 envlist =
-  py37,py38,py39,
-  py37-not-relaxed-strides
+  py37,py38,py39
 
 [testenv]
 deps= -Ur{toxinidir}/test_requirements.txt
 changedir={envdir}
 commands={envpython} -b {toxinidir}/runtests.py --mode=full {posargs:}
 
-[testenv:py37-not-relaxed-strides]
-basepython=python3.7
-env=NPY_RELAXED_STRIDES_CHECKING=0
-
 # Not run by default. Set up the way you want then use 'tox -e debug'
 # if you want it:
 [testenv:debug]


Code review comments:
diff_hunk: @@ -834,11 +834,9 @@ typedef int (PyArray_FinalizeFunc)(PyArrayObject *, PyObject *);
  * 1-d array is C_CONTIGUOUS it is also F_CONTIGUOUS. Arrays with
  * more then one dimension can be C_CONTIGUOUS and F_CONTIGUOUS
  * at the same time if they have either zero or one element.
- * If NPY_RELAXED_STRIDES_CHECKING is set, a higher dimensional
- * array is always C_CONTIGUOUS and F_CONTIGUOUS if it has zero elements
- * and the array is contiguous if ndarray.squeeze() is contiguous.
- * I.e. dimensions for which `ndarray.shape[dimension] == 1` are
- * ignored.
+ * A higher dimensional array is always C_CONTIGUOUS and F_CONTIGUOUS if it
path: numpy/core/include/numpy/ndarraytypes.h
text: @user1:
Maybe "or" instead of "and"? This make is sound like the array is both, which is probably true for empty arrays, but squeezed arrays might be one of the other. Maybe rewrite?