Raise minimum python version to 3.8

0 views
Skip to first unread message

Rob Browning

unread,
Jul 1, 2026, 6:01:37 PM (3 days ago) Jul 1
to bup-...@googlegroups.com
Pushed to main.

--
Rob Browning
rlb @defaultvalue.org and @debian.org
GPG as of 2011-07-10 E6A9 DA3C C9FD 1FF8 C676 D2C4 C0F0 39E9 ED1B 597A
GPG as of 2002-11-03 14DD 432F AE39 534D B592 F9A0 25C8 D377 8C7E 73A4

Rob Browning

unread,
Jul 1, 2026, 6:01:38 PM (3 days ago) Jul 1
to bup-...@googlegroups.com
This should allow us to drop our custom argv (bytes) handling since
the underlying issue

https://sourceware.org/bugzilla/show_bug.cgi?id=2373

appears to have been fixed upstream and then backported all the way to 3.8:

614e836f6e934854c6bbf698d759e5cde607a629
bpo-35883: Py_DecodeLocale() escapes invalid Unicode characters (GH-24843)
https://github.com/python/cpython/pull/24906

We can also count on having Py_BytesMain, and gain access to
assignment expressions and positional-only parameters.

Version 3.8's been available since 2019, and 3.7 is well past its
upstream EOL.

Signed-off-by: Rob Browning <r...@defaultvalue.org>
Tested-by: Rob Browning <r...@defaultvalue.org>
---
.gitignore | 1 -
GNUmakefile | 9 +++---
README.md | 2 +-
dev/python-proposed.c | 9 +-----
dev/validate-python | 4 +--
lib/bup/compat.py | 6 ++--
lib/bup/helpers.py | 3 +-
lib/cmd/bup.c | 67 ++++++++-----------------------------------
note/main.md | 2 ++
src/bup/compat.c | 45 -----------------------------
src/bup/compat.h | 3 --
11 files changed, 25 insertions(+), 126 deletions(-)
delete mode 100644 src/bup/compat.c
delete mode 100644 src/bup/compat.h

diff --git a/.gitignore b/.gitignore
index 322e6b88..061ea3de 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,7 +34,6 @@
/lib/cmd/bup
/lib/cmd/bup.[do]
/nbproject/
-/src/bup/compat.[do]
/src/bup/io.[do]
/src/bup/pyutil.[do]
/src/bup/test-pyutil.[do]
diff --git a/GNUmakefile b/GNUmakefile
index 14e7dd02..98e607fa 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -253,7 +253,6 @@ ld_py_bin = $(CC) $(LDFLAGS) $^ -o $@ $(py_embed_ldflags)
common_obj := \
dev/python-proposed.o \
lib/cmd/bup.o \
- src/bup/compat.o \
src/bup/io.o \
src/bup/pyutil.o \
src/bup/test-pyutil.o
@@ -287,7 +286,7 @@ clean_paths += lib/bup/_helpers$(soext)

## dev/python

-dev/python-proposed: dev/python-proposed.o src/bup/compat.o src/bup/io.o
+dev/python-proposed: dev/python-proposed.o src/bup/io.o
rm -f dev/python
$(ld_py_bin)
clean_paths += dev/python-proposed
@@ -306,7 +305,7 @@ dev/bup-exec.o: lib/cmd/bup.c
generated_dependencies += dev/bup-exec.d
clean_paths += dev/bup-exec.o

-dev/bup-exec: dev/bup-exec.o src/bup/compat.o src/bup/io.o
+dev/bup-exec: dev/bup-exec.o src/bup/io.o
$(ld_py_bin)
clean_paths += dev/bup-exec

@@ -316,11 +315,11 @@ dev/bup-python.o: lib/cmd/bup.c
generated_dependencies += dev/bup-python.d
clean_paths += dev/bup-python.o

-dev/bup-python: dev/bup-python.o src/bup/compat.o src/bup/io.o
+dev/bup-python: dev/bup-python.o src/bup/io.o
$(ld_py_bin)
clean_paths += dev/bup-python

-lib/cmd/bup: lib/cmd/bup.o src/bup/compat.o src/bup/io.o
+lib/cmd/bup: lib/cmd/bup.o src/bup/io.o
$(ld_py_bin)
clean_paths += lib/cmd/bup

diff --git a/README.md b/README.md
index ec3d2ed6..38b6dc07 100644
--- a/README.md
+++ b/README.md
@@ -71,7 +71,7 @@ Reasons you might want to avoid bup
more likely to eat your data. It's also missing some
probably-critical features, though fewer than it used to be.

