Repository: conda/conda-build

Issue:
setup.cfg vs setup.py precedence is backwards in reading setup.py data
Discussion at:
https://github.com/conda/conda-build/commit/be5cc63cfa2f421d4ebf14e3634c21ea7939d486


Discussion:
Maybe the inclusion of `setup.cfg`'s metadata/options should be moved into `setup(**kw)` which would be cleaner and also emulate the behavior in `setuptools` a bit more. Something like this:
```python
    setup_cfg_data = {}
    try:
        from setuptools.config import read_configuration
    except ImportError:
        pass  # setuptools <30.3.0 cannot read metadata / options from 'setup.cfg'
    else:
        setup_cfg = os.path.join(os.path.dirname(setup_file), 'setup.cfg')
        if os.path.isfile(setup_cfg):
            # read_configuration returns a dict of dicts. Each dict (keys: 'metadata',
            # 'options'), if present, provides keyword arguments for the setup function.
            for kwargs in read_configuration(setup_cfg).values():
                for k, v in kwargs.items():
                    setup_cfg_data[k] = v

    def setup(**kw):
        _setuptools_data.update(kw)
        # values in setup.cfg take priority over explicit arguments to setup.py
        _setuptools_data.update(setup_cfg_data)
```

Resolving patch:
diff --git a/conda_build/_load_setup_py_data.py b/conda_build/_load_setup_py_data.py
--- a/conda_build/_load_setup_py_data.py
+++ b/conda_build/_load_setup_py_data.py
@@ -8,9 +8,6 @@ def load_setup_py_data(setup_file, from_recipe_dir=False, recipe_dir=None, work_
     _setuptools_data = {}
     log = logging.getLogger(__name__)
 
-    def setup(**kw):
-        _setuptools_data.update(kw)
-
     import setuptools
     import distutils.core
 
@@ -37,6 +34,25 @@ def setup(**kw):
         else:
             raise RuntimeError(message)
 
+    setup_cfg_data = {}
+    try:
+        from setuptools.config import read_configuration
+    except ImportError:
+        pass  # setuptools <30.3.0 cannot read metadata / options from 'setup.cfg'
+    else:
+        setup_cfg = os.path.join(os.path.dirname(setup_file), 'setup.cfg')
+        if os.path.isfile(setup_cfg):
+            # read_configuration returns a dict of dicts. Each dict (keys: 'metadata',
+            # 'options'), if present, provides keyword arguments for the setup function.
+            for kwargs in read_configuration(setup_cfg).values():
+                # explicit arguments to setup.cfg take priority over values in setup.py
+                setup_cfg_data.update(kwargs)
+
+    def setup(**kw):
+        _setuptools_data.update(kw)
+        # values in setup.cfg take priority over explicit arguments to setup.py
+        _setuptools_data.update(setup_cfg_data)
+
     # Patch setuptools, distutils
     setuptools_setup = setuptools.setup
     distutils_setup = distutils.core.setup
@@ -73,20 +89,7 @@ def setup(**kw):
     setuptools.setup = setuptools_setup
     if numpy_setup:
         numpy.distutils.core.setup = numpy_setup
-    try:
-        from setuptools.config import read_configuration
-    except ImportError:
-        pass  # setuptools <30.3.0 cannot read metadata / options from 'setup.cfg'
-    else:
-        setup_cfg = os.path.join(os.path.dirname(setup_file), 'setup.cfg')
-        if os.path.isfile(setup_cfg):
-            # read_configuration returns a dict of dicts. Each dict (keys: 'metadata',
-            # 'options'), if present, provides keyword arguments for the setup function.
-            for kwargs in read_configuration(setup_cfg).values():
-                # explicit arguments to setup.py take priority over values in setup.cfg
-                for k, v in kwargs.items():
-                    if k not in _setuptools_data:
-                        _setuptools_data[k] = v
+
     if cd_to_work:
         os.chdir(cwd)
     # remove our workdir from sys.path
diff --git a/conda_build/jinja_context.py b/conda_build/jinja_context.py
--- a/conda_build/jinja_context.py
+++ b/conda_build/jinja_context.py
@@ -101,20 +101,6 @@ def load_setup_py_data(config, setup_file='setup.py', from_recipe_dir=False, rec
                                                     recipe_dir=recipe_dir,
                                                     work_dir=config.work_dir,
                                                     permit_undefined_jinja=permit_undefined_jinja)
-        try:
-            from setuptools.config import read_configuration
-        except ImportError:
-            pass  # setuptools <30.3.0 cannot read metadata / options from 'setup.cfg'
-        else:
-            setup_cfg = os.path.join(os.path.dirname(setup_file), 'setup.cfg')
-            if os.path.isfile(setup_cfg):
-                # read_configuration returns a dict of dicts. Each dict (keys: 'metadata',
-                # 'options'), if present, provides keyword arguments for the setup function.
-                for kwargs in read_configuration(setup_cfg).values():
-                    # explicit arguments to setup.py take priority over values in setup.cfg
-                    for k, v in kwargs.items():
-                        if k not in _setuptools_data:
-                            _setuptools_data[k] = v
     # cleanup: we must leave the source tree empty unless the source code is already present
     rm_rf(os.path.join(config.work_dir, '_load_setup_py_data.py'))
     return _setuptools_data if _setuptools_data else None


Test patch:
diff --git a/tests/test_jinja_context.py b/tests/test_jinja_context.py
--- a/tests/test_jinja_context.py
+++ b/tests/test_jinja_context.py
@@ -125,6 +125,7 @@ def test_load_setup_py_data_from_setup_cfg(testing_config, tmpdir):
     )
     setup_file = str(setup_py)
     setuptools_data = jinja_context.load_setup_py_data(testing_config, setup_file)
-    assert setuptools_data['name'] == 'name_from_setup_py'
+    # ensure that setup.cfg has priority over setup.py
+    assert setuptools_data['name'] == 'name_from_setup_cfg'
     assert setuptools_data['version'] == 'version_from_setup_cfg'
     assert setuptools_data['extras_require'] == {'extra': ['extra_package']}