From 39cdd6a6769fdc965aa8e7c5c8576e68c14f70b6 Mon Sep 17 00:00:00 2001 From: Alan Date: Thu, 1 Oct 2026 08:09:37 +0800 Subject: [PATCH] Reject ISO durations with missing components --- src/environs/fields.py | 4 ++-- tests/test_environs.py | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/environs/fields.py b/src/environs/fields.py index 3d36af6..bba9146 100644 --- a/src/environs/fields.py +++ b/src/environs/fields.py @@ -74,10 +74,10 @@ class TimeDelta(fields.TimeDelta): _ISO_8601_REGEX = re.compile( r"^(?:\s*)" # optional leading whitespace r"(?P[+-]?)" # optional sign - r"P" # designator + r"P(?=\d|T\d)" # designator followed by at least one component r"(?:(?P\d+(?:\.\d+)?)W|" # weeks variant r"(?:(?P\d+(?:\.\d+)?)D)?" # days - r"(?:T" # time designator + r"(?:T(?=\d)" # time designator must be followed by a component r"(?:(?P\d+(?:\.\d+)?)H)?" # hours r"(?:(?P\d+(?:\.\d+)?)M)?" # minutes r"(?:(?P\d+(?:\.\d+)?)S)?" # seconds (can be fractional) diff --git a/tests/test_environs.py b/tests/test_environs.py index 7baf13c..7cfef05 100644 --- a/tests/test_environs.py +++ b/tests/test_environs.py @@ -355,6 +355,23 @@ def test_timedelta_cast(self, set_env, env: environs.Env): set_env({"TIMEDELTA": "PT0S"}) assert env.timedelta("TIMEDELTA", format="iso8601") == dt.timedelta() + @pytest.mark.parametrize( + "value", ["P", "PT", "+P", "-PT", " P ", " PT ", "P1DT", "P0DT", "-P1DT"] + ) + def test_timedelta_iso8601_requires_components( + self, value, set_env, env: environs.Env + ): + set_env({"TIMEDELTA": value}) + with pytest.raises( + environs.EnvValidationError, match="Not a valid period of time" + ): + env.timedelta("TIMEDELTA", format="iso8601") + + @pytest.mark.parametrize("value", ["P0W", "P0D", "PT0H", "PT0M", "PT0S", "P0DT0S"]) + def test_timedelta_iso8601_zero_components(self, value, set_env, env: environs.Env): + set_env({"TIMEDELTA": value}) + assert env.timedelta("TIMEDELTA", format="iso8601") == dt.timedelta() + def test_time_cast(self, set_env, env: environs.Env): set_env({"TIME": "10:30"}) assert env.time("TIME") == dt.time(hour=10, minute=30, second=0)