Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

Trailing whitespace strip in <pre> is quadratic on a long whitespace run

Đang mở Phù hợp với người mới
#286 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

@ac1982 đang làm issue này rồi.

Từ ngày 11/10/2026.

  • #287 của @ac1982 — đang mở

Đá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
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
python
Lĩnh vực
tooling

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 which markdownify/__init__.py was 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). On 1.2.3 the 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 x then a run, many short runs split by x) 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

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. 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.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của matthewwithanm/python-markdownify

Tất cả issue của matthewwithanm/python-markdownify

Issue tương tự

Thêm issue về Python

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.