Repository: pypa/setuptools_scm

Issue:
latest version of git breaks setuptools_scm with python3.9
When trying to pip install a package with python 3.9 we get the following exception:
```
Processing /home/cal/conda/build/pydarm
  Installing build dependencies ... done
  Getting requirements to build wheel ... error
  error: subprocess-exited-with-error
  
  × Getting requirements to build wheel did not run successfully.
  │ exit code: 1
  ╰─> [49 lines of output]
      /local/cal/pip-build-env-4xfw_75z/overlay/lib/python3.9/site-packages/setuptools_scm/git.py:163: UserWarning: "/home/cal/conda/build/pydarm" is shallow and may cause errors
        warnings.warn(f'"{wd.path}" is shallow and may cause errors')
      Traceback (most recent call last):
        File "/home/cal/conda/pydarm-20240430.0/lib/python3.9/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py", line 353, in <module>
          main()
        File "/home/cal/conda/pydarm-20240430.0/lib/python3.9/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py", line 335, in main
          json_out['return_val'] = hook(**hook_input['kwargs'])
        File "/home/cal/conda/pydarm-20240430.0/lib/python3.9/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py", line 118, in get_requires_for_build_wheel
          return hook(config_settings)
        File "/local/cal/pip-build-env-4xfw_75z/overlay/lib/python3.9/site-packages/setuptools/build_meta.py", line 325, in get_requires_for_build_wheel
          return self._get_build_requires(config_settings, requirements=['wheel'])
        File "/local/cal/pip-build-env-4xfw_75z/overlay/lib/python3.9/site-packages/setuptools/build_meta.py", line 295, in _get_build_requires
          self.run_setup()
        File "/local/cal/pip-build-env-4xfw_75z/overlay/lib/python3.9/site-packages/setuptools/build_meta.py", line 311, in run_setup
          exec(code, locals())
        File "<string>", line 1, in <module>
        File "/local/cal/pip-build-env-4xfw_75z/overlay/lib/python3.9/site-packages/setuptools/__init__.py", line 104, in setup
          return distutils.core.setup(**attrs)
        File "/local/cal/pip-build-env-4xfw_75z/overlay/lib/python3.9/site-packages/setuptools/_distutils/core.py", line 146, in setup
          _setup_distribution = dist = klass(attrs)
        File "/local/cal/pip-build-env-4xfw_75z/overlay/lib/python3.9/site-packages/setuptools/dist.py", line 307, in __init__
          _Distribution.__init__(self, dist_attrs)
        File "/local/cal/pip-build-env-4xfw_75z/overlay/lib/python3.9/site-packages/setuptools/_distutils/dist.py", line 284, in __init__
          self.finalize_options()
        File "/local/cal/pip-build-env-4xfw_75z/overlay/lib/python3.9/site-packages/setuptools/dist.py", line 658, in finalize_options
          ep(self)
        File "/local/cal/pip-build-env-4xfw_75z/overlay/lib/python3.9/site-packages/setuptools_scm/_integration/setuptools.py", line 121, in infer_version
          _assign_version(dist, config)
        File "/local/cal/pip-build-env-4xfw_75z/overlay/lib/python3.9/site-packages/setuptools_scm/_integration/setuptools.py", line 53, in _assign_version
          maybe_version = _get_version(config, force_write_version_files=True)
        File "/local/cal/pip-build-env-4xfw_75z/overlay/lib/python3.9/site-packages/setuptools_scm/_get_version_impl.py", line 93, in _get_version
          parsed_version = parse_version(config)
        File "/local/cal/pip-build-env-4xfw_75z/overlay/lib/python3.9/site-packages/setuptools_scm/_get_version_impl.py", line 56, in parse_version
          or parse_scm_version(config)
        File "/local/cal/pip-build-env-4xfw_75z/overlay/lib/python3.9/site-packages/setuptools_scm/_get_version_impl.py", line 35, in parse_scm_version
          return _entrypoints.version_from_entrypoint(
        File "/local/cal/pip-build-env-4xfw_75z/overlay/lib/python3.9/site-packages/setuptools_scm/_entrypoints.py", line 55, in version_from_entrypoint
          maybe_version: version.ScmVersion | None = fn(root, config=config)
        File "/local/cal/pip-build-env-4xfw_75z/overlay/lib/python3.9/site-packages/setuptools_scm/git.py", line 211, in parse
          return _git_parse_inner(
        File "/local/cal/pip-build-env-4xfw_75z/overlay/lib/python3.9/site-packages/setuptools_scm/git.py", line 272, in _git_parse_inner
          node_date = wd.get_head_date() or datetime.now(timezone.utc).date()
        File "/local/cal/pip-build-env-4xfw_75z/overlay/lib/python3.9/site-packages/setuptools_scm/git.py", line 131, in get_head_date
          return res.parse_success(
        File "/local/cal/pip-build-env-4xfw_75z/overlay/lib/python3.9/site-packages/setuptools_scm/_run_cmd.py", line 77, in parse_success
          return parse(self.stdout)
        File "/local/cal/pip-build-env-4xfw_75z/overlay/lib/python3.9/site-packages/setuptools_scm/git.py", line 121, in parse_timestamp
          return datetime.fromisoformat(timestamp_text).date()
      ValueError: Invalid isoformat string: '2024-04-30T22:33:10Z'
      [end of output]
  
  note: This error originates from a subprocess, and is likely not a problem with pip.
error: subprocess-exited-with-error

× Getting requirements to build wheel did not run successfully.
│ exit code: 1
╰─> See above for output.

note: This error originates from a subprocess, and is likely not a problem with pip.
```

