Repository: pypa/setuptools_scm

Issue:
Default version scheme is invalid when using pyproject.toml configuration
On master, when loading the configuration `from_file` the `version_scheme` defaults to 'version_scheme' (line 10 of config.py) which is not a valid entry point for `setuptools_scm.version_scheme`.  This isn't caught in the test suite because `preformatted` is true bypassing the whole extracting-the-version-from-VCS thang:

```diff
diff --git a/testing/test_integration.py b/testing/test_integration.py
index a0d1143..e48ac3b 100644
--- a/testing/test_integration.py
+++ b/testing/test_integration.py
@@ -1,8 +1,20 @@
 import sys

+import pytest
+
 from setuptools_scm.utils import do


+@pytest.fixture
+def wd(wd):
+    wd("git init")
+    wd("git config user.email test@example.com")
+    wd('git config user.name "a test"')
+    wd.add_command = "git add ."
+    wd.commit_command = "git commit -m test-{reason}"
+    return wd
+
+
 def test_pyproject_support(tmpdir, monkeypatch):
     monkeypatch.delenv("SETUPTOOLS_SCM_DEBUG")
     pkg = tmpdir.ensure("package", dir=42)
@@ -14,3 +26,14 @@ fallback_version = "12.34"
     pkg.join("setup.py").write("__import__('setuptools').setup()")
     res = do((sys.executable, "setup.py", "--version"), pkg)
     assert res == "12.34"
+
+
+def test_pyproject_support_with_git(tmpdir, monkeypatch, wd):
+    monkeypatch.delenv("SETUPTOOLS_SCM_DEBUG")
+    pkg = tmpdir.join("wd")
+    pkg.join("pyproject.toml").write(
+        """[tool.setuptools_scm]"""
+    )
+    pkg.join("setup.py").write("__import__('setuptools').setup()")
+    res = do((sys.executable, "setup.py", "--version"), pkg)
+    assert res == "0.1.dev0"
```

Output:

```
============================================================================================================ test session starts =============================================================================================================
platform darwin -- Python 3.8.0, pytest-5.3.1, py-1.8.0, pluggy-0.13.1
setuptools version 42.0.0 from '/Users/layday/code/setuptools_scm/.venv/lib/python3.8/site-packages/setuptools/__init__.py'
setuptools_scm version 3.3.4.dev14+g6879db5.d20191126 from '/Users/layday/code/setuptools_scm/.venv/lib/python3.8/site-packages/setuptools_scm/__init__.py'
rootdir: /Users/layday/code/setuptools_scm, inifile: tox.ini
collected 2 items

testing/test_integration.py .F                                                                                                                                                                                                         [100%]

================================================================================================================== FAILURES ==================================================================================================================
______________________________________________________________________________________________________ test_pyproject_support_with_git _______________________________________________________________________________________________________

tmpdir = local('/private/var/folders/18/r2nky_v137d3fwhtx0644m940000gn/T/pytest-of-layday/pytest-48/test_pyproject_support_with_gi0'), monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x10f7649a0>
wd = <conftest.Wd object at 0x10f7648e0>

    def test_pyproject_support_with_git(tmpdir, monkeypatch, wd):
        monkeypatch.delenv("SETUPTOOLS_SCM_DEBUG")
        pkg = tmpdir.join("wd")
        pkg.join("pyproject.toml").write(
            """[tool.setuptools_scm]"""
        )
        pkg.join("setup.py").write("__import__('setuptools').setup()")
        res = do((sys.executable, "setup.py", "--version"), pkg)
>       assert res == "0.1.dev0"
E       AssertionError: assert '' == '0.1.dev0'
E         + 0.1.dev0

testing/test_integration.py:39: AssertionError
----------------------------------------------------------------------------------------------------------- Captured stdout setup ------------------------------------------------------------------------------------------------------------
cmd 'git init'
out b'Initialized empty Git repository in /private/var/folders/18/r2nky_v137d3fwhtx0644m940000gn/T/pytest-of-layday/pytest-48/test_pyproject_support_with_gi0/wd/.git/\n'
cmd 'git config user.email test@example.com'
cmd 'git config user.name "a test"'
------------------------------------------------------------------------------------------------------------ Captured stdout call ------------------------------------------------------------------------------------------------------------
cmd ('/Users/layday/code/setuptools_scm/.venv/bin/python3', 'setup.py', '--version')
err b'Traceback (most recent call last):\n  File "setup.py", line 1, in <module>\n    __import__(\'setuptools\').setup()\n  File "/Users/layday/code/setuptools_scm/.venv/lib/python3.8/site-packages/setuptools/__init__.py", line 144, in setup\n    _install_setup_requires(attrs)\n  File "/Users/layday/code/setuptools_scm/.venv/lib/python3.8/site-packages/setuptools/__init__.py", line 132, in _install_setup_requires\n    dist = distutils.core.Distribution(dict(\n  File "/Users/layday/code/setuptools_scm/.venv/lib/python3.8/site-packages/setuptools/dist.py", line 447, in __init__\n    _Distribution.__init__(self, {\n  File "/nix/store/mnl1njrv6g2jcs69kq0cy19766kcgc5n-python3-3.8.0/lib/python3.8/distutils/dist.py", line 292, in __init__\n    self.finalize_options()\n  File "/Users/layday/code/setuptools_scm/.venv/lib/python3.8/site-packages/setuptools/dist.py", line 740, in finalize_options\n    ep.load()(self)\n  File "/Users/layday/code/setuptools_scm/.venv/lib/python3.8/site-packages/setuptools_scm/integration.py", line 39, in infer_version\n    dist.metadata.version = _get_version(config)\n  File "/Users/layday/code/setuptools_scm/.venv/lib/python3.8/site-packages/setuptools_scm/__init__.py", line 156, in _get_version\n    version_string = format_version(\n  File "/Users/layday/code/setuptools_scm/.venv/lib/python3.8/site-packages/setuptools_scm/version.py", line 307, in format_version\n    main_version = version_scheme(version)\nTypeError: \'NoneType\' object is not callable\n'
ret 1
Traceback (most recent call last):
  File "setup.py", line 1, in <module>
    __import__('setuptools').setup()
  File "/Users/layday/code/setuptools_scm/.venv/lib/python3.8/site-packages/setuptools/__init__.py", line 144, in setup
    _install_setup_requires(attrs)
  File "/Users/layday/code/setuptools_scm/.venv/lib/python3.8/site-packages/setuptools/__init__.py", line 132, in _install_setup_requires
    dist = distutils.core.Distribution(dict(
  File "/Users/layday/code/setuptools_scm/.venv/lib/python3.8/site-packages/setuptools/dist.py", line 447, in __init__
    _Distribution.__init__(self, {
  File "/nix/store/mnl1njrv6g2jcs69kq0cy19766kcgc5n-python3-3.8.0/lib/python3.8/distutils/dist.py", line 292, in __init__
    self.finalize_options()
  File "/Users/layday/code/setuptools_scm/.venv/lib/python3.8/site-packages/setuptools/dist.py", line 740, in finalize_options
    ep.load()(self)
  File "/Users/layday/code/setuptools_scm/.venv/lib/python3.8/site-packages/setuptools_scm/integration.py", line 39, in infer_version
    dist.metadata.version = _get_version(config)
  File "/Users/layday/code/setuptools_scm/.venv/lib/python3.8/site-packages/setuptools_scm/__init__.py", line 156, in _get_version
    version_string = format_version(
  File "/Users/layday/code/setuptools_scm/.venv/lib/python3.8/site-packages/setuptools_scm/version.py", line 307, in format_version
    main_version = version_scheme(version)
TypeError: 'NoneType' object is not callable
======================================================================================================== 1 failed, 1 passed in 0.80s =========================================================================================================
```

