Repository: numpy/numpy
Pull request: DOC: document site.cfg.example
Fixes gh-14286

Add references to `site.cfg.example` to the docs, and fix the example file's `DEFAULT` section name.

Problem statement:
DOC, BUILD: improve documentation around site.cfg
Documentation of `site.cfg` exists in `numpy/distutils/system_info.py` but that is not extracted as a docstring into the sphinx-built documentation.

`site.cfg` is also mentioned in  `./INSTALLATION.rst.txt`, but that file is also not linked into the sphinx-built documentation, the information there should be merged with the other bits and pieces about building from source and installation.

Hints from issue discussion:
`site.cfg.example` should be self-documenting, what is needed in addition is some high level docs on when/why to use it. That should go mainly into https://www.numpy.org/devdocs/user/building.html and be linked from https://www.numpy.org/devdocs/reference/distutils.html
`site.cfg.example` in master seems to be outdated:
```
# Defaults
# ========
# The settings given here will apply to all other sections if not overridden.
# This is a good place to add general library and include directories like
# /usr/local/{lib,include}
#
#[ALL]
#library_dirs = /usr/local/lib
#include_dirs = /usr/local/include
```
But the installation scripts actually does not seem to respect the ALL section; instead one has to use [DEFAULT], which is handled https://docs.python.org/2/library/configparser.html / https://docs.python.org/3.8/library/configparser.html.

