Repository: pypa/setuptools_scm
Pull request #410 (2020-03-22 13:55:10)

Allow getting the version from the parent directory suffix.
Add a `parentdir_prefix_version` config parameter which allows lets
setuptools_scm get a version string from the name of the parent
directory: if `parentdir_prefix_version = "foo-v"` and the parent
directory is named `foo-v12.34`, then the version is 12.34.  This is
only active if the parameter is set (it defaults to unset, i.e. the
feature is opt-in), if getting the version from the SCM failed, *and*
there's no metadata already present.  However, this has higher priority
than `fallback_version` (which remains the last-resort fallback).

This feature is intended to support the tarballs that GitHub
automatically creates for all releases, which have no metadata but
follow the correct naming scheme for the feature to work.

This feature is directly inspired from versioneer's `parentdir_prefix`
setting.

(I am open to changing the name of the param, of course.)

RonnyPfannschmidt: Thanks, i like it as it enhances a number of things,

I can't promise timely review as personal time is currently used mostly for offsetting the daycare shutdown 
anntzer: I switched to passing the string to tag_regex (I don't have a strong opinion there).  I think it's already the resolved root that gets passed to the parsers in hacks.py -- or if it isn't, how do I get it?

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 8a49e69..36c1d70 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,3 +1,9 @@
+next
+====
+
+* Add ``parentdir_project_version`` to support installs from GitHub release
+  tarballs.
+
 v3.5.1
 ======
 
diff --git a/README.rst b/README.rst
index c6be06c..1d1e90e 100644
--- a/README.rst
+++ b/README.rst
@@ -370,6 +370,18 @@ The currently supported configuration keys are:
     Defaults to the value of ``setuptools_scm.config.DEFAULT_TAG_REGEX``
     (see `config.py <src/setuptools_scm/config.py>`_).
 
+:parentdir_prefix_version:
+    If the normal methods for detecting the version (SCM version,
+    sdist metadata) fail, and the parent directory name starts with
+    ``parentdir_prefix_version``, then this prefix is stripped and the rest of
+    the parent directory name is matched with ``tag_regex`` to get a version
+    string.  If this parameter is unset (the default), then this fallback is
+    not used.
+
+    This is intended to cover GitHub's "release tarballs", which extract into
+    directories named ``projectname-tag/`` (in which case
+    ``parentdir_prefix_version`` can be set e.g. to ``projectname-``).
+
 :fallback_version:
     A version string that will be used if no other method for detecting the
     version worked (e.g., when using a tarball with no metadata). If this is
diff --git a/src/setuptools_scm/__init__.py b/src/setuptools_scm/__init__.py
index eebd9bb..6b22b28 100644
--- a/src/setuptools_scm/__init__.py
+++ b/src/setuptools_scm/__init__.py
@@ -127,6 +127,7 @@ def get_version(
     write_to_template=None,
     relative_to=None,
     tag_regex=DEFAULT_TAG_REGEX,
+    parentdir_prefix_version=None,
     fallback_version=None,
     fallback_root=".",
     parse=None,
diff --git a/src/setuptools_scm/config.py b/src/setuptools_scm/config.py
index b452557..e7f4d72 100644
--- a/src/setuptools_scm/config.py
+++ b/src/setuptools_scm/config.py
@@ -49,6 +49,7 @@ class Configuration(object):
         write_to=None,
         write_to_template=None,
         tag_regex=DEFAULT_TAG_REGEX,
+        parentdir_prefix_version=None,
         fallback_version=None,
         fallback_root=".",
         parse=None,
@@ -63,6 +64,7 @@ class Configuration(object):
         self.local_scheme = local_scheme
         self.write_to = write_to
         self.write_to_template = write_to_template
+        self.parentdir_prefix_version = parentdir_prefix_version
         self.fallback_version = fallback_version
         self.fallback_root = fallback_root
         self.parse = parse
diff --git a/src/setuptools_scm/hacks.py b/src/setuptools_scm/hacks.py
index 1ef0bb4..e0db383 100644
--- a/src/setuptools_scm/hacks.py
+++ b/src/setuptools_scm/hacks.py
@@ -1,6 +1,6 @@
 import os
 from .utils import data_from_mime, trace
-from .version import meta
+from .version import tag_to_version, meta
 
 
 def parse_pkginfo(root, config=None):
@@ -25,5 +25,12 @@ def parse_pip_egg_info(root, config=None):
 
 
 def fallback_version(root, config=None):
+    if config.parentdir_prefix_version is not None:
+        _, parent_name = os.path.split(os.path.abspath(root))
+        if parent_name.startswith(config.parentdir_prefix_version):
+            version = tag_to_version(
+                parent_name[len(config.parentdir_prefix_version) :], config)
+            if version is not None:
+                return meta(str(version), preformatted=True, config=config)
     if config.fallback_version is not None:
         return meta(config.fallback_version, preformatted=True, config=config)


Test patch:
diff --git a/testing/test_basic_api.py b/testing/test_basic_api.py
index 9227b2e..c5104f5 100644
--- a/testing/test_basic_api.py
+++ b/testing/test_basic_api.py
@@ -58,6 +58,18 @@ def test_root_parameter_pass_by(monkeypatch, tmpdir):
     setuptools_scm.get_version(root=tmpdir.strpath)
 
 
+def test_parentdir_prefix(tmpdir, monkeypatch):
+    monkeypatch.delenv("SETUPTOOLS_SCM_DEBUG")
+    p = tmpdir.ensure("projectname-v12.34", dir=True)
+    p.join("setup.py").write(
+        """from setuptools import setup
+setup(use_scm_version={"parentdir_prefix_version": "projectname-"})
+"""
+    )
+    res = do((sys.executable, "setup.py", "--version"), p)
+    assert res == "12.34"
+
+
 def test_fallback(tmpdir, monkeypatch):
     monkeypatch.delenv("SETUPTOOLS_SCM_DEBUG")
     p = tmpdir.ensure("sub/package", dir=1)