Repository: python-versioneer/python-versioneer (language: python)

Issue:
Doesn't work with Python 3.12
I'm trying to build [BuildStream](https://github.com/apache/buildstream/) on Fedora 39 which has Python 3.12, and [it fails](https://github.com/apache/buildstream/issues/1855) :disappointed: 

Once again, fedora is first on the latest and greatest, and we're the one finding issues in a lot of software. :shrug:

I think that issue in BuildStream should give you all you need to fix the issue in versioneer:

```
commit 06ca13b4181c9140a536a72086a324e242ec249b (HEAD -> main)
Author: Mathieu Bridon <mathieu@hashbang.coop>
Date:   Tue Aug 8 09:28:01 2023 +0200

    Try to fix #1855

diff --git a/versioneer.py b/versioneer.py
index 1c97e02..5751584 100644
--- a/versioneer.py
+++ b/versioneer.py
@@ -340,9 +340,16 @@ def get_config_from_root(root):
     # configparser.NoOptionError (if it lacks "VCS="). See the docstring at
     # the top of versioneer.py for instructions on writing your setup.cfg .
     setup_cfg = os.path.join(root, "setup.cfg")
-    parser = configparser.SafeConfigParser()
-    with open(setup_cfg, "r") as f:
-        parser.readfp(f)
+    if sys.version_info.major == 3 and sys.version_info.minor >= 12:
+        parser = configparser.ConfigParser()
+        with open(setup_cfg, "r") as f:
+            parser.read_file(f)
+
+    else:
+        parser = configparser.SafeConfigParser()
+        with open(setup_cfg, "r") as f:
+            parser.readfp(f)
+
     VCS = parser.get("versioneer", "VCS")  # mandatory
 
     def get(parser, name):
```

Can this be merged soon ? :zany_face: 


Discussion / hints:
I'll look at this no later than this upcoming weekend, thanks. 
Commenting here too that with Python 3.2 SafeConfigParser was renamed to ConfigParser and SafeConfigParser was left as deprecated alias. So there most likely is no need for version checking given versioneer doesn't support Python 2.

Fix patch:
diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml
index 32198e86..1ceadefc 100644
--- a/.github/workflows/tox.yml
+++ b/.github/workflows/tox.yml
@@ -20,7 +20,7 @@ jobs:
     strategy:
       matrix:
         os: ['ubuntu-latest']
-        python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', 'pypy3.9']
+        python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', 'pypy3.9']
         shell: ['bash']
         include:
           - os: 'windows-latest'
@@ -42,6 +42,7 @@ jobs:
         uses: actions/setup-python@v4
         with:
           python-version: ${{ matrix.python-version }}
+          allow-prereleases: true
       - name: Display Python version
         run: python -c "import sys; print(sys.version)"
       - name: Install Tox
