diff --git a/debian/changelog b/debian/changelog
index 6c3162bc4ae99cf7b2b1121714cde00d96b07879..fad132b45b392bf1b1766a2bd184d2856223c631 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+python-multidict (6.0.4-1.1) unstable; urgency=medium
+
+  * Non-maintainer upload.
+  * Pull upstream patches for Python 3.12 compatibility. Closes: #1055717.
+
+ -- Matthias Klose <doko@debian.org>  Wed, 03 Jan 2024 12:13:23 +0100
+
 python-multidict (6.0.4-1) unstable; urgency=medium
 
   * New upstream release
diff --git a/debian/patches/864.diff b/debian/patches/864.diff
new file mode 100644
index 0000000000000000000000000000000000000000..75cbcd57856c06aa97ffd4cc007a808ed4be139e
--- /dev/null
+++ b/debian/patches/864.diff
@@ -0,0 +1,293 @@
+diff --git a/CHANGES/864.bugfix.rst b/CHANGES/864.bugfix.rst
+new file mode 100644
+index 00000000..2cbd4b3e
+--- /dev/null
++++ b/CHANGES/864.bugfix.rst
+@@ -0,0 +1,2 @@
++Upgraded the C-API macros that have been deprecated in
++Python 3.9 and later removed in 3.13 -- :user:`webknjaz`.
+diff --git a/multidict/_multidict.c b/multidict/_multidict.c
+index 1ba79df3..bcb37137 100644
+--- a/multidict/_multidict.c
++++ b/multidict/_multidict.c
+@@ -709,13 +709,21 @@ static inline void
+ multidict_tp_dealloc(MultiDictObject *self)
+ {
+     PyObject_GC_UnTrack(self);
++#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 9
++    Py_TRASHCAN_BEGIN(self, multidict_tp_dealloc)
++#else
+     Py_TRASHCAN_SAFE_BEGIN(self);
++#endif
+     if (self->weaklist != NULL) {
+         PyObject_ClearWeakRefs((PyObject *)self);
+     };
+     pair_list_dealloc(&self->pairs);
+     Py_TYPE(self)->tp_free((PyObject *)self);
++#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 9
++    Py_TRASHCAN_END // there should be no code after this
++#else
+     Py_TRASHCAN_SAFE_END(self);
++#endif
+ }
+ 
+ static inline int
+@@ -777,9 +785,12 @@ multidict_add(MultiDictObject *self, PyObject *const *args,
+         return NULL;
+     }
+ #else
+-    static _PyArg_Parser _parser = {NULL, _keywords, "add", 0};
++    static _PyArg_Parser _parser = {
++        .keywords = _keywords,
++        .fname = "add",
++        .kwtuple = NULL,
++    };
+     PyObject *argsbuf[2];
+-
+     args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames,
+                                  &_parser, 2, 2, 0, argsbuf);
+     if (!args) {
+@@ -1655,6 +1666,9 @@ getversion(PyObject *self, PyObject *md)
+ static inline void
+ module_free(void *m)
+ {
++#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 9
++    Py_CLEAR(multidict_str_lower);
++#endif
+     Py_CLEAR(collections_abc_mapping);
+     Py_CLEAR(collections_abc_mut_mapping);
+     Py_CLEAR(collections_abc_mut_multi_mapping);
+@@ -1683,6 +1697,13 @@ static PyModuleDef multidict_module = {
+ PyMODINIT_FUNC
+ PyInit__multidict()
+ {
++#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 9
++    multidict_str_lower = PyUnicode_InternFromString("lower");
++    if (multidict_str_lower == NULL) {
++        goto fail;
++    }
++#endif
++
+     PyObject *module = NULL,
+              *reg_func_call_result = NULL;
+ 
+@@ -1813,6 +1834,9 @@ PyInit__multidict()
+     return module;
+ 
+ fail:
++#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 9
++    Py_XDECREF(multidict_str_lower);
++#endif
+     Py_XDECREF(collections_abc_mapping);
+     Py_XDECREF(collections_abc_mut_mapping);
+     Py_XDECREF(collections_abc_mut_multi_mapping);
+diff --git a/multidict/_multilib/defs.h b/multidict/_multilib/defs.h
+index c7027c81..55c21074 100644
+--- a/multidict/_multilib/defs.h
++++ b/multidict/_multilib/defs.h
+@@ -5,7 +5,11 @@
+ extern "C" {
+ #endif
+ 
++#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 9
++static PyObject *multidict_str_lower = NULL;
++#else
+ _Py_IDENTIFIER(lower);
++#endif
+ 
+ /* We link this module statically for convenience.  If compiled as a shared
+    library instead, some compilers don't allow addresses of Python objects
+diff --git a/multidict/_multilib/istr.h b/multidict/_multilib/istr.h
+index 2688f489..61dc61ae 100644
+--- a/multidict/_multilib/istr.h
++++ b/multidict/_multilib/istr.h
+@@ -43,7 +43,11 @@ istr_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+     if (!ret) {
+         goto fail;
+     }
++#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 9
++    s = PyObject_CallMethodNoArgs(ret, multidict_str_lower);
++#else
+     s =_PyObject_CallMethodId(ret, &PyId_lower, NULL);
++#endif
+     if (!s) {
+         goto fail;
+     }
+diff --git a/multidict/_multilib/pair_list.h b/multidict/_multilib/pair_list.h
+index 7eafd215..15291d46 100644
+--- a/multidict/_multilib/pair_list.h
++++ b/multidict/_multilib/pair_list.h
+@@ -8,8 +8,7 @@ extern "C" {
+ #include <string.h>
+ #include <stddef.h>
+ #include <stdint.h>
+-
+-typedef PyObject * (*calc_identity_func)(PyObject *key);
++#include <stdbool.h>
+ 
+ typedef struct pair {
+     PyObject  *identity;  // 8
+@@ -38,12 +37,12 @@ HTTP headers into the buffer without allocating an extra memory block.
+ #define EMBEDDED_CAPACITY 29
+ #endif
+ 
+-typedef struct pair_list {  // 40
+-    Py_ssize_t  capacity;   // 8
+-    Py_ssize_t  size;       // 8
+-    uint64_t  version;      // 8
+-    calc_identity_func calc_identity;  // 8
+-    pair_t *pairs;          // 8
++typedef struct pair_list {
++    Py_ssize_t capacity;
++    Py_ssize_t size;
++    uint64_t version;
++    bool calc_ci_indentity;
++    pair_t *pairs;
+     pair_t buffer[EMBEDDED_CAPACITY];
+ } pair_list_t;
+ 
+@@ -111,7 +110,11 @@ ci_key_to_str(PyObject *key)
+         return ret;
+     }
+     if (PyUnicode_Check(key)) {
++#if PY_VERSION_HEX < 0x03090000
+         return _PyObject_CallMethodId(key, &PyId_lower, NULL);
++#else
++        return PyObject_CallMethodNoArgs(key, multidict_str_lower);
++#endif
+     }
+     PyErr_SetString(PyExc_TypeError,
+                     "CIMultiDict keys should be either str "
+@@ -199,30 +202,38 @@ pair_list_shrink(pair_list_t *list)
+ 
+ 
+ static inline int
+-_pair_list_init(pair_list_t *list, calc_identity_func calc_identity)
++_pair_list_init(pair_list_t *list, bool calc_ci_identity)
+ {
++    list->calc_ci_indentity = calc_ci_identity;
+     list->pairs = list->buffer;
+     list->capacity = EMBEDDED_CAPACITY;
+     list->size = 0;
+     list->version = NEXT_VERSION();
+-    list->calc_identity = calc_identity;
+     return 0;
+ }
+ 
+ static inline int
+ pair_list_init(pair_list_t *list)
+ {
+-    return _pair_list_init(list, key_to_str);
++    return _pair_list_init(list, /* calc_ci_identity = */ false);
+ }
+ 
+ 
+ static inline int
+ ci_pair_list_init(pair_list_t *list)
+ {
+-    return _pair_list_init(list, ci_key_to_str);
++    return _pair_list_init(list, /* calc_ci_identity = */ true);
+ }
+ 
+ 
++static inline PyObject *
++pair_list_calc_identity(pair_list_t *list, PyObject *key)
++{
++    if (list->calc_ci_indentity)
++        return ci_key_to_str(key);
++    return key_to_str(key);
++}
++
+ static inline void
+ pair_list_dealloc(pair_list_t *list)
+ {
+@@ -304,7 +315,7 @@ pair_list_add(pair_list_t *list,
+     PyObject *identity = NULL;
+     int ret;
+ 
+-    identity = list->calc_identity(key);
++    identity = pair_list_calc_identity(list, key);
+     if (identity == NULL) {
+         goto fail;
+     }
+@@ -412,7 +423,7 @@ pair_list_del(pair_list_t *list, PyObject *key)
+     Py_hash_t hash;
+     int ret;
+ 
+-    identity = list->calc_identity(key);
++    identity = pair_list_calc_identity(list, key);
+     if (identity == NULL) {
+         goto fail;
+     }
+@@ -486,7 +497,7 @@ pair_list_contains(pair_list_t *list, PyObject *key)
+     PyObject *identity = NULL;
+     int tmp;
+ 
+-    ident = list->calc_identity(key);
++    ident = pair_list_calc_identity(list, key);
+     if (ident == NULL) {
+         goto fail;
+     }
+@@ -528,7 +539,7 @@ pair_list_get_one(pair_list_t *list, PyObject *key)
+     PyObject *value = NULL;
+     int tmp;
+ 
+-    ident = list->calc_identity(key);
++    ident = pair_list_calc_identity(list, key);
+     if (ident == NULL) {
+         goto fail;
+     }
+@@ -573,7 +584,7 @@ pair_list_get_all(pair_list_t *list, PyObject *key)
+     PyObject *res = NULL;
+     int tmp;
+ 
+-    ident = list->calc_identity(key);
++    ident = pair_list_calc_identity(list, key);
+     if (ident == NULL) {
+         goto fail;
+     }
+@@ -631,7 +642,7 @@ pair_list_set_default(pair_list_t *list, PyObject *key, PyObject *value)
+     PyObject *value2 = NULL;
+     int tmp;
+ 
+-    ident = list->calc_identity(key);
++    ident = pair_list_calc_identity(list, key);
+     if (ident == NULL) {
+         goto fail;
+     }
+@@ -680,7 +691,7 @@ pair_list_pop_one(pair_list_t *list, PyObject *key)
+     int tmp;
+     PyObject *ident = NULL;
+ 
+-    ident = list->calc_identity(key);
++    ident = pair_list_calc_identity(list, key);
+     if (ident == NULL) {
+         goto fail;
+     }
+@@ -730,7 +741,7 @@ pair_list_pop_all(pair_list_t *list, PyObject *key)
+     PyObject *res = NULL;
+     PyObject *ident = NULL;
+ 
+-    ident = list->calc_identity(key);
++    ident = pair_list_calc_identity(list, key);
+     if (ident == NULL) {
+         goto fail;
+     }
+@@ -826,7 +837,7 @@ pair_list_replace(pair_list_t *list, PyObject * key, PyObject *value)
+     PyObject *identity = NULL;
+     Py_hash_t hash;
+ 
+-    identity = list->calc_identity(key);
++    identity = pair_list_calc_identity(list, key);
+     if (identity == NULL) {
+         goto fail;
+     }
+@@ -1101,7 +1112,7 @@ pair_list_update_from_seq(pair_list_t *list, PyObject *seq)
+         Py_INCREF(key);
+         Py_INCREF(value);
+ 
+-        identity = list->calc_identity(key);
++        identity = pair_list_calc_identity(list, key);
+         if (identity == NULL) {
+             goto fail_1;
+         }
diff --git a/debian/patches/877.diff b/debian/patches/877.diff
new file mode 100644
index 0000000000000000000000000000000000000000..3cc42a1a56f5cdec47f7c09f22a51fe06aab6b22
--- /dev/null
+++ b/debian/patches/877.diff
@@ -0,0 +1,20 @@
+diff --git a/CHANGES/877.packaging.rst b/CHANGES/877.packaging.rst
+new file mode 100644
+index 00000000..93b3bb31
+--- /dev/null
++++ b/CHANGES/877.packaging.rst
+@@ -0,0 +1,2 @@
++Declared Python 3.12 supported officially in the
++distribution package metadata -- by :user:`hugovk`.
+diff --git a/setup.py b/setup.py
+index dba780f5..733fa54e 100644
+--- a/setup.py
++++ b/setup.py
+@@ -66,6 +66,7 @@ def read(f):
+         "Programming Language :: Python :: 3.9",
+         "Programming Language :: Python :: 3.10",
+         "Programming Language :: Python :: 3.11",
++        "Programming Language :: Python :: 3.12",
+         "Development Status :: 5 - Production/Stable",
+     ],
+     author="Andrew Svetlov",
diff --git a/debian/patches/series b/debian/patches/series
index eca6fbb397d80673d52866157475aabf39beae51..7405aa77e81539a1213a497e7d83f4a23b3cdf09 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1 +1,3 @@
 0001-Drop-coverage-options-from-pytest.patch
+864.diff
+877.diff