File: pure-python-empty-durations-error.patch

package info (click to toggle)
pendulum 3.1.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,776 kB
  • sloc: python: 18,420; makefile: 41
file content (80 lines) | stat: -rw-r--r-- 3,153 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
From: Colin Watson <cjwatson@debian.org>
Date: Sun, 15 Jun 2025 10:52:15 +0100
Subject: Make empty durations an error in pure-Python parser

Some of Debian's test runners noticed that the pydantic-extra-types
tests are failing on 32-bit architectures:

  ______________________ test_invalid_zero_duration_string _______________________

      def test_invalid_zero_duration_string():
          """'P' is not a valid ISO 8601 duration and should raise a validation error."""
  >       with pytest.raises(ValidationError):
  E       Failed: DID NOT RAISE <class 'pydantic_core._pydantic_core.ValidationError'>

  tests/test_pendulum_dt.py:447: Failed

Debian currently has pendulum 3.0.0, which disabled the Rust extensions
if `struct.calcsize("P") == 4`, and the Rust and Python parsers disagree
about how to handle an empty duration: the Rust parser reports an error,
while the Python parser returns `Duration()`.  3.1.0 removes that
particular limitation on using Rust extensions on 32-bit architectures,
but the parser discrepancy still seems to be present.

I don't have access to the full text of the standard, but Wikipedia's
summary says 'However, at least one element must be present, thus "P" is
not a valid representation for a duration of 0 seconds', so I think the
Rust parser is correct.  Adjust the Python parser to match.

Origin: other, https://github.com/python-pendulum/pendulum/pull/903
Last-Update: 2025-06-16
---
 src/pendulum/parsing/iso8601.py     |  2 +-
 tests/parsing/test_parse_iso8601.py | 10 ++++++++--
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/src/pendulum/parsing/iso8601.py b/src/pendulum/parsing/iso8601.py
index 101eb3b..e04fa51 100644
--- a/src/pendulum/parsing/iso8601.py
+++ b/src/pendulum/parsing/iso8601.py
@@ -265,7 +265,7 @@ def parse_iso8601(
 
 def _parse_iso8601_duration(text: str, **options: str) -> Duration | None:
     m = ISO8601_DURATION.match(text)
-    if not m:
+    if not m or (not m.group("w") and not m.group("ymd") and not m.group("hms")):
         return None
 
     years = 0
diff --git a/tests/parsing/test_parse_iso8601.py b/tests/parsing/test_parse_iso8601.py
index c15b9bd..ed2d398 100644
--- a/tests/parsing/test_parse_iso8601.py
+++ b/tests/parsing/test_parse_iso8601.py
@@ -90,7 +90,7 @@ def test_parse_iso8601(text: str, expected: date) -> None:
     assert parse_iso8601(text) == expected
 
 
-def test_parse_ios8601_invalid():
+def test_parse_iso8601_invalid():
     # Invalid month
     with pytest.raises(ValueError):
         parse_iso8601("20161306T123456")
@@ -193,7 +193,7 @@ def test_parse_ios8601_invalid():
         ("P2Y30M4DT5H6M7S", (2, 30, 0, 4, 5, 6, 7, 0)),
     ],
 )
-def test_parse_ios8601_duration(
+def test_parse_iso8601_duration(
     text: str, expected: tuple[int, int, int, int, int, int, int, int]
 ) -> None:
     parsed = parse_iso8601(text)
@@ -208,3 +208,9 @@ def test_parse_ios8601_duration(
         parsed.remaining_seconds,
         parsed.microseconds,
     ) == expected
+
+
+def test_parse_iso8601_duration_invalid():
+    # Must include at least one element
+    with pytest.raises(ValueError):
+        parse_iso8601("P")