-
Notifications
You must be signed in to change notification settings - Fork 24
Remove pendulum dependency #450
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
9139758
Remove pendulum dependency
svrooij 41d8efd
Feedback processed and removed python-dateutil
svrooij f2ad78e
Merge branch 'main' into remove-pendulum-dependency
svrooij fc6c719
Some linting
svrooij 431b036
parse_timedelta acts exacty the same as previous library
svrooij 3812082
Merge branch 'main' into remove-pendulum-dependency
baywet 74fbeeb
Fix issues caused by auto format
svrooij e6eb346
More pendulem removed
svrooij e61eabb
Reduce complexity and raise error instead of return none
svrooij 5a2205a
ci: disables fail fast to get all feedback at once
baywet d7af3e3
chore: minor formatting fixes
baywet 4540b25
chore: additional formatting issue
baywet 58a8121
chore: additional formatting issues
baywet f9d9e84
fix: replace calls to parser by iso format
baywet f4a5692
chore: additional formatting
baywet fedf34c
fix: multiple text failing tests
baywet 42f1b5b
chore: additional fixture data corrections
baywet e9021c9
fix: additional fixes for unit test setup
baywet f0ed780
Support additional timedeltas
svrooij 293ea59
Make P mandatory
svrooij 3bea953
chore: fixes formatting
baywet 8aaeff1
fromisoformat compatibility
svrooij 190f9dd
chore: linting
baywet 15b5a31
Hopefully try 50 fixes it 💣
svrooij 1373232
More 🕑 compatibility
svrooij f1b515f
Fixing datetime parsing in test 🧪
svrooij File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| from sys import version_info as sys_version_info | ||
| import re | ||
| from datetime import datetime, time, timedelta | ||
|
|
||
| _ISO8601_DURATION_PATTERN = re.compile( | ||
| "^P" # Duration P indicator | ||
| # Weeks | ||
| "(?P<w>" | ||
| r" (?P<weeks>\d+(?:[.,]\d+)?W)" | ||
| ")?" | ||
| # Years, Months, Days | ||
| "(?P<ymd>" | ||
| r" (?P<years>\d+(?:[.,]\d+)?Y)?" | ||
| r" (?P<months>\d+(?:[.,]\d+)?M)?" | ||
| r" (?P<days>\d+(?:[.,]\d+)?D)?" | ||
| ")?" | ||
| # Time | ||
| "(?P<hms>" | ||
| " (?P<timesep>T)" # Separator (T) | ||
| r" (?P<hours>\d+(?:[.,]\d+)?H)?" | ||
| r" (?P<minutes>\d+(?:[.,]\d+)?M)?" | ||
| r" (?P<seconds>\d+(?:[.,]\d+)?S)?" | ||
| ")?" | ||
| "$", | ||
| re.VERBOSE, | ||
| ) | ||
|
|
||
|
|
||
| def parse_timedelta_from_iso_format(text: str) -> timedelta: | ||
| """Parses a ISO8601 duration string into a timedelta object.""" | ||
|
|
||
| m = _ISO8601_DURATION_PATTERN.match(text) | ||
| if not m: | ||
| raise ValueError(f"Invalid ISO8601 duration string: {text}") | ||
|
|
||
| weeks = float(m.group("weeks").replace(",", ".").replace("W", "")) if m.group("weeks") else 0 | ||
| years = float(m.group("years").replace(",", ".").replace("Y", "")) if m.group("years") else 0 | ||
| months = float(m.group("months").replace(",", ".").replace("M", "")) if m.group("months") else 0 | ||
| days = float(m.group("days").replace(",", ".").replace("D", "")) if m.group("days") else 0 | ||
| hours = float(m.group("hours").replace(",", ".").replace("H", "")) if m.group("hours") else 0 | ||
| minutes = float(m.group("minutes").replace(",", ".").replace("M", "") | ||
| ) if m.group("minutes") else 0 | ||
| seconds = float(m.group("seconds").replace(",", ".").replace("S", "") | ||
| ) if m.group("seconds") else 0 | ||
| _have_date = years or months or days | ||
| _have_time = hours or minutes or seconds | ||
| if weeks and (_have_date or _have_time): | ||
| raise ValueError("Combining weeks with other date/time parts is not supported") | ||
|
|
||
| _total_days = (years * 365) + (months * 30) + days | ||
| return timedelta( | ||
| days=_total_days, | ||
| hours=hours, | ||
| minutes=minutes, | ||
| seconds=seconds, | ||
| weeks=weeks, | ||
| ) | ||
|
|
||
|
|
||
| _TIMEDELTA_PATTERN = re.compile(r"^(?P<hours>\d+):(?P<minutes>\d+)(?::(?P<seconds>\d+))?$") | ||
|
|
||
|
|
||
| def parse_timedelta_string(text: str) -> timedelta: | ||
| """Checks if a given string is a valid ISO8601 duration string. Or hh:mm:ss format.""" | ||
| try: | ||
| return parse_timedelta_from_iso_format(text) | ||
| except ValueError as exc: | ||
| # The previous library also supported hh:mm:ss format | ||
| m = _TIMEDELTA_PATTERN.match(text) | ||
| if not m: | ||
| raise ValueError(f"Invalid timedelta string: {text}") from exc | ||
|
|
||
| hours = int(m.group("hours")) | ||
| minutes = int(m.group("minutes")) | ||
| seconds = int(m.group("seconds") or 0) | ||
| return timedelta(hours=hours, minutes=minutes, seconds=seconds) | ||
|
|
||
|
|
||
| _TIME_REPLACEMENT_PATTERN = re.compile(r'(\d)([.,])(\d+)') | ||
|
|
||
|
|
||
| def datetime_from_iso_format_compat(text: str) -> datetime: | ||
| """Parses a ISO8601 formatted string into a datetime object.""" | ||
| try: | ||
| # Try regular first (faster for most cases) | ||
| return datetime.fromisoformat(text) | ||
| except ValueError as exc: | ||
| # Python 3.10 and below only support fractions of seconds in either 3 or 6 digits | ||
| # Python 3.11+ supports any number of digits | ||
|
|
||
| if sys_version_info[:3] < (3, 11): | ||
| # The following code is a workaround for Python 3.10 and below | ||
| fixed_time = re.sub( | ||
| _TIME_REPLACEMENT_PATTERN, | ||
| lambda x: x.group(1) + "." + x.group(3).ljust(6, '0')[:6], text | ||
| ).replace("Z", "+00:00") | ||
| return datetime.fromisoformat(fixed_time) | ||
|
|
||
| raise exc | ||
|
|
||
|
|
||
| def time_from_iso_format_compat(text: str) -> time: | ||
| """Parses a ISO8601 formatted string into a time object.""" | ||
| try: | ||
| # Try regular first (faster for most cases) | ||
| return time.fromisoformat(text) | ||
| except ValueError as exc: | ||
| # Python 3.10 and below only support fractions of seconds in either 3 or 6 digits | ||
| # Python 3.11+ supports any number of digits | ||
|
|
||
| if sys_version_info[:3] < (3, 11): | ||
| # The following code is a workaround for Python 3.10 and below | ||
| fixed_time = re.sub( | ||
| _TIME_REPLACEMENT_PATTERN, | ||
| lambda x: x.group(1) + "." + x.group(3).ljust(6, '0')[:6], text | ||
| ).replace("Z", "+00:00") | ||
| return time.fromisoformat(fixed_time) | ||
|
|
||
| raise exc | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| import pytest | ||
|
|
||
| from kiota_abstractions.date_utils import ( | ||
| parse_timedelta_from_iso_format, | ||
| parse_timedelta_string, | ||
| time_from_iso_format_compat, | ||
| datetime_from_iso_format_compat | ||
| ) | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("text", ["08:00:00", "08:00:00.0", "08:00:00.00","08:00:00.000", | ||
| "08:00:00.0000","08:00:00.00000","08:00:00.000000", "08:00:00.0000000", "08:00:00,0000000", | ||
| "08:00:00,0000000Z", "08:00:00.00Z", "08:00:00.00+00:00" ]) | ||
| def test_time_from_iso_format_compat(text: str): | ||
| result = time_from_iso_format_compat(text) | ||
| assert result.hour == 8 | ||
| assert result.minute == 0 | ||
| assert result.second == 0 | ||
|
|
||
| @pytest.mark.parametrize("text", ["1986-07-28T08:00:00", "1986-07-28T08:00:00.0", "1986-07-28T08:00:00.00", | ||
| "1986-07-28T08:00:00.000", "1986-07-28T08:00:00.0000", "1986-07-28T08:00:00.00000", | ||
| "1986-07-28T08:00:00.000000", "1986-07-28T08:00:00.0000000", "1986-07-28T08:00:00,0000000", | ||
| "1986-07-28T08:00:00.0000000Z", "1986-07-28T08:00:00.00Z", "1986-07-28T08:00:00.00+00:00" ]) | ||
| def test_datetime_from_iso_format_compat(text: str): | ||
| result = datetime_from_iso_format_compat(text) | ||
| assert result.hour == 8 | ||
| assert result.minute == 0 | ||
| assert result.second == 0 | ||
|
|
||
|
|
||
| def test_parse_timedelta_from_iso_format_weeks(): | ||
| result = parse_timedelta_from_iso_format("P3W") | ||
| assert result.days == 21 | ||
|
|
||
|
|
||
| def test_parse_timedelta_from_iso_format_days(): | ||
| result = parse_timedelta_from_iso_format("P3D") | ||
| assert result.days == 3 | ||
|
|
||
|
|
||
| def test_parse_timedelta_from_iso_format_hours(): | ||
| result = parse_timedelta_from_iso_format("PT3H") | ||
| assert result.seconds == 10800 | ||
|
|
||
|
|
||
| def test_parse_timedelta_from_iso_format_minutes(): | ||
| result = parse_timedelta_from_iso_format("PT3M") | ||
| assert result.seconds == 180 | ||
|
|
||
|
|
||
| def test_parse_timedelta_from_iso_format_seconds(): | ||
| result = parse_timedelta_from_iso_format("PT3S") | ||
| assert result.seconds == 3 | ||
|
|
||
|
|
||
| def test_parse_timedelta_from_iso_format_years(): | ||
| result = parse_timedelta_from_iso_format("P3Y") | ||
| assert result.days == 1095 | ||
|
|
||
|
|
||
| def test_parse_timedelta_from_iso_format_months(): | ||
| result = parse_timedelta_from_iso_format("P3M") | ||
| assert result.days == 90 | ||
|
|
||
|
|
||
| def test_parse_timedelta_from_iso_format_days_and_time(): | ||
| result = parse_timedelta_from_iso_format("P3DT3H3M3S") | ||
| assert result.days == 3 | ||
| assert result.seconds == 10983 | ||
|
|
||
| def test_parse_timedelta_from_iso_format_time_without_p(): | ||
| with pytest.raises(ValueError): | ||
| parse_timedelta_from_iso_format("T3H3M3S") | ||
|
|
||
| @pytest.mark.parametrize("text", ["P3W3Y", "P3W3Y3D", "P3W3Y3DT3H3M3S"]) | ||
| def test_parse_timedelta_from_iso_format_must_raise(text: str): | ||
| # assert this raises a ValueError | ||
| with pytest.raises(ValueError): | ||
| parse_timedelta_from_iso_format(text) | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("text, expected_hours", [("PT3H", 3), ("2:00:00", 2)]) | ||
| def test_parse_timedelta_string_valid(text:str, expected_hours:int): | ||
| result = parse_timedelta_string(text) | ||
| assert result.days == 0 | ||
| assert result.seconds == expected_hours * 3600 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.