- - While it is intended to work with python 3.7 or newer, a C
+ - While it is intended to work with python 3.8 or newer, a C
compiler, and an installed git version >= 1.7.2, it is currently
only automatically tested against some python versions 3.9 and
newer and git versions 2.3 and newer. Please report any
diff --git a/dev/python-proposed.c b/dev/python-proposed.c
index 4d2844cb..e5c732a1 100644
--- a/dev/python-proposed.c
+++ b/dev/python-proposed.c
@@ -7,16 +7,9 @@
// http://docs.python.org/3/c-api/intro.html#include-files
#include <Python.h>

-#include "bup/compat.h"
-
-#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 8
-# define bup_py_main bup_py_bytes_main
-#else
-# define bup_py_main Py_BytesMain
-#endif

int main(int argc, char **argv)
{
assert(argc > 0);
- return bup_py_main (argc, argv);
+ return Py_BytesMain (argc, argv);
}
diff --git a/dev/validate-python b/dev/validate-python
index eef0b322..59746dec 100755
--- a/dev/validate-python
+++ b/dev/validate-python
@@ -11,9 +11,9 @@ majver=$("$python" -c 'import sys; print(sys.version_info[0])')
minver=$("$python" -c 'import sys; print(sys.version_info[1])')

# May not be correct yet, i.e. actual requirement may be higher.
-if test "$majver" -lt 3 || test "$majver" -eq 3 && test "$minver" -lt 7; then
+if test "$majver" -lt 3 || test "$majver" -eq 3 && test "$minver" -lt 8; then
# utime follow_symlinks >= 3.3
bup_version_str=$("$python" --version 2>&1)
- echo "ERROR: found $bup_version_str (must be >= 3.7)" 1>&2
+ echo "ERROR: found $bup_version_str (must be >= 3.8)" 1>&2
exit 2
fi
diff --git a/lib/bup/compat.py b/lib/bup/compat.py
index a4ef8644..d8919a91 100644
--- a/lib/bup/compat.py
+++ b/lib/bup/compat.py
@@ -7,8 +7,6 @@ from os import environb as environ
from os import fsencode
import dataclasses, sys, traceback

-import bup_main
-

ver = sys.version_info

@@ -41,11 +39,11 @@ def argv_bytes(x):

def get_argvb():
"Return a new list containing the current process argv bytes."
- return bup_main.argv()
+ return [fsencode(x) for x in sys.argv]

def get_argv():
"Return a new list containing the current process argv strings."
- return [x.decode(errors='surrogateescape') for x in bup_main.argv()]
+ return list(sys.argv)

# Makes slots best effort
if (ver.major, ver.minor) >= (3, 10):
diff --git a/lib/bup/helpers.py b/lib/bup/helpers.py
index 18402d4a..b3eb8521 100644
--- a/lib/bup/helpers.py
+++ b/lib/bup/helpers.py
@@ -71,8 +71,7 @@ def getgroups():


class finalized:
- # pyupgrade 3.8+: add final / to make args positional only
- def __init__(self, what_or_how, how=None):
+ def __init__(self, what_or_how, how=None, /):
if how is None:
self.enter_result = None
self.finalize = what_or_how
diff --git a/lib/cmd/bup.c b/lib/cmd/bup.c
index efc24964..52653065 100644
--- a/lib/cmd/bup.c
+++ b/lib/cmd/bup.c
@@ -8,10 +8,10 @@
#include <Python.h>

// pyupgrade *: adjust
-#if PY_MAJOR_VERSION < 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 7)
+#if PY_MAJOR_VERSION < 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 8)
#define BUP_STR(x) #x
#define BUP_XSTR(x) BUP_STR(x)
-#pragma message "Python versions older than 3.7 are not supported; detected X.Y " \
+#pragma message "Python versions older than 3.8 are not supported; detected X.Y " \
BUP_XSTR(PY_MAJOR_VERSION) "." BUP_XSTR(PY_MINOR_VERSION)
#error "Halting"
#endif
@@ -30,35 +30,12 @@
#include <unistd.h>

#include "bup.h"
-#include "bup/compat.h"
#include "bup/intprops.h"
#include "bup/io.h"

-static int prog_argc = 0;
-static char **prog_argv = NULL;
static char *orig_env_pythonpath = NULL;

-// pyupgrade 3.8+: reconsider
-static PyObject*
-get_argv(PyObject *self, PyObject *args) // https://bugs.python.org/issue35883
-{
- if (!PyArg_ParseTuple(args, ""))
- return NULL;
-
- PyObject *result = PyList_New(prog_argc);
- int i;
- for (i = 0; i < prog_argc; i++) {
- PyObject *s = PyBytes_FromString(prog_argv[i]);
- if (!s)
- die(BUP_EXIT_FAILURE, "cannot convert argument to bytes: %s\n", prog_argv[i]);
- PyList_SET_ITEM(result, i, s);
- }
- return result;
-}
-
static PyMethodDef bup_main_methods[] = {
- {"argv", get_argv, METH_VARARGS,
- "Return the program's current argv array as a list of byte strings." },
{NULL, NULL, 0, NULL}
};

