[PATCH 1/9] BUP_ASSIGN_PYLONG_TO_INTEGRAL: check the sign of the *destination*

0 views
Skip to first unread message

Rob Browning

unread,
Jun 30, 2026, 2:22:31 PM (4 days ago) Jun 30
to bup-...@googlegroups.com
Check EXPR_SIGNED(*(dest)), not the sign of the address of the
destination...

Signed-off-by: Rob Browning <r...@defaultvalue.org>
Tested-by: Rob Browning <r...@defaultvalue.org>
---
src/bup/pyutil.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/bup/pyutil.h b/src/bup/pyutil.h
index 1a0ca74d..fbd09c28 100644
--- a/src/bup/pyutil.h
+++ b/src/bup/pyutil.h
@@ -23,7 +23,7 @@ int bup_ullong_from_py(unsigned long long *x, PyObject *py, const char *name);
({ \
int result = 0; \
int pending_overflow = 0; \
- if (EXPR_SIGNED(dest)) { \
+ if (EXPR_SIGNED(*(dest))) { \
const long long tmp = PyLong_AsLongLong(pylong); \
if (tmp == -1 && PyErr_Occurred() \
&& PyErr_ExceptionMatches(PyExc_OverflowError)) \
--
2.47.3

Rob Browning

unread,
Jun 30, 2026, 2:22:31 PM (4 days ago) Jun 30
to bup-...@googlegroups.com
Use __auto_type to make it obvious we only evaluate pylong and
overflow once, and then restructure the code to write overflow
directly.

Signed-off-by: Rob Browning <r...@defaultvalue.org>
Tested-by: Rob Browning <r...@defaultvalue.org>
---
src/bup/pyutil.h | 30 ++++++++++++++----------------
1 file changed, 14 insertions(+), 16 deletions(-)

diff --git a/src/bup/pyutil.h b/src/bup/pyutil.h
index 10a0893a..4b1b428b 100644
--- a/src/bup/pyutil.h
+++ b/src/bup/pyutil.h
@@ -22,35 +22,33 @@ int bup_ullong_from_py(unsigned long long *x, PyObject *py, const char *name);
#define BUP_ASSIGN_PYLONG_TO_INTEGRAL(dest, pylong, overflow) \
({ \
int res___ = 0; \
- int pending_overflow___ = 0; \
+ __auto_type pylong___ = (pylong); \
+ __auto_type overflow___ = (overflow); \
if (EXPR_SIGNED(*(dest))) { \
- const long long tmp___ = PyLong_AsLongLong(pylong); \
+ const long long tmp___ = PyLong_AsLongLong(pylong___); \
if (tmp___ == -1 && PyErr_Occurred() \
- && PyErr_ExceptionMatches(PyExc_OverflowError)) \
- pending_overflow___ = 2; \
- else { \
+ && PyErr_ExceptionMatches(PyExc_OverflowError)) { \
+ PyErr_Clear(); \
+ *overflow___ = 1; \
+ } else { \
if (INT_ADD_OK(tmp___, 0, (dest))) \
res___ = 1; \
else \
- pending_overflow___ = 1; \
+ *overflow___ = 1; \
} \
} else { \
const unsigned long long tmp___ = \
- PyLong_AsUnsignedLongLong(pylong); \
+ PyLong_AsUnsignedLongLong(pylong___); \
if (tmp___ == (unsigned long long) -1 && PyErr_Occurred() \
- && PyErr_ExceptionMatches(PyExc_OverflowError)) \
- pending_overflow___ = 2; \
- else { \
+ && PyErr_ExceptionMatches(PyExc_OverflowError)) { \
+ PyErr_Clear(); \
+ *overflow___ = 1; \
+ } else { \
if (INT_ADD_OK(tmp___, 0, (dest))) \
res___ = 1; \
else \
- pending_overflow___ = 1; \
+ *overflow___ = 1; \
} \
} \
- if (pending_overflow___) { \
- if (pending_overflow___ == 2) \
- PyErr_Clear(); \
- *(overflow) = 1; \
- } \
res___; \
})
--
2.47.3

Rob Browning

unread,
Jun 30, 2026, 2:22:32 PM (4 days ago) Jun 30
to bup-...@googlegroups.com
Signed-off-by: Rob Browning <r...@defaultvalue.org>
Tested-by: Rob Browning <r...@defaultvalue.org>
---
.gitignore | 2 +
GNUmakefile | 9 +-
src/bup/test-pyutil.c | 206 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 216 insertions(+), 1 deletion(-)
create mode 100644 src/bup/test-pyutil.c

diff --git a/.gitignore b/.gitignore
index e7917dd1..322e6b88 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,6 +37,8 @@
/src/bup/compat.[do]
/src/bup/io.[do]
/src/bup/pyutil.[do]
+/src/bup/test-pyutil.[do]
+/test/ext/test-pyutil
/test/int/__init__.pyc
/test/lib/__init__.pyc
/test/lib/buptest/__init__.pyc
diff --git a/GNUmakefile b/GNUmakefile
index 14a433f1..d1d6a0cc 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -300,6 +300,13 @@ generated_dependencies += lib/bup/_helpers.d src/bup/pyutil.d lib/bup/bupsplit.d
lib/bup/_helpers$(soext): lib/bup/_helpers.o src/bup/pyutil.o lib/bup/bupsplit.o lib/bup/_hashsplit.o
$(ld_helpers)

+clean_paths += src/bup/test-pyutil.o test/ext/test-pyutil
+generated_dependencies += src/bup/test-pyutil.d
+src/bup/test-%.o: src/bup/test-%.c
+ $(cc_bin)
+test/ext/test-%: src/bup/test-%.o src/bup/pyutil.o
+ $(ld_bin)
+
test/tmp:
mkdir test/tmp

@@ -332,7 +339,7 @@ define run_check
./pytest $(xdist_opt)
endef

-check_targets := all test/tmp dev/python
+check_targets := all test/tmp dev/python test/ext/test-pyutil

check: $(check_targets)
$(run_check)
diff --git a/src/bup/test-pyutil.c b/src/bup/test-pyutil.c
new file mode 100644
index 00000000..68847580
--- /dev/null
+++ b/src/bup/test-pyutil.c
@@ -0,0 +1,206 @@
+
+#define _LARGEFILE64_SOURCE 1
+#define PY_SSIZE_T_CLEAN 1
+#undef NDEBUG
+#include "../config/config.h"
+
+// According to Python, its header has to go first:
+// http://docs.python.org/3/c-api/intro.html#include-files
+#include <Python.h>
+
+#include <assert.h>
+#include <limits.h>
+
+#include "bup/pyutil.h"
+
+
+static void
+test_bup_assign_pylong_to_integral(void)
+{
+ PyObject *zero = PyLong_FromLong(0);
+ PyObject *one = PyLong_FromLong(1);
+ PyObject *neg_one = PyLong_FromLong(-1);
+ assert (zero);
+ assert (one);
+ assert (neg_one);
+
+ int overflow;
+ {
+ long long i;
+ PyObject *min = PyLong_FromLongLong(LLONG_MIN);
+ PyObject *max = PyLong_FromLongLong(LLONG_MAX);
+ PyObject *under = PyNumber_Subtract(min, one);
+ PyObject *over = PyNumber_Add(max, one);
+
+ overflow = 0;
+ assert(BUP_ASSIGN_PYLONG_TO_INTEGRAL(&i, min, &overflow));
+ assert(i == LLONG_MIN);
+ assert(!overflow);
+ overflow = 0;
+ assert(BUP_ASSIGN_PYLONG_TO_INTEGRAL(&i, max, &overflow));
+ assert(i == LLONG_MAX);
+ assert(!overflow);
+ overflow = 0;
+ assert(!BUP_ASSIGN_PYLONG_TO_INTEGRAL(&i, under, &overflow));
+ assert(overflow);
+ overflow = 0;
+ assert(!BUP_ASSIGN_PYLONG_TO_INTEGRAL(&i, over, &overflow));
+ assert(overflow);
+
+ Py_DECREF(max);
+ Py_DECREF(min);
+ Py_DECREF(over);
+ Py_DECREF(under);
+ }
+ {
+ unsigned long long i;
+ PyObject *max = PyLong_FromUnsignedLongLong(ULLONG_MAX);
+ PyObject *over = PyNumber_Add(max, one);
+
+ overflow = 0;
+ assert(BUP_ASSIGN_PYLONG_TO_INTEGRAL(&i, zero, &overflow));
+ assert(i == 0);
+ assert(!overflow);
+ overflow = 0;
+ assert(BUP_ASSIGN_PYLONG_TO_INTEGRAL(&i, max, &overflow));
+ assert(i == ULLONG_MAX);
+ assert(!overflow);
+ overflow = 0;
+ assert(!BUP_ASSIGN_PYLONG_TO_INTEGRAL(&i, neg_one, &overflow));
+ assert(overflow);
+ overflow = 0;
+ assert(!BUP_ASSIGN_PYLONG_TO_INTEGRAL(&i, over, &overflow));
+ assert(overflow);
+
+ Py_DECREF(over);
+ Py_DECREF(max);
+ }
+ {
+ assert(sizeof(short) < sizeof(long long));
+
+ short i;
+ PyObject *min = PyLong_FromLongLong(SHRT_MIN);
+ PyObject *max = PyLong_FromLongLong(SHRT_MAX);
+ PyObject *under = PyNumber_Subtract(min, one);
+ PyObject *over = PyNumber_Add(max, one);
+
+ overflow = 0;
+ assert(BUP_ASSIGN_PYLONG_TO_INTEGRAL(&i, min, &overflow));
+ assert(i == SHRT_MIN);
+ assert(!overflow);
+ overflow = 0;
+ assert(BUP_ASSIGN_PYLONG_TO_INTEGRAL(&i, max, &overflow));
+ assert(i == SHRT_MAX);
+ assert(!overflow);
+ overflow = 0;
+ assert(!BUP_ASSIGN_PYLONG_TO_INTEGRAL(&i, under, &overflow));
+ assert(overflow);
+ overflow = 0;
+ assert(!BUP_ASSIGN_PYLONG_TO_INTEGRAL(&i, over, &overflow));
+ assert(overflow);
+
+ Py_DECREF(max);
+ Py_DECREF(min);
+ Py_DECREF(over);
+ Py_DECREF(under);
+ }
+ {
+ assert(sizeof(unsigned short) < sizeof(unsigned long long));
+
+ unsigned short i;
+ PyObject *max = PyLong_FromUnsignedLongLong(USHRT_MAX);
+ PyObject *over = PyNumber_Add(max, one);
+
+ overflow = 0;
+ assert(BUP_ASSIGN_PYLONG_TO_INTEGRAL(&i, zero, &overflow));
+ assert(i == 0);
+ assert(!overflow);
+ overflow = 0;
+ assert(BUP_ASSIGN_PYLONG_TO_INTEGRAL(&i, max, &overflow));
+ assert(i == USHRT_MAX);
+ assert(!overflow);
+ overflow = 0;
+ assert(!BUP_ASSIGN_PYLONG_TO_INTEGRAL(&i, neg_one, &overflow));
+ assert(overflow);
+ overflow = 0;
+ assert(!BUP_ASSIGN_PYLONG_TO_INTEGRAL(&i, over, &overflow));
+ assert(overflow);
+
+ Py_DECREF(over);
+ Py_DECREF(max);
+ }
+
+ {
+ int i;
+ unsigned u;
+
+ overflow = 0;
+ assert(!BUP_ASSIGN_PYLONG_TO_INTEGRAL(&i, Py_None, &overflow));
+ assert(!overflow);
+ assert(PyErr_Occurred());
+ PyErr_Clear();
+ overflow = 0;
+ assert(!BUP_ASSIGN_PYLONG_TO_INTEGRAL(&u, Py_None, &overflow));
+ assert(!overflow);
+ assert(PyErr_Occurred());
+ PyErr_Clear();
+ }
+
+ Py_DECREF(neg_one);
+ Py_DECREF(one);
+ Py_DECREF(zero);
+ fprintf(stderr, "test-pyutil::%s OK\n", __func__);
+}
+
+static PyObject*
+run(PyObject *self, PyObject *args)
+{
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+
+ test_bup_assign_pylong_to_integral();
+
+ fprintf(stderr, "test-pyutil OK\n");
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef test_methods[] = {
+ {"run", run, METH_VARARGS, "Run tests." },
+ {NULL, NULL, 0, NULL}
+};
+
+static int setup_module(PyObject *mod)
+{
+ return 1;
+}
+
+static struct PyModuleDef bup_main_module_def = {
+ .m_base = PyModuleDef_HEAD_INIT,
+ .m_name = "bup_test",
+ .m_doc = "bup test module",
+ .m_size = -1,
+ .m_methods = test_methods
+};
+
+static PyObject *
+PyInit_bup_test(void) {
+ PyObject *mod = PyModule_Create(&bup_main_module_def);
+ if (!setup_module(mod))
+ {
+ Py_DECREF(mod);
+ return NULL;
+ }
+ return mod;
+}
+
+int
+main(int argc, char **argv)
+{
+ assert(argc == 1);
+ if (PyImport_AppendInittab("bup_test", PyInit_bup_test) == -1) {
+ fprintf(stderr, "unable to register bup_test module\n");
+ exit(2);
+ }
+ char *bup_argv[] = { argv[0], "-c", "import bup_test; bup_test.run()"};
+ return Py_BytesMain (3, bup_argv);
+}
--
2.47.3

Rob Browning

unread,
Jun 30, 2026, 2:22:32 PM (4 days ago) Jun 30
to bup-...@googlegroups.com
Set *overflow when an overflow exception is pending, *and* when
INTEGRAL_ASSIGNMENT_FITS fails.

Signed-off-by: Rob Browning <r...@defaultvalue.org>
Tested-by: Rob Browning <r...@defaultvalue.org>
---
src/bup/pyutil.h | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/bup/pyutil.h b/src/bup/pyutil.h
index fbd09c28..8e98e3ed 100644
--- a/src/bup/pyutil.h
+++ b/src/bup/pyutil.h
@@ -47,8 +47,9 @@ int bup_ullong_from_py(unsigned long long *x, PyObject *py, const char *name);
pending_overflow = 1; \
} \
} \
- if (pending_overflow == 2) { \
- PyErr_Clear(); \
+ if (pending_overflow) { \
+ if (pending_overflow == 2) \
+ PyErr_Clear(); \
*(overflow) = 1; \
} \
result; \
--
2.47.3

Rob Browning

unread,
Jun 30, 2026, 2:22:32 PM (4 days ago) Jun 30
to bup-...@googlegroups.com
Signed-off-by: Rob Browning <r...@defaultvalue.org>
Tested-by: Rob Browning <r...@defaultvalue.org>
---
src/bup/test-pyutil.c | 133 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 133 insertions(+)

diff --git a/src/bup/test-pyutil.c b/src/bup/test-pyutil.c
index 2af5cf74..f8b1608c 100644
--- a/src/bup/test-pyutil.c
+++ b/src/bup/test-pyutil.c
@@ -47,6 +47,138 @@ test_bup_longish_to_py()
fprintf(stderr, "test-pyutil::%s OK\n", __func__);
}

+static void
+test_bup_integral_from_py(void)
+{
+ PyObject *zero = PyLong_FromLong(0);
+ PyObject *one = PyLong_FromLong(1);
+ PyObject *neg_one = PyLong_FromLong(-1);
+ PyObject *ll_min = PyLong_FromLongLong(LLONG_MIN);
+ PyObject *ll_max = PyLong_FromLongLong(LLONG_MAX);
+ PyObject *ull_max = PyLong_FromUnsignedLongLong(ULLONG_MAX);
+ assert (one);
+ assert (neg_one);
+ assert (ll_min);
+ assert (ll_max);
+ assert (ull_max);
+
+ {
+ PyObject *min = PyLong_FromLong(INT_MIN);
+ PyObject *max = PyLong_FromLong(INT_MAX);
+ PyObject *umax = PyLong_FromUnsignedLong(UINT_MAX);
+ PyObject *under = PyNumber_Subtract(min, one);
+ PyObject *over = PyNumber_Add(max, one);
+ PyObject *uover = PyNumber_Add(umax, one);
+ assert(min);
+ assert(max);
+ assert(umax);
+ assert(under);
+ assert(over);
+ assert(uover);
+
+ int i;
+ assert(bup_int_from_py(&i, min, __func__));
+ assert(i == INT_MIN);
+ assert(bup_int_from_py(&i, max, __func__));
+ assert(i == INT_MAX);
+ assert(!bup_int_from_py(&i, under, __func__));
+ assert(PyErr_Occurred() == PyExc_OverflowError);
+ PyErr_Clear();
+ assert(!bup_int_from_py(&i, over, __func__));
+ assert(PyErr_Occurred() == PyExc_OverflowError);
+ PyErr_Clear();
+ if (sizeof(int) < sizeof(long long)) {
+ assert(!bup_int_from_py(&i, ll_min, __func__));
+ assert(PyErr_Occurred() == PyExc_OverflowError);
+ PyErr_Clear();
+ assert(!bup_int_from_py(&i, ll_max, __func__));
+ assert(PyErr_Occurred() == PyExc_OverflowError);
+ PyErr_Clear();
+ }
+
+ unsigned u;
+ assert(bup_uint_from_py(&u, zero, __func__));
+ assert(u == 0);
+ assert(bup_uint_from_py(&u, umax, __func__));
+ assert(u == UINT_MAX);
+ assert(!bup_uint_from_py(&u, neg_one, __func__));
+ assert(PyErr_Occurred() == PyExc_OverflowError);
+ PyErr_Clear();
+ assert(!bup_uint_from_py(&u, uover, __func__));
+ assert(PyErr_Occurred() == PyExc_OverflowError);
+ PyErr_Clear();
+ if (sizeof(unsigned) < sizeof(unsigned long long)) {
+ assert(!bup_uint_from_py(&u, ull_max, __func__));
+ assert(PyErr_Occurred() == PyExc_OverflowError);
+ PyErr_Clear();
+ }
+
+ Py_DECREF(uover);
+ Py_DECREF(over);
+ Py_DECREF(under);
+ Py_DECREF(umax);
+ Py_DECREF(max);
+ Py_DECREF(min);
+ }
+
+ {
+ PyObject *umax = PyLong_FromUnsignedLong(ULONG_MAX);
+ PyObject *uover = PyNumber_Add(umax, one);
+ assert(umax);
+ assert(uover);
+
+ unsigned long u;
+ assert(bup_ulong_from_py(&u, zero, __func__));
+ assert(u == 0);
+ assert(bup_ulong_from_py(&u, umax, __func__));
+ assert(u == ULONG_MAX);
+ assert(!bup_ulong_from_py(&u, neg_one, __func__));
+ assert(PyErr_Occurred() == PyExc_OverflowError);
+ PyErr_Clear();
+ assert(!bup_ulong_from_py(&u, uover, __func__));
+ assert(PyErr_Occurred() == PyExc_OverflowError);
+ PyErr_Clear();
+ if (sizeof(unsigned long) < sizeof(unsigned long long)) {
+ assert(!bup_ulong_from_py(&u, ull_max, __func__));
+ assert(PyErr_Occurred() == PyExc_OverflowError);
+ PyErr_Clear();
+ }
+
+ Py_DECREF(uover);
+ Py_DECREF(umax);
+ }
+
+ {
+ PyObject *umax = PyLong_FromUnsignedLongLong(ULLONG_MAX);
+ PyObject *uover = PyNumber_Add(umax, one);
+ assert(umax);
+ assert(uover);
+
+ unsigned long long u;
+ assert(bup_ullong_from_py(&u, zero, __func__));
+ assert(u == 0);
+ assert(bup_ullong_from_py(&u, umax, __func__));
+ assert(u == ULLONG_MAX);
+ assert(!bup_ullong_from_py(&u, neg_one, __func__));
+ assert(PyErr_Occurred() == PyExc_OverflowError);
+ PyErr_Clear();
+ assert(!bup_ullong_from_py(&u, uover, __func__));
+ assert(PyErr_Occurred() == PyExc_OverflowError);
+ PyErr_Clear();
+
+ Py_DECREF(uover);
+ Py_DECREF(umax);
+ }
+
+ Py_DECREF(ull_max);
+ Py_DECREF(ll_max);
+ Py_DECREF(ll_min);
+ Py_DECREF(neg_one);
+ Py_DECREF(one);
+ Py_DECREF(zero);
+ fprintf(stderr, "test-pyutil::%s OK\n", __func__);
+}
+
static void
test_bup_assign_pylong_to_integral(void)
{
@@ -192,6 +324,7 @@ run(PyObject *self, PyObject *args)
return NULL;

test_bup_longish_to_py();
+ test_bup_integral_from_py();
test_bup_assign_pylong_to_integral();

fprintf(stderr, "test-pyutil OK\n");
--
2.47.3

Rob Browning

unread,
Jun 30, 2026, 2:22:32 PM (4 days ago) Jun 30
to bup-...@googlegroups.com
Don't keep going.

Signed-off-by: Rob Browning <r...@defaultvalue.org>
Tested-by: Rob Browning <r...@defaultvalue.org>
---
src/bup/pyutil.h | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/src/bup/pyutil.h b/src/bup/pyutil.h
index 4b1b428b..e59266a6 100644
--- a/src/bup/pyutil.h
+++ b/src/bup/pyutil.h
@@ -26,10 +26,11 @@ int bup_ullong_from_py(unsigned long long *x, PyObject *py, const char *name);
__auto_type overflow___ = (overflow); \
if (EXPR_SIGNED(*(dest))) { \
const long long tmp___ = PyLong_AsLongLong(pylong___); \
- if (tmp___ == -1 && PyErr_Occurred() \
- && PyErr_ExceptionMatches(PyExc_OverflowError)) { \
- PyErr_Clear(); \
- *overflow___ = 1; \
+ if (tmp___ == -1 && PyErr_Occurred()) { \
+ if(PyErr_ExceptionMatches(PyExc_OverflowError)) { \
+ PyErr_Clear(); \
+ *overflow___ = 1; \
+ } \
} else { \
if (INT_ADD_OK(tmp___, 0, (dest))) \
res___ = 1; \
@@ -39,10 +40,11 @@ int bup_ullong_from_py(unsigned long long *x, PyObject *py, const char *name);
} else { \
const unsigned long long tmp___ = \
PyLong_AsUnsignedLongLong(pylong___); \
- if (tmp___ == (unsigned long long) -1 && PyErr_Occurred() \
- && PyErr_ExceptionMatches(PyExc_OverflowError)) { \
- PyErr_Clear(); \
- *overflow___ = 1; \
+ if (tmp___ == (unsigned long long) -1 && PyErr_Occurred()) { \
+ if (PyErr_ExceptionMatches(PyExc_OverflowError)) { \
+ PyErr_Clear(); \
+ *overflow___ = 1; \
+ } \
} else { \
if (INT_ADD_OK(tmp___, 0, (dest))) \
res___ = 1; \
--
2.47.3

Rob Browning

unread,
Jun 30, 2026, 2:22:32 PM (4 days ago) Jun 30
to bup-...@googlegroups.com
Name temporary variables with trailing underscores to reduce the
possibliity of shadowing.

Signed-off-by: Rob Browning <r...@defaultvalue.org>
Tested-by: Rob Browning <r...@defaultvalue.org>
---
src/bup/pyutil.h | 34 +++++++++++++++++-----------------
1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/src/bup/pyutil.h b/src/bup/pyutil.h
index 168ce43e..10a0893a 100644
--- a/src/bup/pyutil.h
+++ b/src/bup/pyutil.h
@@ -21,36 +21,36 @@ int bup_ullong_from_py(unsigned long long *x, PyObject *py, const char *name);
// will be pending.
#define BUP_ASSIGN_PYLONG_TO_INTEGRAL(dest, pylong, overflow) \
({ \
- int result = 0; \
- int pending_overflow = 0; \
+ int res___ = 0; \
+ int pending_overflow___ = 0; \
if (EXPR_SIGNED(*(dest))) { \
- const long long tmp = PyLong_AsLongLong(pylong); \
- if (tmp == -1 && PyErr_Occurred() \
+ const long long tmp___ = PyLong_AsLongLong(pylong); \
+ if (tmp___ == -1 && PyErr_Occurred() \
&& PyErr_ExceptionMatches(PyExc_OverflowError)) \
- pending_overflow = 2; \
+ pending_overflow___ = 2; \
else { \
- if (INT_ADD_OK(tmp, 0, (dest))) \
- result = 1; \
+ if (INT_ADD_OK(tmp___, 0, (dest))) \
+ res___ = 1; \
else \
- pending_overflow = 1; \
+ pending_overflow___ = 1; \
} \
} else { \
- const unsigned long long tmp = \
+ const unsigned long long tmp___ = \
PyLong_AsUnsignedLongLong(pylong); \
- if (tmp == (unsigned long long) -1 && PyErr_Occurred() \
+ if (tmp___ == (unsigned long long) -1 && PyErr_Occurred() \
&& PyErr_ExceptionMatches(PyExc_OverflowError)) \
- pending_overflow = 2; \
+ pending_overflow___ = 2; \
else { \
- if (INT_ADD_OK(tmp, 0, (dest))) \
- result = 1; \
+ if (INT_ADD_OK(tmp___, 0, (dest))) \
+ res___ = 1; \
else \
- pending_overflow = 1; \
+ pending_overflow___ = 1; \
} \
} \
- if (pending_overflow) { \
- if (pending_overflow == 2) \
+ if (pending_overflow___) { \
+ if (pending_overflow___ == 2) \
PyErr_Clear(); \
*(overflow) = 1; \
} \
- result; \
+ res___; \
})
--
2.47.3

Rob Browning

unread,
Jun 30, 2026, 2:22:34 PM (4 days ago) Jun 30
to bup-...@googlegroups.com
Signed-off-by: Rob Browning <r...@defaultvalue.org>
Tested-by: Rob Browning <r...@defaultvalue.org>
---
src/bup/test-pyutil.c | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)

diff --git a/src/bup/test-pyutil.c b/src/bup/test-pyutil.c
index 68847580..2af5cf74 100644
--- a/src/bup/test-pyutil.c
+++ b/src/bup/test-pyutil.c
@@ -14,6 +14,39 @@
#include "bup/pyutil.h"


+static void
+test_bup_longish_to_py()
+{
+ // Only test (unsigned) long long since that's what python offers,
+ // e.g. there's currently no (u)intmax_t support.
+ PyObject *ll_min_py = PyLong_FromLongLong(LLONG_MIN);
+ PyObject *ll_max_py = PyLong_FromLongLong(LLONG_MAX);
+ PyObject *ull_max_py = PyLong_FromUnsignedLongLong(ULLONG_MAX);
+ assert (ll_min_py);
+ assert (ll_max_py);
+ assert (ull_max_py);
+
+ PyObject *py = BUP_LONGISH_TO_PY(LLONG_MIN);
+ assert(py);
+ assert(PyObject_RichCompareBool(py, ll_min_py, Py_EQ));
+ Py_DECREF(py);
+
+ py = BUP_LONGISH_TO_PY(LLONG_MAX);
+ assert(py);
+ assert(PyObject_RichCompareBool(py, ll_max_py, Py_EQ));
+ Py_DECREF(py);
+
+ py = BUP_LONGISH_TO_PY(ULLONG_MAX);
+ assert(py);
+ assert(PyObject_RichCompareBool(py, ull_max_py, Py_EQ));
+ Py_DECREF(py);
+
+ Py_DECREF(ull_max_py);
+ Py_DECREF(ll_max_py);
+ Py_DECREF(ll_min_py);
+ fprintf(stderr, "test-pyutil::%s OK\n", __func__);
+}
+
static void
test_bup_assign_pylong_to_integral(void)
{
@@ -158,6 +191,7 @@ run(PyObject *self, PyObject *args)
if (!PyArg_ParseTuple(args, ""))
return NULL;

+ test_bup_longish_to_py();

Rob Browning

unread,
Jun 30, 2026, 2:24:49 PM (4 days ago) Jun 30
to bup-...@googlegroups.com
Rob Browning <r...@defaultvalue.org> writes:

> Check EXPR_SIGNED(*(dest)), not the sign of the address of the
> destination...

All these patches have been pushed to main.

They fix BUP_ASSIGN_PYLONG_TO_INTEGRAL and add tests for most of
pyutil.c.

--
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
Reply all
Reply to author
Forward
0 new messages