I think I've tracked the issue down to the version of git being used, and the way it formats the date string with `log --format=%cI`.  With the new version:
```
$ git --version
git version 2.45.0
$ git -c log.showSignature=false log -n 1 HEAD --format=%cI
2024-04-30T22:33:10Z
```
Note that the output uses the 'Z' suffix, which is standard ISO format but was not supported in older versions (e.g. 3.9) of the datetime module.

The previous version of git outputs the ISO datetime string in a format that older datetime can handle: 
```
$ git --version
git version 2.44.0
$ git -c log.showSignature=false log -n 1 HEAD --format=%cI
2024-04-30T22:33:10+00:00
```

So things are "fixed" with later versions of python that can handle regular ISO formats.  Maybe a restriction on supported git versions with python 3.9 could be expressed somehow?



Discussion:

Patch:
diff --git a/src/setuptools_scm/git.py b/src/setuptools_scm/git.py
index 8f5c77e..eb1d519 100644
--- a/src/setuptools_scm/git.py
+++ b/src/setuptools_scm/git.py
@@ -5,6 +5,7 @@ import logging
 import os
 import re
 import shlex
+import sys
 import warnings
 
 from datetime import date
@@ -119,6 +120,8 @@ class GitWorkdir(Workdir):
             if "%c" in timestamp_text:
                 log.warning("git too old -> timestamp is %r", timestamp_text)
                 return None
+            if sys.version_info < (3, 11) and timestamp_text.endswith("Z"):
+                timestamp_text = timestamp_text[:-1] + "+00:00"
             return datetime.fromisoformat(timestamp_text).date()
 
         res = run_git(


Test patch:
diff --git a/testing/test_git.py b/testing/test_git.py
index 7918f47..15f4d09 100644
--- a/testing/test_git.py
+++ b/testing/test_git.py
@@ -498,6 +498,22 @@ def test_git_getdate_badgit(
         assert git_wd.get_head_date() is None
 
 
+def test_git_getdate_git_2_45_0_plus(
+    wd: WorkDir, caplog: pytest.LogCaptureFixture, monkeypatch: pytest.MonkeyPatch
+) -> None:
+    wd.commit_testfile()
+    git_wd = git.GitWorkdir(wd.cwd)
+    fake_date_result = CompletedProcess(
+        args=[], stdout="2024-04-30T22:33:10Z", stderr="", returncode=0
+    )
+    with patch.object(
+        git,
+        "run_git",
+        Mock(return_value=fake_date_result),
+    ):
+        assert git_wd.get_head_date() == date(2024, 4, 30)
+
+
 @pytest.fixture()
 def signed_commit_wd(monkeypatch: pytest.MonkeyPatch, wd: WorkDir) -> WorkDir:
     if not has_command("gpg", args=["--version"], warn=False):