@@ -366,42 +343,18 @@ prepend_lib_to_pythonpath(const char * const exec_path,
free(parent);
}

-#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 8
-# define bup_py_main bup_py_bytes_main
-#else
-# define bup_py_main Py_BytesMain
-#endif
-
#if defined(BUP_DEV_BUP_PYTHON) && defined(BUP_DEV_BUP_EXEC)
# error "Both BUP_DEV_BUP_PYTHON and BUP_DEV_BUP_EXEC are defined"
#endif

-#ifdef BUP_DEV_BUP_PYTHON
+#if defined(BUP_DEV_BUP_PYTHON) || defined (BUP_DEV_BUP_EXEC)

int main(int argc, char **argv)
{
assert(argc > 0);
- prog_argc = argc;
- prog_argv = argv;
setup_bup_main_module();
prepend_lib_to_pythonpath(argv[0], "../lib");
- return bup_py_main (argc, argv);
-}
-
-#elif defined(BUP_DEV_BUP_EXEC)
-
-int main(int argc, char **argv)
-{
- assert(argc > 0);
- prog_argc = argc - 1;
- prog_argv = argv + 1;
- setup_bup_main_module();
- prepend_lib_to_pythonpath(argv[0], "../lib");
- if (argc == 1)
- return bup_py_main (1, argv);
- // This can't handle a script with a name like "-c", but that's
- // python's problem, not ours.
- return bup_py_main (2, argv);
+ return Py_BytesMain (argc, argv);
}

#else // normal bup command
@@ -409,12 +362,16 @@ int main(int argc, char **argv)
int main(int argc, char **argv)
{
assert(argc > 0);
- prog_argc = argc;
- prog_argv = argv;
setup_bup_main_module();
prepend_lib_to_pythonpath(argv[0], "..");
- char *bup_argv[] = { argv[0], "-m", "bup.main" };
- return bup_py_main (3, bup_argv);
+
+ char **bup_argv = alloca(argc * sizeof(char *) + 2);
+ bup_argv[0] = argv[0];
+ bup_argv[1] = "-m";
+ bup_argv[2] = "bup.main";
+ for (int i = 0; i < argc - 1; i++)
+ bup_argv[i + 3] = argv[i + 1];
+ return Py_BytesMain (argc + 2, bup_argv);
}

#endif // normal bup command
diff --git a/note/main.md b/note/main.md
index d8517cf7..039a64ad 100644
--- a/note/main.md
+++ b/note/main.md
@@ -4,6 +4,8 @@ Notable changes in main (incomplete)
May require attention
---------------------

+* The minimum Python version has been raised from 3.7 to 3.8.
+
* Versions of `bup` at or after 0.25 and before 0.30.1 might (rarely)
drop metadata entries for non-directories. That makes the metadata
for all of the other non-directory paths in the same directory
diff --git a/src/bup/compat.c b/src/bup/compat.c
deleted file mode 100644
index dc00fed5..00000000
--- a/src/bup/compat.c
+++ /dev/null
@@ -1,45 +0,0 @@
-
-#define PY_SSIZE_T_CLEAN
-#define _GNU_SOURCE 1 // asprintf
-#undef NDEBUG
-
-// According to Python, its header has to go first:
-// http://docs.python.org/3/c-api/intro.html#include-files
-#include <Python.h>
-
-#include "bup.h"
-#include "bup/compat.h"
-#include "bup/io.h"
-
-#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 8
-
-int bup_py_bytes_main(int argc, char **argv)
-{
- assert(argc > 0);
- wchar_t **wargv = PyMem_RawMalloc(argc * sizeof(wchar_t *));
- if (!wargv)
- die(BUP_EXIT_FAILURE, "memory insufficient to decode command line arguments");
- int i;
- for (i = 0; i < argc; i++) {
- size_t wargn;
- wargv[i] = Py_DecodeLocale(argv[i], &wargn);
- if (!wargv[i]) {
- switch (wargn) {
- case (size_t) -1:
- die(BUP_EXIT_FAILURE, "too little memory to decode command line argument %d\n",
- i);
- break;
- case (size_t) -2:
- die(BUP_EXIT_FAILURE, "unable to decode command line argument %d\n", i);
- break;
- default:
- die(BUP_EXIT_FAILURE, "unexpected error from Py_DecodeLocale(): %zu\n", wargn);
- break;
- }
- exit(BUP_EXIT_FAILURE);
- }
- }
- return Py_Main(argc, wargv);
-}
-
-#endif // PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 8
diff --git a/src/bup/compat.h b/src/bup/compat.h
deleted file mode 100644
index 2e3797c5..00000000
--- a/src/bup/compat.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-int bup_py_bytes_main(int argc, char **argv);
--
2.47.3

Reply all
Reply to author
Forward
0 new messages