Trailing whitespace strip in <pre> is quadratic on a long whitespace run
Đánh giá
- Độ khó
- 2/5
- Thời gian dự kiến
- 1-3 giờ
- Mức phù hợp với người mới
- 78/100
Hướng nghiên cứu
Bước chậm là re_pre_rstrip.sub trong strip_pre ở markdownify/init.py, khoảng dòng 78; issue đề xuất thay thế nó bằng một lệnh gọi str.rstrip. Hãy đọc định nghĩa của re_pre_lstrip và re_pre_rstrip, sau đó tái hiện bằng một khối <pre> 64 KB chứa khoảng trắng cuối dòng, truyền qua markdownify(). Hoàn thành khi trường hợp này chạy nhanh hơn một giây nhiều, bộ 83 test hiện có vẫn pass, và một test thời gian mới bao phủ các cách viết <pre> được liệt kê trong issue.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
strip_pre() takes quadratic time on a <pre> block whose trailing whitespace run is a run of spaces not ended by a newline. Measured through markdownify() on tag 1.2.3, both trees in one isolated run:
| HTML | before | after |
|---|---|---|
| 16,000 B | 0.6881 s | 0.0003 s |
| 32,000 B | 2.6570 s | 0.0004 s |
| 65,536 B | 9.1117 s | 0.0007 s |
| 131,072 B | 35.5100 s | 0.0014 s |
| 16,000 B of ordinary words | 0.0011 s | 0.0009 s |
Each doubling of the block quadruples the time, so the cost belongs to the shape rather than the size - the last row is a document of the same size that converts in a millisecond. The input is an HTML document, so nothing bounds it, and this is the default path: strip_pre defaults to STRIP, while STRIP_ONE uses the separate re_pre_rstrip1 = r'\n *$' and is unaffected.
Cause. re_pre_rstrip = re.compile(r'[ \n]*$') applied with .sub() at __init__.py:78. .sub restarts at every position in the string, and at each one the unbounded [ \n]* run expands and then backtracks looking for the $ that the following character denies.
It is worth saying why the costly shape is spaces and not newlines, because it is not obvious from the pattern alone: re_pre_lstrip = r'^[ \n]*\n' runs on the line before, so a leading newline run is consumed there and re_pre_rstrip is handed a one-character string. With no newline in the block, lstrip does not fire and rstrip receives the whole thing. On the same 65,536-byte input a newline run costs 0.0001 s and a space run 9.3704 s.
Fix. The docstring already describes a strip, so use one:
- text = re_pre_rstrip.sub('', text)
+ text = text.rstrip(' \n')
Verification.
- Test suite on both trees at tag
1.2.3: 83 passed, unchanged, with each run asserting whichmarkdownify/__init__.pywas loaded and whether it still contains the regex call. - Differential: 0 differences from current behaviour across all 29,524 strings of length 0 to 9 over
{space, newline, x}- the whole alphabet the two patterns act on. - A new test converts a 64 KB
<pre>block in under 0.5 s and checks seven<pre>spellings (blank lines, leading and trailing spaces, indentation, empty, whitespace-only). On1.2.3the timing test fails and the behaviour test passes. - Shapes aimed at the fix rather than the original (pure space run with no terminator, a leading
xthen a run, many short runs split byx) are all flat: worst 0.0003 s at 65,536 B.
re_pre_rstrip is now unused. I have deliberately left the definition in place to keep the diff to one line, since it is a module-level name that something downstream may import; say the word and I will remove it.
I am happy to open a pull request with the patch if that is easier.
Found with AI assistance (Claude) and verified by hand.
Patch: full patch
--- a/markdownify/__init__.py
+++ b/markdownify/__init__.py
@@ -75,7 +75,7 @@
def strip_pre(text):
"""Strip all leading and trailing newlines from a <pre> string."""
text = re_pre_lstrip.sub('', text)
- text = re_pre_rstrip.sub('', text)
+ text = text.rstrip(' \n')
return text
- Ngôn ngữ chính
- Python
- Star
- 2.3k
- Fork
- 213
- Chỉ số merge pull request
- Không có pull request nào được merge trong 30 ngày
Chuẩn bị môi trường
Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.
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.
Issue khác của matthewwithanm/python-markdownify
-
RecursionError on deeply nested HTML (about 330 levels), separate from the cyclic-tree case in #256Có thể đã có người làm @HardMax71 đã nhận 46 ngày trước. Đang mở
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 68/100
-
Image/link attributes containing `]`, `)`, or spaces produce broken Markdown outputCó thể đã có người làm @assinscreedFC đã nhận 136 ngày trước. Đang mở
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 52/100
matthewwithanm/python-markdownify#261 · 1 bình luận · 1 reaction ·
-
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 65/100
matthewwithanm/python-markdownify#259 · 2 reaction ·
-
Recursion Error: Process_element / process_tag mutual recursion (infinite loop)Có thể đã có người làm @chiliec đã nhận 53 ngày trước. Đang mở
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 50/100
-
Behavior with strip_pre=mdfy.STRIP_ONE seems incorrect with trailing newlines within the preCó thể đã có người làm @oiahoon đã nhận 99 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 50/100
Tất cả issue của matthewwithanm/python-markdownify
Issue tương tự
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 79/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
mohitmishra786/readprism#322 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
good first issue
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 75/100
Maintainer thường phản hồi trong vòng 1 ngày
-
API-key credentials accept a blank key: empty via the API, spaces-only via the builder's Add API key dialogCó thể đã có người làm Có pull request liên kết đang mở hoặc đã được merge. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 77/100
Significant-Gravitas/AutoGPT#15727 ·
Maintainer thường phản hồi trong vòng 1 ngày