Your message dated Mon, 14 Sep 2026 22:39:12 +0000
with message-id <[email protected]>
and subject line Bug#1144864: fixed in pandas 3.0.5+dfsg-1
has caused the Debian Bug report #1144864,
regarding pandas: FTBFS building against python 3.15
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact [email protected]
immediately.)


-- 
1144864: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1144864
Debian Bug Tracking System
Contact [email protected] with problems
--- Begin Message ---
Package: src:pandas
Version: 3.0.5+dfsg-1
User: [email protected]
Usertags: python3.15
Tags: patch

Hi!

While rebuilding the Python related packages against the Python 3.15rc1
version we found that pandas fails to build from source[1]. The upstream
code already has fixes for these issues and since there are many
packages that require pandas to build I already applied these fixes in
the packages that are in the rebuild sandbox[2].

Please consider applying this patches in your next upload, or consider
updating pandas when a new release including these patches is available.

Happy hacking,

[1]: 
https://debusine.debian.net/debian/r-python-python3.15/work-request/1012982/
[2]: https://debusine.debian.net/debian/r-python-python3.15/

--
"recursividad 95, 154, 156, 201, 224, 293"
-- El Lenguaje de Programacion C, pag. 293 (Kernighan & Ritchie)
Saludos /\/\ /\ >< `/
Description: Python 3.15 compatibility fixes
 Backport of upstream commit 7bb284ac43 (PR #66473) for Python 3.15 support:
 - Support optional %z and %:z (colon_z) in strptime
 - Explicitly close XML iterparse parser to avoid unclosed iterator ResourceWarning
 - Accept Python 3.15 TypeError / ValueError error message changes in tests
 - Define and export PY315 in pandas.compat
Origin: upstream, https://github.com/pandas-dev/pandas/commit/7bb284ac438d2b981a7f42e3bdd0fe5ee26aada0
Bug: https://github.com/pandas-dev/pandas/issues/66473
Forwarded: not-needed

Index: pandas/pandas/_libs/tslibs/strptime.pyx
===================================================================
--- pandas.orig/pandas/_libs/tslibs/strptime.pyx
+++ pandas/pandas/_libs/tslibs/strptime.pyx
@@ -167,6 +167,7 @@ cdef dict _parse_code_table = {"y": 0,
                                "Z": 17,
                                "p": 18,  # an additional key, only with I
                                "z": 19,
+                               "colon_z": 24,
                                "G": 20,
                                "V": 21,
                                "u": 22}
@@ -178,7 +179,7 @@ cdef _validate_fmt(str fmt):
             raise ValueError("Cannot use '%W' or '%U' without day and year")
         if "%A" not in fmt and "%a" not in fmt and "%w" not in fmt:
             raise ValueError("Cannot use '%W' or '%U' without day and year")
-    elif "%Z" in fmt and "%z" in fmt:
+    elif "%Z" in fmt and ("%z" in fmt or "%:z" in fmt):
         raise ValueError("Cannot parse both %Z and %z")
     elif "%j" in fmt and "%G" in fmt:
         raise ValueError("Day of the year directive '%j' is not "
@@ -618,9 +619,19 @@ cdef tzinfo _parse_with_format(
                 f"time data \"{val}\" doesn't match format \"{fmt}\""
             )
         if len(val) != found.end():
+            rest = val[found.end():]
+            # Specific check for '%:z' directive
+            if (
+                "colon_z" in found.re.groupindex
+                and found.group("colon_z") is not None
+                and rest[0] != ":"
+            ):
+                raise ValueError(
+                    f"Missing colon in %:z before '{rest}', got '{val}'"
+                )
             raise ValueError(
                 "unconverted data remains when parsing with "
-                f"format \"{fmt}\": \"{val[found.end():]}\""
+                f"format \"{fmt}\": \"{rest}\""
             )
 
     else:
@@ -760,9 +771,15 @@ cdef tzinfo _parse_with_format(
         elif parse_code == 17:
             # e.g. val='2011-12-30T00:00:00.000000UTC'; fmt='%Y-%m-%dT%H:%M:%S.%f%Z'
             tz = zoneinfo.ZoneInfo(found_dict["Z"])
-        elif parse_code == 19:
+        # elif group_key in ('z', 'colon_z'):
+        elif parse_code == 19 or parse_code == 24:
             # e.g. val='March 1, 2018 12:00:00+0400'; fmt='%B %d, %Y %H:%M:%S%z'
-            tz = parse_timezone_directive(found_dict["z"])
+            if found_dict[group_key] is None:
+                raise ValueError(
+                    f"time data \"{val}\" doesn't match format \"{fmt}\""
+                )
+
+            tz = parse_timezone_directive(found_dict[group_key])
         elif parse_code == 20:
             # e.g. val='2015-1-7'; fmt='%G-%V-%u'
             iso_year = int(found_dict["G"])
Index: pandas/pandas/compat/__init__.py
===================================================================
--- pandas.orig/pandas/compat/__init__.py
+++ pandas/pandas/compat/__init__.py
@@ -21,6 +21,7 @@ from pandas.compat._constants import (
     ISMUSL,
     PY312,
     PY314,
+    PY315,
     PYPY,
     WASM,
 )
@@ -161,6 +162,7 @@ __all__ = [
     "ISMUSL",
     "PY312",
     "PY314",
+    "PY315",
     "PYARROW_INSTALLED",
     "PYARROW_MIN_VERSION",
     "PYPY",
Index: pandas/pandas/compat/_constants.py
===================================================================
--- pandas.orig/pandas/compat/_constants.py
+++ pandas/pandas/compat/_constants.py
@@ -15,6 +15,7 @@ IS64 = sys.maxsize > 2**32
 
 PY312 = sys.version_info >= (3, 12)
 PY314 = sys.version_info >= (3, 14)
+PY315 = sys.version_info >= (3, 15)
 PYPY = platform.python_implementation() == "PyPy"
 WASM = (sys.platform == "emscripten") or (platform.machine() in ["wasm32", "wasm64"])
 ISMUSL = "musl" in (sysconfig.get_config_var("HOST_GNU_TYPE") or "")
@@ -30,6 +31,7 @@ __all__ = [
     "ISMUSL",
     "PY312",
     "PY314",
+    "PY315",
     "PYPY",
     "WASM",
 ]
Index: pandas/pandas/io/xml.py
===================================================================
--- pandas.orig/pandas/io/xml.py
+++ pandas/pandas/io/xml.py
@@ -337,44 +337,53 @@ class _XMLFrameParser:
             set(self.iterparse[row_node])
         )
 
-        for event, elem in iterparse(self.path_or_buffer, events=("start", "end")):
-            curr_elem = elem.tag.split("}")[1] if "}" in elem.tag else elem.tag
-
-            if event == "start":
-                if curr_elem == row_node:
-                    row = {}
-
-            if row is not None:
-                if self.names and iterparse_repeats:
-                    for col, nm in zip(
-                        self.iterparse[row_node], self.names, strict=True
-                    ):
-                        if curr_elem == col:
-                            elem_val = elem.text if elem.text else None
-                            if elem_val not in row.values() and nm not in row:
-                                row[nm] = elem_val
-
-                        if col in elem.attrib:
-                            if elem.attrib[col] not in row.values() and nm not in row:
-                                row[nm] = elem.attrib[col]
-                else:
-                    for col in self.iterparse[row_node]:
-                        if curr_elem == col:
-                            row[col] = elem.text if elem.text else None
-                        if col in elem.attrib:
-                            row[col] = elem.attrib[col]
-
-            if event == "end":
-                if curr_elem == row_node and row is not None:
-                    dicts.append(row)
-                    row = None
-
-                elem.clear()
-                if hasattr(elem, "getprevious"):
-                    while (
-                        elem.getprevious() is not None and elem.getparent() is not None
-                    ):
-                        del elem.getparent()[0]
+        parser = iterparse(self.path_or_buffer, events=("start", "end"))
+        try:
+            for event, elem in parser:
+                curr_elem = elem.tag.split("}")[1] if "}" in elem.tag else elem.tag
+
+                if event == "start":
+                    if curr_elem == row_node:
+                        row = {}
+
+                if row is not None:
+                    if self.names and iterparse_repeats:
+                        for col, nm in zip(
+                            self.iterparse[row_node], self.names, strict=True
+                        ):
+                            if curr_elem == col:
+                                elem_val = elem.text if elem.text else None
+                                if elem_val not in row.values() and nm not in row:
+                                    row[nm] = elem_val
+
+                            if col in elem.attrib:
+                                if (
+                                    elem.attrib[col] not in row.values()
+                                    and nm not in row
+                                ):
+                                    row[nm] = elem.attrib[col]
+                    else:
+                        for col in self.iterparse[row_node]:
+                            if curr_elem == col:
+                                row[col] = elem.text if elem.text else None
+                            if col in elem.attrib:
+                                row[col] = elem.attrib[col]
+
+                if event == "end":
+                    if curr_elem == row_node and row is not None:
+                        dicts.append(row)
+                        row = None
+
+                    elem.clear()
+                    if hasattr(elem, "getprevious"):
+                        while (
+                            elem.getprevious() is not None
+                            and elem.getparent() is not None
+                        ):
+                            del elem.getparent()[0]
+        finally:
+            if hasattr(parser, "close"):
+                parser.close()
 
         if dicts == []:
             raise ParserError("No result from selected items in iterparse.")
Index: pandas/pandas/tests/extension/decimal/test_decimal.py
===================================================================
--- pandas.orig/pandas/tests/extension/decimal/test_decimal.py
+++ pandas/pandas/tests/extension/decimal/test_decimal.py
@@ -151,7 +151,12 @@ class TestDecimalArray(base.ExtensionTes
         # GH#57723
         # EAs that don't have special logic for None will raise, unlike pandas'
         # which interpret None as the NA value for the dtype.
-        msg = "conversion from NoneType to Decimal is not supported"
+        msg = "|".join(
+            [
+                "Cannot convert None to Decimal",  # PY315 - maybe linux specific
+                "conversion from NoneType to Decimal is not supported",
+            ]
+        )
         with pytest.raises(TypeError, match=msg):
             super().test_fillna_with_none(data_missing)
 
Index: pandas/pandas/tests/frame/indexing/test_indexing.py
===================================================================
--- pandas.orig/pandas/tests/frame/indexing/test_indexing.py
+++ pandas/pandas/tests/frame/indexing/test_indexing.py
@@ -12,6 +12,7 @@ import pytest
 from pandas._libs import iNaT
 from pandas.errors import InvalidIndexError
 
+from pandas.compat import PY315
 from pandas.core.dtypes.common import is_integer
 
 import pandas as pd
@@ -30,7 +31,10 @@ from pandas import (
 import pandas._testing as tm
 
 # We pass through a TypeError raised by numpy
-_slice_msg = "slice indices must be integers or None or have an __index__ method"
+if PY315:
+    _slice_msg = "slice indices must be integers or have an __index__ method"
+else:
+    _slice_msg = "slice indices must be integers or None or have an __index__ method"
 
 
 class TestDataFrameIndexing:
Index: pandas/pandas/tests/indexes/period/test_partial_slicing.py
===================================================================
--- pandas.orig/pandas/tests/indexes/period/test_partial_slicing.py
+++ pandas/pandas/tests/indexes/period/test_partial_slicing.py
@@ -1,6 +1,8 @@
 import numpy as np
 import pytest
 
+from pandas.compat import PY315
+
 from pandas import (
     DataFrame,
     PeriodIndex,
@@ -53,7 +55,10 @@ class TestPeriodIndex:
         # GH#6716
         idx = make_range(start="2013/01/01", freq="D", periods=400)
 
-        msg = "slice indices must be integers or None or have an __index__ method"
+        if PY315:
+            msg = "slice indices must be integers or have an __index__ method"
+        else:
+            msg = "slice indices must be integers or None or have an __index__ method"
         # slices against index should raise IndexError
         values = [
             "2014",
@@ -82,7 +87,10 @@ class TestPeriodIndex:
     def test_range_slice_seconds(self, make_range):
         # GH#6716
         idx = make_range(start="2013/01/01 09:00:00", freq="s", periods=4000)
-        msg = "slice indices must be integers or None or have an __index__ method"
+        if PY315:
+            msg = "slice indices must be integers or have an __index__ method"
+        else:
+            msg = "slice indices must be integers or None or have an __index__ method"
 
         # slices against index should raise IndexError
         values = [
Index: pandas/pandas/tests/indexes/test_old_base.py
===================================================================
--- pandas.orig/pandas/tests/indexes/test_old_base.py
+++ pandas/pandas/tests/indexes/test_old_base.py
@@ -7,6 +7,7 @@ import numpy as np
 import pytest
 
 from pandas._libs.tslibs import Timestamp
+from pandas.compat import PY315
 from pandas.errors import Pandas4Warning
 
 from pandas.core.dtypes.common import (
@@ -452,6 +453,8 @@ class TestBase:
         if len(index) == 0:
             # 0 vs 0.5 in error message varies with numpy version
             msg = "index (0|0.5) is out of bounds for axis 0 with size 0"
+        elif PY315:
+            msg = "slice indices must be integers or have an __index__ method"
         else:
             msg = "slice indices must be integers or None or have an __index__ method"
 
Index: pandas/pandas/tests/indexing/test_floats.py
===================================================================
--- pandas.orig/pandas/tests/indexing/test_floats.py
+++ pandas/pandas/tests/indexing/test_floats.py
@@ -1,6 +1,8 @@
 import numpy as np
 import pytest
 
+from pandas.compat import PY315
+
 from pandas import (
     DataFrame,
     Index,
@@ -251,7 +253,12 @@ class TestFloatIndexers:
         # setitem
         if indexer_sli is tm.iloc:
             # otherwise we keep the same message as above
-            msg = "slice indices must be integers or None or have an __index__ method"
+            if PY315:
+                msg = "slice indices must be integers or have an __index__ method"
+            else:
+                msg = (
+                    "slice indices must be integers or None or have an __index__ method"
+                )
         with pytest.raises(TypeError, match=msg):
             indexer_sli(s)[idx] = 0
 
Index: pandas/pandas/tests/scalar/timestamp/test_constructors.py
===================================================================
--- pandas.orig/pandas/tests/scalar/timestamp/test_constructors.py
+++ pandas/pandas/tests/scalar/timestamp/test_constructors.py
@@ -18,7 +18,10 @@ import pytest
 
 import pandas.util._test_decorators as td
 from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
-from pandas.compat import PY314
+from pandas.compat import (
+    PY314,
+    PY315,
+)
 from pandas.errors import (
     OutOfBoundsDatetime,
     Pandas4Warning,
@@ -240,7 +243,13 @@ class TestTimestampConstructorPositional
 
     def test_constructor_keyword(self):
         # GH#10758
-        msg = "function missing required argument 'day'|Required argument 'day'"
+        msg = "|".join(
+            [
+                r"datetime\(\) missing required argument 'day'",  # PY315
+                "function missing required argument 'day'",
+                "Required argument 'day'",
+            ]
+        )
         with pytest.raises(TypeError, match=msg):
             Timestamp(year=2000, month=1)
 
@@ -299,7 +308,15 @@ class TestTimestampConstructorPositional
         # GH#31200
 
         # The exact error message of datetime() depends on its version
-        msg1 = r"function missing required argument '(year|month|day)' \(pos [123]\)"
+        if PY315:
+            msg1 = (
+                r"datetime\(\) missing required argument "
+                r"'(year|month|day)' \(pos [123]\)"
+            )
+        else:
+            msg1 = (
+                r"function missing required argument '(year|month|day)' \(pos [123]\)"
+            )
         msg2 = r"Required argument '(year|month|day)' \(pos [123]\) not found"
         msg = "|".join([msg1, msg2])
 
Index: pandas/pandas/tests/tools/test_to_datetime.py
===================================================================
--- pandas.orig/pandas/tests/tools/test_to_datetime.py
+++ pandas/pandas/tests/tools/test_to_datetime.py
@@ -23,6 +23,7 @@ from pandas._libs.tslibs import (
 )
 from pandas.compat import (
     PY314,
+    PY315,
     WASM,
 )
 from pandas.errors import (
@@ -517,6 +518,39 @@ class TestTimeConversionFormats:
         expected = DatetimeIndex(expected_dates)
         tm.assert_index_equal(result, expected)
 
+    @pytest.mark.xfail(
+        not PY315, reason="%:z directive not supported prior to 3.15", raises=ValueError
+    )
+    def test_to_datetime_colon_z_offset(self):
+        dates = [
+            "2010-01-01 12:00:00+04:00",
+            "2010-01-01 12:00:00+04:30",
+            "2010-01-01 12:00:00-05:00",
+        ]
+        expected_dates = [
+            "2010-01-01 08:00:00+00:00",
+            "2010-01-01 07:30:00+00:00",
+            "2010-01-01 17:00:00+00:00",
+        ]
+        fmt = "%Y-%m-%d %H:%M:%S%:z"
+
+        result = to_datetime(dates, format=fmt, utc=True)
+        expected = DatetimeIndex(expected_dates)
+        tm.assert_index_equal(result, expected)
+
+    def test_to_datetime_missing_colon_z_offset(self):
+        # test adapted from python/cpython#136961
+        dates = ["+04:0030"]
+        fmt = "%:z"
+
+        if PY315:
+            msg = r"Missing colon in %:z before '30', got '\+04:0030'"
+        else:
+            msg = "':' is a bad directive in format '%:z'"
+
+        with pytest.raises(ValueError, match=msg):
+            to_datetime(dates, format=fmt, utc=True)
+
     @pytest.mark.parametrize(
         "offset", ["+0", "-1foo", "UTCbar", ":10", "+01:000:01", ""]
     )
@@ -1390,9 +1424,12 @@ class TestToDatetime:
     @pytest.mark.parametrize("errors", ["coerce", "raise"])
     def test_invalid_format_raises(self, errors):
         # https://github.com/pandas-dev/pandas/issues/50255
-        with pytest.raises(
-            ValueError, match="':' is a bad directive in format 'H%:M%:S%"
-        ):
+        if PY315:
+            msg = r"':M' is a bad directive in format 'H%:M%:S%"
+        else:
+            msg = "':' is a bad directive in format 'H%:M%:S%"
+
+        with pytest.raises(ValueError, match=msg):
             to_datetime(["00:00:00"], format="H%:M%:S%", errors=errors)
 
     @pytest.mark.parametrize("value", ["a", "00:01:99"])

Attachment: signature.asc
Description: PGP signature


--- End Message ---
--- Begin Message ---
Source: pandas
Source-Version: 3.0.5+dfsg-1
Done: Rebecca N. Palmer <[email protected]>

We believe that the bug you reported is fixed in the latest version of
pandas, which is due to be installed in the Debian FTP archive.

A summary of the changes between this version and the previous one is
attached.

Thank you for reporting the bug, which will now be closed.  If you
have further comments please address them to [email protected],
and the maintainer will reopen the bug report if appropriate.

Debian distribution maintenance software
pp.
Rebecca N. Palmer <[email protected]> (supplier of updated pandas package)

(This message was generated automatically at their request; if you
believe that there is a problem with it please contact the archive
administrators by mailing [email protected])


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

Format: 1.8
Date: Sat, 12 Sep 2026 19:04:57 +0100
Source: pandas
Architecture: source
Version: 3.0.5+dfsg-1
Distribution: experimental
Urgency: medium
Maintainer: Debian Science Maintainers 
<[email protected]>
Changed-By: Rebecca N. Palmer <[email protected]>
Closes: 1144864
Changes:
 pandas (3.0.5+dfsg-1) experimental; urgency=medium
 .
   * Upstream bugfix release.  Update contributors, patches.
   * Be compatible with Python 3.15 (Closes: #1144864).
   * Tests: remove no longer needed xfails/skips,
     fix check of which ones are still needed,
     replace temporary xfails with fixes,
     skip a new instance of #790925 (HDF5 I/O crashing on armhf),
     accept pyarrow 25.
   * Docs: be more reproducible.
   * Simplify d/rules.
Checksums-Sha1:
 0d56a8623b0896f58a0bc578c80abf15b7eef1d7 5915 pandas_3.0.5+dfsg-1.dsc
 d12ea09cae217293370de3bd457e63140ecd2dd9 10286980 pandas_3.0.5+dfsg.orig.tar.xz
 7820dddd55bf01308a9c34f64f3246980b12da6a 92344 
pandas_3.0.5+dfsg-1.debian.tar.xz
 502f4b13c850fc405c513705ef218ad552aae25a 10123 
pandas_3.0.5+dfsg-1_source.buildinfo
Checksums-Sha256:
 0dbc1263f7804dd6da0c889f599e2146d74d5268cb930d0450fce5ef884bd158 5915 
pandas_3.0.5+dfsg-1.dsc
 e8b392073359fc7e3084a91a5be3d4465260647ebb5799a7aa63b5b649408508 10286980 
pandas_3.0.5+dfsg.orig.tar.xz
 2d639e03f29f4d9893787196cd60954594fa06e6bbc6887b6611ab885df72f55 92344 
pandas_3.0.5+dfsg-1.debian.tar.xz
 f53b233c30972b4cbe5894bb220d197320090b6159376036c93ff262c3240b4f 10123 
pandas_3.0.5+dfsg-1_source.buildinfo
Files:
 4b15a7dd9ef67e376c398608a161ac8d 5915 python optional pandas_3.0.5+dfsg-1.dsc
 746603b9b238242fe6277edfc101a925 10286980 python optional 
pandas_3.0.5+dfsg.orig.tar.xz
 186178c86be7c3b3d693e0bf5d3d4cfc 92344 python optional 
pandas_3.0.5+dfsg-1.debian.tar.xz
 61883e62a3da251a6dcd521161294f73 10123 python optional 
pandas_3.0.5+dfsg-1_source.buildinfo

-----BEGIN PGP SIGNATURE-----

iQJMBAEBCgA2FiEEZ8sxEAXE7b4yF1MI3uUNDVZ+omYFAmqoZgkYHHJlYmVjY2Ff
cGFsbWVyQHpvaG8uY29tAAoJEN7lDQ1WfqJmco0P/Raby1OLnHX2zvJxuXrErKdr
aQ50hmYSNqZptXe7pn9DHN0P54BC+SoV5sdsSh4go0Dudg6Sbiv14Nlc9iku2Tzl
/AryLT/+EsG0DDSJhMCj2K1ZCXWOErfW8Dry+IHHdhdeHySjviU58JLkHxpVgXYP
LCINZozHIdeCbWkT6CdGiNx125eEAGnWi6Qb0z5sLxY4ZSYM5eKmUjjVVk6B9nZS
V/fxXVaUBA1amT5PNPF/5eiCgU4RwVleWc+UnIUZQtbAin+mIrG/zzWI0pvZLZuC
IAh9Yee9mna0sHQB1AxhQzTxA4eUS5haK+noFa6J9qzcOU+AVKCwcRsVXi1gF1pU
vkp3M3fPv4loAph/c9DaR4AGU9cHbP0ImHVpdNN5GhbI+Nxy/ckx+SWRuvGOJXtu
QMyahX35sHd7qcVDYrLFhI8VPcqQc2pKphr6osThO63sfxForetT9SaFjbAvbsqu
McY04zDCHsdeRkvwNVD8f0hHkXbrcGBe8I19SSa2RL9ZMASSV1I8d11FIPwCPzx4
4TQ2j1memqQ4tCQhkmVlJWoJUU+zjcIiWylYeaOfYJe+OZTRtiPTf6Grvna9vyaZ
6bU41Z0Kz8UXrCnx0vPzfhPJ2FtX9Hypca6QcUKgk+lYS2gOQKZrDqVA4av04V/4
HBuY7Zd4Kme6IO5hRp/2
=EDqR
-----END PGP SIGNATURE-----

Attachment: pgp95jElQK5mw.pgp
Description: PGP signature


--- End Message ---

Reply via email to