Discussion:

Patch:
diff --git a/src/setuptools_scm/__init__.py b/src/setuptools_scm/__init__.py
index 2e05006..eebd9bb 100644
--- a/src/setuptools_scm/__init__.py
+++ b/src/setuptools_scm/__init__.py
@@ -5,7 +5,12 @@
 import os
 import warnings
 
-from .config import Configuration
+from .config import (
+    Configuration,
+    DEFAULT_VERSION_SCHEME,
+    DEFAULT_LOCAL_SCHEME,
+    DEFAULT_TAG_REGEX,
+)
 from .utils import function_has_arg, string_types
 from .version import format_version, meta
 from .discover import iter_matching_entrypoints
@@ -116,12 +121,12 @@ def _do_parse(config):
 
 def get_version(
     root=".",
-    version_scheme="guess-next-dev",
-    local_scheme="node-and-date",
+    version_scheme=DEFAULT_VERSION_SCHEME,
+    local_scheme=DEFAULT_LOCAL_SCHEME,
     write_to=None,
     write_to_template=None,
     relative_to=None,
-    tag_regex=None,
+    tag_regex=DEFAULT_TAG_REGEX,
     fallback_version=None,
     fallback_root=".",
     parse=None,
@@ -134,18 +139,7 @@ def get_version(
     root of the repository by supplying ``__file__``.
     """
 
-    config = Configuration()
-    config.root = root
-    config.fallback_root = fallback_root
-    config.version_scheme = version_scheme
-    config.local_scheme = local_scheme
-    config.write_to = write_to
-    config.write_to_template = write_to_template
-    config.relative_to = relative_to
-    config.tag_regex = tag_regex
-    config.fallback_version = fallback_version
-    config.parse = parse
-    config.git_describe_command = git_describe_command
+    config = Configuration(**locals())
     return _get_version(config)
 
 
diff --git a/src/setuptools_scm/config.py b/src/setuptools_scm/config.py
index d548c81..7dee1e4 100644
--- a/src/setuptools_scm/config.py
+++ b/src/setuptools_scm/config.py
@@ -7,7 +7,8 @@ import warnings
 from .utils import trace
 
 DEFAULT_TAG_REGEX = r"^(?:[\w-]+-)?(?P<version>[vV]?\d+(?:\.\d+){0,2}[^\+]+)(?:\+.*)?$"
-DEFAULT_VERSION_SCHEME = "version_scheme"
+DEFAULT_VERSION_SCHEME = "guess-next-dev"
+DEFAULT_LOCAL_SCHEME = "node-and-date"
 
 
 def _check_tag_regex(value):
@@ -39,32 +40,34 @@ def _check_absolute_root(root, relative_to):
 class Configuration(object):
     """ Global configuration model """
 
-    _root = None
-    version_scheme = None
-    local_scheme = None
-    write_to = None
-    write_to_template = None
-    fallback_version = None
-    _relative_to = None
-    parse = None
-    _tag_regex = None
-    _absolute_root = None
-
-    def __init__(self, relative_to=None, root="."):
+    def __init__(
+        self,
+        relative_to=None,
+        root=".",
+        version_scheme=DEFAULT_VERSION_SCHEME,
+        local_scheme=DEFAULT_LOCAL_SCHEME,
+        write_to=None,
+        write_to_template=None,
+        tag_regex=DEFAULT_TAG_REGEX,
+        fallback_version=None,
+        fallback_root=".",
+        parse=None,
+        git_describe_command=None,
+    ):
         # TODO:
         self._relative_to = relative_to
         self._root = "."
 
         self.root = root
-        self.version_scheme = DEFAULT_VERSION_SCHEME
-        self.local_scheme = "node-and-date"
-        self.write_to = ""
-        self.write_to_template = None
-        self.fallback_version = None
-        self.fallback_root = "."
-        self.parse = None
+        self.version_scheme = version_scheme
+        self.local_scheme = local_scheme
+        self.write_to = write_to
+        self.write_to_template = write_to_template
+        self.fallback_version = fallback_version
+        self.fallback_root = fallback_root
+        self.parse = parse
         self.tag_regex = DEFAULT_TAG_REGEX
-        self.git_describe_command = None
+        self.git_describe_command = git_describe_command
 
     @property
     def fallback_root(self):
@@ -106,10 +109,6 @@ class Configuration(object):
     def tag_regex(self, value):
         self._tag_regex = _check_tag_regex(value)
 
-    def _load(self, values):
-        vars(self).update(values)
-        return self
-
     @classmethod
     def from_file(cls, name="pyproject.toml"):
         """
@@ -120,4 +119,5 @@ class Configuration(object):
         """
         with open(name) as strm:
             defn = __import__("toml").load(strm)
-        return cls()._load(defn.get("tool", {})["setuptools_scm"])
+        section = defn.get("tool", {})["setuptools_scm"]
+        return cls(**section)
diff --git a/src/setuptools_scm/integration.py b/src/setuptools_scm/integration.py
index c5cc25d..c623db7 100644
--- a/src/setuptools_scm/integration.py
+++ b/src/setuptools_scm/integration.py
@@ -1,9 +1,8 @@
 from pkg_resources import iter_entry_points
 
 from .version import _warn_if_setuptools_outdated
-from .config import Configuration
 from .utils import do, trace_exception
-from . import get_version, _get_version
+from . import _get_version, Configuration
 
 
 def version_keyword(dist, keyword, value):
@@ -14,8 +13,8 @@ def version_keyword(dist, keyword, value):
         value = {}
     if getattr(value, "__call__", None):
         value = value()
-
-    dist.metadata.version = get_version(**value)
+    config = Configuration(**value)
+    dist.metadata.version = _get_version(config)
 
 
 def find_files(path=""):
@@ -31,7 +30,17 @@ def find_files(path=""):
     return []
 
 
+def _args_from_toml(name="pyproject.toml"):
+    # todo: more sensible config initialization
+    # move this elper back to config and unify it with the code from get_config
+
+    with open(name) as strm:
+        defn = __import__("toml").load(strm)
+    return defn.get("tool", {})["setuptools_scm"]
+
+
 def infer_version(dist):
+
     try:
         config = Configuration.from_file()
     except Exception:


Test patch:
diff --git a/testing/test_integration.py b/testing/test_integration.py
index e67f6fd..a01ac8b 100644
--- a/testing/test_integration.py
+++ b/testing/test_integration.py
@@ -16,6 +16,7 @@ def wd(wd):
 
 
 def test_pyproject_support(tmpdir, monkeypatch):
+    pytest.importorskip("toml")
     monkeypatch.delenv("SETUPTOOLS_SCM_DEBUG")
     pkg = tmpdir.ensure("package", dir=42)
     pkg.join("pyproject.toml").write(
@@ -28,7 +29,6 @@ fallback_version = "12.34"
     assert res == "12.34"
 
 
-@pytest.mark.xfail(reason="#174")
 def test_pyproject_support_with_git(tmpdir, monkeypatch, wd):
     monkeypatch.delenv("SETUPTOOLS_SCM_DEBUG")
     pkg = tmpdir.join("wd")