diff --git a/NEWS.md b/NEWS.md
index 4e9e9089..eb591b35 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,3 +1,10 @@
+## Release 0.30 (??-???-2023)
+
+This release adds formal support for Python 3.12.
+
+* MNT: Update `Cython` generated `.c` in `demoappext-setuptools` test project. by @Callek (#376)
+* CI: Add Python 3.12 to CI (initially at it's rc1 version). by @Callek (#376)
+
 ## Release 0.29 (7-Jul-2023)
 
 This release allows for `pyproject.toml`-only build systems to allow versioneer to find
diff --git a/README.md b/README.md
index 4bfa13f0..71ccdf9f 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,7 @@ The Versioneer
 * Brian Warner
 * License: Public Domain (Unlicense)
 * Compatible with: Python 3.7, 3.8, 3.9, 3.10, 3.11 and pypy3
+* Experimental support for Python 3.12.
 * [![Latest Version][pypi-image]][pypi-url]
 * [![Build Status][travis-image]][travis-url]
 
diff --git a/pyproject.toml b/pyproject.toml
index ac2a7df2..723fd163 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -23,6 +23,7 @@ classifiers = [
   "Programming Language :: Python :: 3.9",
   "Programming Language :: Python :: 3.10",
   "Programming Language :: Python :: 3.11",
+  "Programming Language :: Python :: 3.12",
   "License :: OSI Approved :: The Unlicense (Unlicense)",
 ]
 dynamic = ["version"]
diff --git a/tox.ini b/tox.ini
index 7c96eb3d..26b6e620 100644
--- a/tox.ini
+++ b/tox.ini
@@ -4,7 +4,7 @@
 # and then run "tox" from this directory.
 
 [tox]
-envlist = py{37,38,39,310,311,py3}-{linux,windows}
+envlist = py{37,38,39,310,311,312,py3}-{linux,windows}
 skip_missing_interpreters = True
 
 [flake8]
@@ -17,6 +17,7 @@ python =
   3.9: py39
   3.10: py310
   3.11: py311
+  3.12: py312
   pypy-3.9: pypy3
 
 [gh-actions:env]


Test patch:
diff --git a/test/demoappext-setuptools/README b/test/demoappext-setuptools/README
index d9b40125..8107379a 100644
--- a/test/demoappext-setuptools/README
+++ b/test/demoappext-setuptools/README
@@ -1,1 +1,17 @@
 read me
+
+== Updating Extension ==
+
+In order to update this extension for testing you'll need a clean venv with `Cython` and
+then run the `cython` command to update the .c.
+
+```
+python -m venv /tmp/cython
+. /tmp/cython/bin/activate
+pip install "cython<3.0"
+
+cd <repo_root>/test/demoappext-setuptools/demo
+cython -3 -w . ext.pyx
+```
+
+After those commands are run you can inspect the difference if you like.
diff --git a/test/demoappext-setuptools/demo/ext.c b/test/demoappext-setuptools/demo/ext.c
index f86a2e59..062d0e8d 100644
--- a/test/demoappext-setuptools/demo/ext.c
+++ b/test/demoappext-setuptools/demo/ext.c
@@ -1,4 +1,4 @@
-/* Generated by Cython 0.29.32 */
+/* Generated by Cython 0.29.36 */
 
 #ifndef PY_SSIZE_T_CLEAN
 #define PY_SSIZE_T_CLEAN
@@ -9,8 +9,8 @@
 #elif PY_VERSION_HEX < 0x02060000 || (0x03000000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x03030000)
     #error Cython requires Python 2.6+ or Python 3.3+.
 #else
-#define CYTHON_ABI "0_29_32"
-#define CYTHON_HEX_VERSION 0x001D20F0
+#define CYTHON_ABI "0_29_36"
+#define CYTHON_HEX_VERSION 0x001D24F0
 #define CYTHON_FUTURE_DIVISION 1
 #include <stddef.h>
 #ifndef offsetof
@@ -78,16 +78,20 @@
   #define CYTHON_FAST_THREAD_STATE 0
   #undef CYTHON_FAST_PYCALL
   #define CYTHON_FAST_PYCALL 0
-  #undef CYTHON_PEP489_MULTI_PHASE_INIT
-  #define CYTHON_PEP489_MULTI_PHASE_INIT 0
+  #if PY_VERSION_HEX < 0x03090000
+    #undef CYTHON_PEP489_MULTI_PHASE_INIT
+    #define CYTHON_PEP489_MULTI_PHASE_INIT 0
+  #elif !defined(CYTHON_PEP489_MULTI_PHASE_INIT)
+    #define CYTHON_PEP489_MULTI_PHASE_INIT 1
+  #endif
   #undef CYTHON_USE_TP_FINALIZE
-  #define CYTHON_USE_TP_FINALIZE 0
+  #define CYTHON_USE_TP_FINALIZE (PY_VERSION_HEX >= 0x030400a1 && PYPY_VERSION_NUM >= 0x07030C00)
   #undef CYTHON_USE_DICT_VERSIONS
   #define CYTHON_USE_DICT_VERSIONS 0
   #undef CYTHON_USE_EXC_INFO_STACK
   #define CYTHON_USE_EXC_INFO_STACK 0
   #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC
-    #define CYTHON_UPDATE_DESCRIPTOR_DOC (PYPY_VERSION_HEX >= 0x07030900)
+    #define CYTHON_UPDATE_DESCRIPTOR_DOC 0
   #endif
 #elif defined(PYSTON_VERSION)
   #define CYTHON_COMPILING_IN_PYPY 0
@@ -203,7 +207,7 @@
     #undef CYTHON_USE_PYLONG_INTERNALS
     #define CYTHON_USE_PYLONG_INTERNALS 0
   #elif !defined(CYTHON_USE_PYLONG_INTERNALS)
-    #define CYTHON_USE_PYLONG_INTERNALS 1
+    #define CYTHON_USE_PYLONG_INTERNALS (PY_VERSION_HEX < 0x030C00A5)
   #endif
   #ifndef CYTHON_USE_PYLIST_INTERNALS
     #define CYTHON_USE_PYLIST_INTERNALS 1
@@ -242,7 +246,7 @@
     #define CYTHON_USE_TP_FINALIZE (PY_VERSION_HEX >= 0x030400a1)
   #endif
   #ifndef CYTHON_USE_DICT_VERSIONS
-    #define CYTHON_USE_DICT_VERSIONS (PY_VERSION_HEX >= 0x030600B1)
+    #define CYTHON_USE_DICT_VERSIONS ((PY_VERSION_HEX >= 0x030600B1) && (PY_VERSION_HEX < 0x030C00A5))
   #endif
   #if PY_VERSION_HEX >= 0x030B00A4
     #undef CYTHON_USE_EXC_INFO_STACK
@@ -365,9 +369,6 @@
   #endif
 #endif
 
-#if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX < 0x02070600 && !defined(Py_OptimizeFlag)
-  #define Py_OptimizeFlag 0
-#endif
 #define __PYX_BUILD_PY_SSIZE_T "n"
 #define CYTHON_FORMAT_SSIZE_T "z"
 #if PY_MAJOR_VERSION < 3
@@ -445,6 +446,11 @@
 #endif
   #define __Pyx_DefaultClassType PyType_Type
 #endif
+#if PY_VERSION_HEX >= 0x030900F0 && !CYTHON_COMPILING_IN_PYPY
+  #define __Pyx_PyObject_GC_IsFinalized(o) PyObject_GC_IsFinalized(o)
+#else
+  #define __Pyx_PyObject_GC_IsFinalized(o) _PyGC_FINALIZED(o)
+#endif
 #ifndef Py_TPFLAGS_CHECKTYPES
   #define Py_TPFLAGS_CHECKTYPES 0
 #endif
@@ -552,11 +558,11 @@ static CYTHON_INLINE void * PyThread_tss_get(Py_tss_t *key) {
 #endif
 #if PY_VERSION_HEX > 0x03030000 && defined(PyUnicode_KIND)
   #define CYTHON_PEP393_ENABLED 1
-  #if defined(PyUnicode_IS_READY)
-  #define __Pyx_PyUnicode_READY(op)       (likely(PyUnicode_IS_READY(op)) ?\
-                                              0 : _PyUnicode_Ready((PyObject *)(op)))
+  #if PY_VERSION_HEX >= 0x030C0000
+    #define __Pyx_PyUnicode_READY(op)       (0)
   #else
-  #define __Pyx_PyUnicode_READY(op)       (0)
+    #define __Pyx_PyUnicode_READY(op)       (likely(PyUnicode_IS_READY(op)) ?\
+                                                0 : _PyUnicode_Ready((PyObject *)(op)))
   #endif
   #define __Pyx_PyUnicode_GET_LENGTH(u)   PyUnicode_GET_LENGTH(u)
   #define __Pyx_PyUnicode_READ_CHAR(u, i) PyUnicode_READ_CHAR(u, i)
@@ -565,14 +571,14 @@ static CYTHON_INLINE void * PyThread_tss_get(Py_tss_t *key) {
   #define __Pyx_PyUnicode_DATA(u)         PyUnicode_DATA(u)
   #define __Pyx_PyUnicode_READ(k, d, i)   PyUnicode_READ(k, d, i)
   #define __Pyx_PyUnicode_WRITE(k, d, i, ch)  PyUnicode_WRITE(k, d, i, ch)
-  #if defined(PyUnicode_IS_READY) && defined(PyUnicode_GET_SIZE)
-  #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x03090000
-  #define __Pyx_PyUnicode_IS_TRUE(u)      (0 != (likely(PyUnicode_IS_READY(u)) ? PyUnicode_GET_LENGTH(u) : ((PyCompactUnicodeObject *)(u))->wstr_length))
-  #else
-  #define __Pyx_PyUnicode_IS_TRUE(u)      (0 != (likely(PyUnicode_IS_READY(u)) ? PyUnicode_GET_LENGTH(u) : PyUnicode_GET_SIZE(u)))
-  #endif
+  #if PY_VERSION_HEX >= 0x030C0000
+    #define __Pyx_PyUnicode_IS_TRUE(u)      (0 != PyUnicode_GET_LENGTH(u))
   #else
-  #define __Pyx_PyUnicode_IS_TRUE(u)      (0 != PyUnicode_GET_LENGTH(u))
+    #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x03090000
+    #define __Pyx_PyUnicode_IS_TRUE(u)      (0 != (likely(PyUnicode_IS_READY(u)) ? PyUnicode_GET_LENGTH(u) : ((PyCompactUnicodeObject *)(u))->wstr_length))
+    #else
+    #define __Pyx_PyUnicode_IS_TRUE(u)      (0 != (likely(PyUnicode_IS_READY(u)) ? PyUnicode_GET_LENGTH(u) : PyUnicode_GET_SIZE(u)))
+    #endif
   #endif
 #else
   #define CYTHON_PEP393_ENABLED 0
@@ -1339,7 +1345,7 @@ static CYTHON_SMALL_CODE int __Pyx_InitCachedConstants(void) {
 }
 
 static CYTHON_SMALL_CODE int __Pyx_InitGlobals(void) {
-  if (__Pyx_InitStrings(__pyx_string_tab) < 0) __PYX_ERR(0, 1, __pyx_L1_error);
+  if (__Pyx_InitStrings(__pyx_string_tab) < 0) __PYX_ERR(0, 1, __pyx_L1_error)
   return 0;
   __pyx_L1_error:;
   return -1;
@@ -1575,7 +1581,7 @@ if (!__Pyx_RefNanny) {
   Py_INCREF(__pyx_b);
   __pyx_cython_runtime = PyImport_AddModule((char *) "cython_runtime"); if (unlikely(!__pyx_cython_runtime)) __PYX_ERR(0, 1, __pyx_L1_error)
   Py_INCREF(__pyx_cython_runtime);
-  if (PyObject_SetAttrString(__pyx_m, "__builtins__", __pyx_b) < 0) __PYX_ERR(0, 1, __pyx_L1_error);
+  if (PyObject_SetAttrString(__pyx_m, "__builtins__", __pyx_b) < 0) __PYX_ERR(0, 1, __pyx_L1_error)
   /*--- Initialize various global constants etc. ---*/
   if (__Pyx_InitGlobals() < 0) __PYX_ERR(0, 1, __pyx_L1_error)
   #if PY_MAJOR_VERSION < 3 && (__PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT)
@@ -1772,7 +1778,7 @@ static CYTHON_INLINE void __Pyx_ErrFetchInState(PyThreadState *tstate, PyObject
 
 /* CLineInTraceback */
 #ifndef CYTHON_CLINE_IN_TRACEBACK
-static int __Pyx_CLineForTraceback(CYTHON_NCP_UNUSED PyThreadState *tstate, int c_line) {
+static int __Pyx_CLineForTraceback(CYTHON_UNUSED PyThreadState *tstate, int c_line) {
     PyObject *use_cline;
     PyObject *ptype, *pvalue, *ptraceback;
 #if CYTHON_COMPILING_IN_CPYTHON
@@ -2119,7 +2125,7 @@ static CYTHON_INLINE long __Pyx_PyInt_As_long(PyObject *x) {
                     break;
             }
 #endif
-#if CYTHON_COMPILING_IN_CPYTHON
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030C00A7
             if (unlikely(Py_SIZE(x) < 0)) {
                 goto raise_neg_overflow;
             }
@@ -2315,7 +2321,7 @@ static CYTHON_INLINE int __Pyx_PyInt_As_int(PyObject *x) {
                     break;
             }
 #endif
-#if CYTHON_COMPILING_IN_CPYTHON
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030C00A7
             if (unlikely(Py_SIZE(x) < 0)) {
                 goto raise_neg_overflow;
             }
diff --git a/test/git/test_invocations.py b/test/git/test_invocations.py
index b14fb95d..1f61ffb6 100644
--- a/test/git/test_invocations.py
+++ b/test/git/test_invocations.py
@@ -58,6 +58,12 @@ def make_venv(self, mode):
             self.run_in_venv(venv_dir, venv_dir,
                              'pip', 'install', '-U',
                              'pip', 'wheel', 'packaging')
+        if sys.version_info.major == 3 and sys.version_info.minor >= 12:
+            # Py 3.12 no longer default-installs setuptools in environments
+            # See PEP-632
+            self.run_in_venv(venv_dir, venv_dir,
+                             'pip', 'install', '-U',
+                             'setuptools')
         return venv_dir
 
     def get_venv_bin(self, venv, command):