Skip to content

Commit

Permalink
Fix timedelta parsing breaking change (#369)
Browse files Browse the repository at this point in the history
* Fix timedelta parsing breaking change

* Account for empty string with whitespace

* Add integer as string default value test case
  • Loading branch information
ddelange authored Nov 20, 2024
1 parent 9d1ff82 commit deb0315
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 12 deletions.
23 changes: 12 additions & 11 deletions src/environs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,17 +378,18 @@ class TimeDeltaField(ma.fields.TimeDelta):
def _deserialize(self, value, *args, **kwargs) -> timedelta:
if isinstance(value, timedelta):
return value
match = _TIMEDELTA_PATTERN.match(value)
if match is not None and match.group(0): # disallow "", allow "0s"
return timedelta(
weeks=int(match.group(1) or 0),
days=int(match.group(2) or 0),
hours=int(match.group(3) or 0),
minutes=int(match.group(4) or 0),
seconds=int(match.group(5) or 0),
milliseconds=int(match.group(6) or 0),
microseconds=int(match.group(7) or 0),
)
if isinstance(value, str):
match = _TIMEDELTA_PATTERN.match(value)
if match is not None and any(groups := match.groups(default=0)):
return timedelta(
weeks=int(groups[0]),
days=int(groups[1]),
hours=int(groups[2]),
minutes=int(groups[3]),
seconds=int(groups[4]),
milliseconds=int(groups[5]),
microseconds=int(groups[6]),
)
return super()._deserialize(value, *args, **kwargs)


Expand Down
13 changes: 12 additions & 1 deletion tests/test_environs.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,13 @@ def test_date_cast(self, set_env, env):
assert env.date("DATE") == date

def test_timedelta_cast(self, set_env, env):
# default values
assert env.timedelta("TIMEDELTA", "42") == dt.timedelta(seconds=42)
assert env.timedelta("TIMEDELTA", 42) == dt.timedelta(seconds=42)
assert env.timedelta("TIMEDELTA", 42.9) == dt.timedelta(seconds=42) # bug?
assert env.timedelta("TIMEDELTA", dt.timedelta(seconds=42)) == dt.timedelta(
seconds=42,
)
# seconds as integer
set_env({"TIMEDELTA": "0"})
assert env.timedelta("TIMEDELTA") == dt.timedelta()
Expand All @@ -244,7 +251,7 @@ def test_timedelta_cast(self, set_env, env):
set_env({"TIMEDELTA": "-42s"})
assert env.timedelta("TIMEDELTA") == dt.timedelta(seconds=-42)
# whitespaces, units subselection (but descending ordering)
set_env({"TIMEDELTA": " 42 d -42s "})
set_env({"TIMEDELTA": " 42 d \t -42s "})
assert env.timedelta("TIMEDELTA") == dt.timedelta(days=42, seconds=-42)
# unicode µs (in addition to us below)
set_env({"TIMEDELTA": "42µs"})
Expand All @@ -262,6 +269,10 @@ def test_timedelta_cast(self, set_env, env):
)
# empty string not allowed
set_env({"TIMEDELTA": ""})
with pytest.raises(environs.EnvError):
env.timedelta("TIMEDELTA")
# empty string with whitespace not allowed
set_env({"TIMEDELTA": " "})
with pytest.raises(environs.EnvError):
env.timedelta("TIMEDELTA")
# float not allowed
Expand Down

0 comments on commit deb0315

Please sign in to comment.