time/datetime.fromisoformat: C accelerator accepts a fractional seconds component with no decimal mark
Chưa có ai nhận issue này.
- Ngôn ngữ chính
- Python
- Star
- 77.2k
- Fork
- 35.9k
- Chỉ số merge pull request
- Chỉ số pull request đang chờ
Mô tả
Bug report
Bug description:
In the basic ISO 8601 time format, the C implementation of
time.fromisoformat() / datetime.fromisoformat() accepts digits following
HHMMSS and silently interprets them as a fractional seconds component, even
though no decimal mark (. or ,) is present. ISO 8601 requires a decimal
sign to introduce a fraction, and the pure-Python implementation correctly
rejects these strings.
>>> from datetime import time, datetime
>>> time.fromisoformat('12345678')
datetime.time(12, 34, 56, 780000) # expected ValueError
>>> time.fromisoformat('123456789')
datetime.time(12, 34, 56, 789000) # expected ValueError
>>> datetime.fromisoformat('2020-01-01T12345678')
datetime.datetime(2020, 1, 1, 12, 34, 56, 780000) # expected ValueError
The pure-Python implementation rejects all of the above:
>>> import _pydatetime
>>> _pydatetime.time.fromisoformat('12345678')
Traceback (most recent call last):
...
ValueError: Invalid isoformat string: '12345678'
The same parser is used for the UTC offset, so a malformed offset is accepted
too, with the extra digits silently discarded:
>>> time.fromisoformat('12:34:56+00000000')
datetime.time(12, 34, 56, tzinfo=datetime.timezone.utc) # expected ValueError
Two further details show the leniency is unintended rather than deliberate:
- The equivalent extended-format string is correctly rejected —
time.fromisoformat('12:34:5678')raisesValueError. - The behaviour is not even self-consistent across lengths.
'1234567'
(7 digits) raisesValueError, while'12345678'(8 digits) is accepted.
This matters because the failure is silent and produces a wrong value rather
than an error: a truncated or corrupted timestamp string parses successfully
into a time/datetime that differs from the input.
Cause
In parse_hh_mm_ss_ff() (Modules/_datetimemodule.c), the HH/MM/SS loop
breaks when it sees a decimal mark, but in the basic format it can also fall
out of the loop normally after SS with characters still unconsumed (the
!has_separator branch does --p). The code following the loop then parses
whatever remains as a fraction, without ever checking that a decimal mark
introduced it.
The pure-Python _parse_hh_mm_ss_ff() in Lib/_pydatetime.py performs that
check explicitly:
if pos < len_str:
if tstr[pos] not in '.,':
raise ValueError("Invalid microsecond separator")
Documentation
fromisoformat() is documented to accept "any valid ISO 8601 format" subject to
an explicit list of exceptions; a fraction without a decimal sign is not among
them.
This is the same class of C/pure-Python divergence as gh-152157 (empty fraction
before a timezone designator) and gh-152079.
Affected versions
Reproduced directly on 3.12.3 and on main (3.16.0a0). I did not have 3.13,
3.14 or 3.15 interpreters to hand, but comparing parse_hh_mm_ss_ff() across
the branches shows the cause is present in all of them: the
else if (!has_separator) { --p; } branch and the unguarded "Parse fractional
components" block that follows the loop are unchanged on every active branch.
The 3.14, 3.15 and main copies of the function are identical; the 3.12 and
3.13 copies differ only in other validation paths (decimal mark on
hour/minute, empty fraction, microsecond separator).
CPython versions tested on:
3.12, 3.16, CPython main branch
Operating systems tested on:
Linux
Linked PRs
- gh-155177
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Hướng nghiên cứu
Bắt đầu với parse_hh_mm_ss_ff() trong Modules/_datetimemodule.c và so sánh việc kiểm tra hợp lệ của nó với _parse_hh_mm_ss_ff() trong Lib/_pydatetime.py. Tái hiện các ví dụ về định dạng cơ bản và offset không đúng định dạng trong báo cáo, sau đó thêm kiểm thử hồi quy để các đầu vào không có dấu thập phân bị triển khai C từ chối một cách nhất quán.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Đánh giá
- Công nghệ
- c, python
- Lĩnh vực
- backend
- Loại issue
- Lỗi
- Độ khó
- 2/5
- Thời gian dự kiến
- 1-3 giờ
- Mức độ hoạt động
- Ít trao đổi
- Độ rõ ràng
- Đặc tả rõ ràng
- Mức phù hợp với người mới
- 25/100