Repository: pypa/setuptools_scm
Pull request #646 (2021-10-29 18:45:21)

Consider configuration when running `__main__`
Since the usage with `setup.py` is deprecated (according to `setuptools_scm` README), it would be nice to have a command that give the same results as:

    python setup.py --version

The idea of the changes implemented here is to do that using the `__main__` module.

The methodology consists in using the `find_files` function to look for a `pyproject.toml` file and then read the configuration from there.

If `pyproject.toml` file is not found or it does not contain a `[tool.setuptools_scm]` table, the command works as implemented before (guessing the next version).

abravalheri: No problems, take your time. I wish you a god recovery :)

Patch:
diff --git a/src/setuptools_scm/__main__.py b/src/setuptools_scm/__main__.py
index f3377b0..ef64819 100644
--- a/src/setuptools_scm/__main__.py
+++ b/src/setuptools_scm/__main__.py
@@ -1,15 +1,68 @@
-import sys
+import argparse
+import os
+import warnings
 
-from setuptools_scm import get_version
+from setuptools_scm import _get_version
+from setuptools_scm.config import Configuration
+from setuptools_scm.discover import walk_potential_roots
 from setuptools_scm.integration import find_files
 
 
 def main() -> None:
-    print("Guessed Version", get_version())
-    if "ls" in sys.argv:
-        for fname in find_files("."):
+    opts = _get_cli_opts()
+    root = opts.root or "."
+
+    try:
+        pyproject = opts.config or _find_pyproject(root)
+        root = opts.root or os.path.relpath(os.path.dirname(pyproject))
+        config = Configuration.from_file(pyproject)
+        config.root = root
+    except (LookupError, FileNotFoundError) as ex:
+        # no pyproject.toml OR no [tool.setuptools_scm]
+        warnings.warn(f"{ex}. Using default configuration.")
+        config = Configuration(root)
+
+    print(_get_version(config))
+
+    if opts.command == "ls":
+        for fname in find_files(config.root):
             print(fname)
 
 
+def _get_cli_opts():
+    prog = "python -m setuptools_scm"
+    desc = "Print project version according to SCM metadata"
+    parser = argparse.ArgumentParser(prog, description=desc)
+    # By default, help for `--help` starts with lower case, so we keep the pattern:
+    parser.add_argument(
+        "-r",
+        "--root",
+        default=None,
+        help='directory managed by the SCM, default: inferred from config file, or "."',
+    )
+    parser.add_argument(
+        "-c",
+        "--config",
+        default=None,
+        metavar="PATH",
+        help="path to 'pyproject.toml' with setuptools_scm config, "
+        "default: looked up in the current or parent directories",
+    )
+    sub = parser.add_subparsers(title="extra commands", dest="command", metavar="")
+    # We avoid `metavar` to prevent printing repetitive information
+    desc = "List files managed by the SCM"
+    sub.add_parser("ls", help=desc[0].lower() + desc[1:], description=desc)
+    return parser.parse_args()
+
+
+def _find_pyproject(parent):
+    for directory in walk_potential_roots(os.path.abspath(parent)):
+        pyproject = os.path.join(directory, "pyproject.toml")
+        if os.path.exists(pyproject):
+            return pyproject
+
+    raise FileNotFoundError("'pyproject.toml' was not found")
+
+
 if __name__ == "__main__":
     main()


Test patch:
diff --git a/testing/test_main.py b/testing/test_main.py
index 97ea05e..ea1373f 100644
--- a/testing/test_main.py
+++ b/testing/test_main.py
@@ -1,4 +1,8 @@
 import os.path
+import sys
+import textwrap
+
+import pytest
 
 
 def test_main():
@@ -8,3 +12,51 @@ def test_main():
     with open(mainfile) as f:
         code = compile(f.read(), "__main__.py", "exec")
         exec(code)
+
+
+@pytest.fixture
+def repo(wd):
+    wd("git init")
+    wd("git config user.email user@host")
+    wd("git config user.name user")
+    wd.add_command = "git add ."
+    wd.commit_command = "git commit -m test-{reason}"
+
+    wd.write("README.rst", "My example")
+    wd.add_and_commit()
+    wd("git tag v0.1.0")
+
+    wd.write("file.txt", "file.txt")
+    wd.add_and_commit()
+
+    return wd
+
+
+def test_repo_with_config(repo):
+    pyproject = """\
+    [tool.setuptools_scm]
+    version_scheme = "no-guess-dev"
+
+    [project]
+    name = "example"
+    """
+    repo.write("pyproject.toml", textwrap.dedent(pyproject))
+    repo.add_and_commit()
+    res = repo((sys.executable, "-m", "setuptools_scm"))
+    assert res.startswith("0.1.0.post1.dev2")
+
+
+def test_repo_without_config(repo):
+    res = repo((sys.executable, "-m", "setuptools_scm"))
+    assert res.startswith("0.1.1.dev1")
+
+
+def test_repo_with_pyproject_missing_setuptools_scm(repo):
+    pyproject = """\
+    [project]
+    name = "example"
+    """
+    repo.write("pyproject.toml", textwrap.dedent(pyproject))
+    repo.add_and_commit()
+    res = repo((sys.executable, "-m", "setuptools_scm"))
+    assert res.startswith("0.1.1.dev2")