(Related old issues: https://github.com/numpy/numpy/issues/1285 and
https://github.com/numpy/numpy/issues/1349)


@mkoeppe see [this comment](https://github.com/numpy/numpy/pull/15338#discussion_r368249819) on the linked PR.

Commit message: DOC: link and cleanup docstrings in site.cfg.example

Patch to review:
diff --git a/doc/source/reference/distutils.rst b/doc/source/reference/distutils.rst
index a22db3e8e3a3..fef3ee5fbdea 100644
--- a/doc/source/reference/distutils.rst
+++ b/doc/source/reference/distutils.rst
@@ -15,6 +15,11 @@ keyword arguments to pass to the setup function (by passing the
 dictionary obtained from the todict() method of the class). More
 information is available in the :ref:`distutils-user-guide`.
 
+The choice and location of linked libraries such as BLAS and LAPACK as well as
+include paths and other such build options can be specified in a ``site.cfg``
+file located in the NumPy root repository or a ``.numpy-site.cfg`` file in your
+home directory. See the ``site.cfg.example`` example file included in the NumPy
+repository or sdist for documentation.
 
 .. index::
    single: distutils
diff --git a/doc/source/user/building.rst b/doc/source/user/building.rst
index fefbb4e0b52c..c88a6cd1e0a3 100644
--- a/doc/source/user/building.rst
+++ b/doc/source/user/building.rst
@@ -35,6 +35,12 @@ Building NumPy requires the following software installed:
    installed. However, if these are available, NumPy's setup script can detect
    them and use them for building. A number of different LAPACK library setups
    can be used, including optimized LAPACK libraries such as OpenBLAS or MKL.
+   The choice and location of these libraries as well as include paths and
+   other such build options can be specified in a ``site.cfg`` file located in
+   the NumPy root repository or a ``.numpy-site.cfg`` file in your home
+   directory. See the ``site.cfg.example`` example file included in the NumPy
+   repository or sdist for documentation, and below for specifying search
+   priority from environmental variables.
 
 4) Cython
 
@@ -105,7 +111,8 @@ Accelerated BLAS/LAPACK libraries
 ---------------------------------
 
 NumPy searches for optimized linear algebra libraries such as BLAS and LAPACK.
-There are specific orders for searching these libraries, as described below.
+There are specific orders for searching these libraries, as described below and
+in the ``site.cfg.example`` file.
 
 BLAS
 ~~~~
diff --git a/numpy/distutils/system_info.py b/numpy/distutils/system_info.py
index 6a69fed59974..40ddba418917 100644
--- a/numpy/distutils/system_info.py
+++ b/numpy/distutils/system_info.py
@@ -2,58 +2,7 @@
 """
 This file defines a set of system_info classes for getting
 information about various resources (libraries, library directories,
-include directories, etc.) in the system. Currently, the following
-classes are available:
-
-  atlas_info
-  atlas_threads_info
-  atlas_blas_info
-  atlas_blas_threads_info
-  lapack_atlas_info
-  lapack_atlas_threads_info
-  atlas_3_10_info
-  atlas_3_10_threads_info
-  atlas_3_10_blas_info,
-  atlas_3_10_blas_threads_info,
-  lapack_atlas_3_10_info
-  lapack_atlas_3_10_threads_info
-  flame_info
-  blas_info
-  lapack_info
-  openblas_info
-  openblas64__info
-  openblas_ilp64_info
-  blis_info
-  blas_opt_info       # usage recommended
-  lapack_opt_info     # usage recommended
-  blas_ilp64_opt_info    # usage recommended (general ILP64 BLAS)
-  lapack_ilp64_opt_info  # usage recommended (general ILP64 LAPACK)
-  blas_ilp64_plain_opt_info    # usage recommended (general ILP64 BLAS, no symbol suffix)
-  lapack_ilp64_plain_opt_info  # usage recommended (general ILP64 LAPACK, no symbol suffix)
-  blas64__opt_info    # usage recommended (general ILP64 BLAS, 64_ symbol suffix)
-  lapack64__opt_info  # usage recommended (general ILP64 LAPACK, 64_ symbol suffix)
-  fftw_info,dfftw_info,sfftw_info
-  fftw_threads_info,dfftw_threads_info,sfftw_threads_info
-  djbfft_info
-  x11_info
-  lapack_src_info
-  blas_src_info
-  numpy_info
-  numarray_info
-  numpy_info
-  boost_python_info
-  agg2_info
-  wx_info
-  gdk_pixbuf_xlib_2_info
-  gdk_pixbuf_2_info
-  gdk_x11_2_info
-  gtkp_x11_2_info
-  gtkp_2_info
-  xft_info
-  freetype2_info
-  umfpack_info
-
-Usage:
+include directories, etc.) in the system. Usage:
     info_dict = get_info(<name>)
   where <name> is a string 'atlas','x11','fftw','lapack','blas',
   'lapack_src', 'blas_src', etc. For a complete list of allowed names,
@@ -81,19 +30,93 @@
 
 The first one found is used to get system configuration options The
 format is that used by ConfigParser (i.e., Windows .INI style). The
-section ALL has options that are the default for each section. The
-available sections are fftw, atlas, and x11. Appropriate defaults are
-used if nothing is specified.
+section ALL is not intended for general use.
+
+Appropriate defaults are used if nothing is specified.
 
 The order of finding the locations of resources is the following:
  1. environment variable
  2. section in site.cfg
- 3. ALL section in site.cfg
+ 3. DEFAULT section in site.cfg
 Only the first complete match is returned.
 
+Currently, the following classes are available, along with their section names:
+
+    Numeric_info:Numeric
+    _numpy_info:Numeric
+    _pkg_config_info:None
+    accelerate_info:accelerate
+    agg2_info:agg2
+    amd_info:amd
+    atlas_3_10_blas_info:atlas
+    atlas_3_10_blas_threads_info:atlas
+    atlas_3_10_info:atlas
+    atlas_3_10_threads_info:atlas
+    atlas_blas_info:atlas
+    atlas_blas_threads_info:atlas
+    atlas_info:atlas
+    atlas_threads_info:atlas
+    blas64__opt_info:ALL               # usage recommended (general ILP64 BLAS, 64_ symbol suffix)
+    blas_ilp64_opt_info:ALL            # usage recommended (general ILP64 BLAS)
+    blas_ilp64_plain_opt_info:ALL      # usage recommended (general ILP64 BLAS, no symbol suffix)
+    blas_info:blas
+    blas_mkl_info:mkl
+    blas_opt_info:ALL                  # usage recommended
+    blas_src_info:blas_src
+    blis_info:blis
+    boost_python_info:boost_python
+    dfftw_info:fftw
+    dfftw_threads_info:fftw
+    djbfft_info:djbfft
+    f2py_info:ALL
+    fft_opt_info:ALL
+    fftw2_info:fftw
+    fftw3_info:fftw3
+    fftw_info:fftw
+    fftw_threads_info:fftw
+    flame_info:flame
+    freetype2_info:freetype2
+    gdk_2_info:gdk_2
+    gdk_info:gdk
+    gdk_pixbuf_2_info:gdk_pixbuf_2
+    gdk_pixbuf_xlib_2_info:gdk_pixbuf_xlib_2
+    gdk_x11_2_info:gdk_x11_2
+    gtkp_2_info:gtkp_2
+    gtkp_x11_2_info:gtkp_x11_2
+    lapack64__opt_info:ALL             # usage recommended (general ILP64 LAPACK, 64_ symbol suffix)
+    lapack_atlas_3_10_info:atlas
+    lapack_atlas_3_10_threads_info:atlas
+    lapack_atlas_info:atlas
+    lapack_atlas_threads_info:atlas
+    lapack_ilp64_opt_info:ALL          # usage recommended (general ILP64 LAPACK)
+    lapack_ilp64_plain_opt_info:ALL    # usage recommended (general ILP64 LAPACK, no symbol suffix)
+    lapack_info:lapack
+    lapack_mkl_info:mkl
+    lapack_opt_info:ALL                # usage recommended
+    lapack_src_info:lapack_src
+    mkl_info:mkl
+    numarray_info:numarray
+    numerix_info:numerix
+    numpy_info:numpy
+    openblas64__info:openblas64_
+    openblas64__lapack_info:openblas64_
+    openblas_clapack_info:openblas
+    openblas_ilp64_info:openblas_ilp64
+    openblas_ilp64_lapack_info:openblas_ilp64
+    openblas_info:openblas
+    openblas_lapack_info:openblas
+    sfftw_info:fftw
+    sfftw_threads_info:fftw
+    system_info:ALL
+    umfpack_info:umfpack
+    wx_info:wx
+    x11_info:x11
+    xft_info:xft
+
 Example:
 ----------
-[ALL]
+[DEFAULT]
+# default section
 library_dirs = /usr/lib:/usr/local/lib:/opt/lib
 include_dirs = /usr/include:/usr/local/include:/opt/include
 src_dirs = /usr/local/src:/opt/src
@@ -583,10 +606,14 @@ class system_info:
 
     """ get_info() is the only public method. Don't use others.
     """
-    section = 'ALL'
     dir_env_var = None
-    search_static_first = 0  # XXX: disabled by default, may disappear in
-                            # future unless it is proved to be useful.
+    # XXX: search_static_first is disabled by default, may disappear in
+    # future unless it is proved to be useful.
+    search_static_first = 0
+    # The base-class section name is a random word "ALL" and is not really
+    # intended for general use. It cannot be None nor can it be DEFAULT as
+    # these break the ConfigParser. See gh-15338
+    section = 'ALL'
     saved_results = {}
 
     notfounderror = NotFoundError
diff --git a/numpy/distutils/tests/test_system_info.py b/numpy/distutils/tests/test_system_info.py
index c40cdb7dba78..0768ffdde55a 100644
--- a/numpy/distutils/tests/test_system_info.py
+++ b/numpy/distutils/tests/test_system_info.py
@@ -7,7 +7,7 @@
 
 from numpy.testing import assert_, assert_equal, assert_raises
 from numpy.distutils import ccompiler, customized_ccompiler
-from numpy.distutils.system_info import system_info, ConfigParser
+from numpy.distutils.system_info import system_info, ConfigParser, mkl_info
 from numpy.distutils.system_info import AliasedOptionError
 from numpy.distutils.system_info import default_lib_dirs, default_include_dirs
 from numpy.distutils import _shell_utils
@@ -253,3 +253,35 @@ def test_compile2(self):
             assert_(os.path.isfile(self._src2.replace('.c', '.o')))
         finally:
             os.chdir(previousDir)
+
+    def test_overrides(self):
+        previousDir = os.getcwd()
+        cfg = os.path.join(self._dir1, 'site.cfg')
+        shutil.copy(self._sitecfg, cfg)
+        try:
+            os.chdir(self._dir1)
+            # Check that the '[ALL]' section does not override
+            # missing values from other sections
+            info = mkl_info()
+            lib_dirs = info.cp['ALL']['library_dirs'].split(os.pathsep)
+            assert info.get_lib_dirs() != lib_dirs
+
+            # But if we copy the values to a '[mkl]' section the value
+            # is correct
+            with open(cfg, 'r') as fid:
+                mkl = fid.read().replace('ALL', 'mkl')
+            with open(cfg, 'w') as fid:
+                fid.write(mkl)
+            info = mkl_info()
+            assert info.get_lib_dirs() == lib_dirs
+
+            # Also, the values will be taken from a section named '[DEFAULT]'
+            with open(cfg, 'r') as fid:
+                dflt = fid.read().replace('mkl', 'DEFAULT')
+            with open(cfg, 'w') as fid:
+                fid.write(dflt)
+            info = mkl_info()
+            assert info.get_lib_dirs() == lib_dirs
+        finally:
+            os.chdir(previousDir)
+        
diff --git a/site.cfg.example b/site.cfg.example
index cff0763813d9..236c26e6a21d 100644
--- a/site.cfg.example
+++ b/site.cfg.example
@@ -40,7 +40,7 @@
 #       List of directories to add to the header file search path.
 #           include_dirs = /usr/include:/usr/local/include
 #
-#   src_dirs 
+#   src_dirs
 #       List of directories that contain extracted source code for the
 #       dependency. For some dependencies, numpy.distutils will be able to build
 #       them from source if binaries cannot be found. The FORTRAN BLAS and
@@ -56,7 +56,7 @@
 #           search_static_first = false
 #
 #   runtime_library_dirs/rpath
-#       List of directories that contains the libraries that should be 
+#       List of directories that contains the libraries that should be
 #       used at runtime, thereby disregarding the LD_LIBRARY_PATH variable.
 #       See 'library_dirs' for formatting on different platforms.
 #           runtime_library_dirs = /opt/blas/lib:/opt/lapack/lib
@@ -79,14 +79,12 @@
 
 # Defaults
 # ========
-# The settings given here will apply to all other sections if not overridden.
+# The settings here will apply to all sections as general defaults
 # This is a good place to add general library and include directories like
 # /usr/local/{lib,include}
-#
-#[ALL]
+#[DEFAULT]
 #library_dirs = /usr/local/lib
 #include_dirs = /usr/local/include
-#
 
 # ATLAS
 # -----
@@ -108,7 +106,7 @@
 # for your configuration (in the following example we installed OpenBLAS with
 # ``make install PREFIX=/opt/OpenBLAS``.
 # OpenBLAS is generically installed as a shared library, to force the OpenBLAS
-# library linked to also be used at runtime you can utilize the 
+# library linked to also be used at runtime you can utilize the
 # runtime_library_dirs variable.
 #
 # **Warning**: OpenBLAS, by default, is built in multithreaded mode. Due to the
@@ -215,26 +213,26 @@
 # runtime_library_dirs = /home/username/flame/lib
 
 # MKL
-#---- 
-# Intel MKL is Intel's very optimized yet proprietary implementation of BLAS and 
+#----
+# Intel MKL is Intel's very optimized yet proprietary implementation of BLAS and
 # LAPACK. Find the latest info on building NumPy with Intel MKL in this article:
 # https://software.intel.com/en-us/articles/numpyscipy-with-intel-mkl
-# Assuming you installed the mkl in /opt/intel/compilers_and_libraries_2018/linux/mkl, 
-# for 64 bits code at Linux: 
-# [mkl] 
+# Assuming you installed the mkl in /opt/intel/compilers_and_libraries_2018/linux/mkl,
+# for 64 bits code at Linux:
+# [mkl]
 # library_dirs = /opt/intel/compilers_and_libraries_2018/linux/mkl/lib/intel64
-# include_dirs = /opt/intel/compilers_and_libraries_2018/linux/mkl/include 
+# include_dirs = /opt/intel/compilers_and_libraries_2018/linux/mkl/include
 # libraries = mkl_rt
-# 
-# For 32 bit code at Linux: 
+#
+# For 32 bit code at Linux:
 # [mkl]
 # library_dirs = /opt/intel/compilers_and_libraries_2018/linux/mkl/lib/ia32
-# include_dirs = /opt/intel/compilers_and_libraries_2018/linux/mkl/include 
+# include_dirs = /opt/intel/compilers_and_libraries_2018/linux/mkl/include
 # libraries = mkl_rt
-# 
-# On win-64, the following options compiles NumPy with the MKL library 
-# dynamically linked. 
-# [mkl] 
+#
+# On win-64, the following options compiles NumPy with the MKL library
+# dynamically linked.
+# [mkl]
 # include_dirs = C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2018\windows\mkl\include
 # library_dirs = C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2018\windows\mkl\lib\intel64
 # libraries = mkl_rt
@@ -249,7 +247,7 @@
 
 # UMFPACK
 # -------
-# The UMFPACK library is used in scikits.umfpack to factor large sparse matrices. 
+# The UMFPACK library is used in scikits.umfpack to factor large sparse matrices.
 # It, in turn, depends on the AMD library for reordering the matrices for
 # better performance.  Note that the AMD library has nothing to do with AMD
 # (Advanced Micro Devices), the CPU company.
@@ -279,7 +277,7 @@
 #[fftw]
 #libraries = fftw3
 #
-# For djbfft, numpy.distutils will look for either djbfft.a or libdjbfft.a . 
+# For djbfft, numpy.distutils will look for either djbfft.a or libdjbfft.a .
 #[djbfft]
 #include_dirs = /usr/local/djbfft/include
 #library_dirs = /usr/local/djbfft/lib


Code review comments:
diff_hunk: @@ -79,14 +79,12 @@
 
 # Defaults
 # ========
-# The settings given here will apply to all other sections if not overridden.
+# The settings here will apply to all sections as general defaults
 # This is a good place to add general library and include directories like
 # /usr/local/{lib,include}
-#
-#[ALL]
+#[DEFAULT]
 #library_dirs = /usr/local/lib
path: site.cfg.example
text: @user1:
Change these to `/usr/local/lib64:/usr/local/lib:/usr/lib64:/usr/lib:/usr/lib/` and `/usr/local/include:/usr/include` so it's clearer how to specify multiple paths and that default search paths will get overridden...

Of course on Windows, the path separator is ";" so maybe include also separate commented-out lines for that, with the values from `numpy.distutils.system_info.system_info().cp.get('ALL', 'library_dirs')`

@author:
adding, and adding a comment about using `os.pathsep`