Skip to content

Commit 7754af3

Browse files
committed
corrected dedent to pass test cases
1 parent 8cf2153 commit 7754af3

File tree

1 file changed

+42
-111
lines changed

1 file changed

+42
-111
lines changed

Lib/textwrap.py

Lines changed: 42 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -413,129 +413,60 @@ def shorten(text, width, **kwargs):
413413

414414
# -- Loosely related functionality -------------------------------------
415415

416-
def dedent(text, only_whitespace = True):
417-
"""Remove any common leading whitespace from every line in `text`.
418-
419-
This can be used to make triple-quoted strings line up with the left
420-
edge of the display, while still presenting them in the source code
421-
in indented form.
422-
423-
Note that tabs and spaces are both treated as whitespace, but they
424-
are not equal: the lines " hello" and "\\thello" are
425-
considered to have no common leading whitespace.
426-
427-
If `only_whitespace` is `True`, the leading whitespaces are removed from the text. Otherwise, all the common leading text is removed.
416+
def dedent(text):
417+
"""
418+
Remove any common leading whitespace from every line in text.
428419
429420
Entirely blank lines are normalized to a newline character.
421+
Tabs and spaces are treated as distinct.
422+
423+
This implementation uses os.path.commonprefix (implemented in C)
424+
to compute the common margin of non-blank lines for maximal performance.
430425
"""
431-
# Early return for empty input
426+
# Fast paths for empty or simple text
432427
if not text:
433428
return text
434429

435-
# Split into lines
436-
lines = text.splitlines(True)
430+
if "\n" not in text:
431+
return text # Single line has no dedent
437432

438-
# Fast path for single line - but make sure we still dedent!
439-
if len(lines) == 1:
440-
line = lines[0]
441-
stripped = line.strip()
442-
if not stripped: # Blank line
443-
return "\n" if line.endswith("\n") else ""
433+
# Split text into lines, preserving line endings
434+
lines: List[str] = text.splitlines(keepends=True)
435+
436+
# Process in a single pass to find:
437+
# 1. Leading whitespace of non-blank lines
438+
# 2. Whether a line has zero leading whitespace (optimization)
439+
non_blank_whites = []
440+
has_zero_margin = False
444441

445-
# Find leading whitespace for a single line
446-
if only_whitespace:
447-
i = 0
448-
while i < len(line) and line[i] in " \t":
449-
i += 1
450-
if i > 0: # Has leading whitespace to remove
451-
return line[i:]
452-
else:
453-
lead_size = len(line) - len(line.lstrip())
454-
if lead_size > 0: # Has leading whitespace to remove
455-
return line[lead_size:]
456-
return line # No whitespace to remove
457-
458-
# Cache method lookups for faster access
459-
_strip = str.strip
460-
_startswith = str.startswith
461-
_endswith = str.endswith
462-
463-
# Find first two non-blank lines
464-
non_blank = []
465442
for line in lines:
466-
if _strip(line):
467-
non_blank.append(line)
468-
if len(non_blank) == 2:
469-
break
470-
471-
# All lines are blank
472-
if not non_blank:
473-
result = []
474-
append = result.append
475-
for line in lines:
476-
append("\n" if _endswith(line, "\n") else "")
477-
return "".join(result)
478-
479-
# Calculate margin length efficiently
480-
if len(non_blank) == 1:
481-
# Single non-blank line
482-
line = non_blank[0]
483-
if only_whitespace:
484-
# Manually find leading whitespace (faster than regex)
485-
i = 0
486-
line_len = len(line)
487-
while i < line_len and line[i] in " \t":
488-
i += 1
489-
margin_len = i
490-
else:
491-
# Use built-in lstrip for non-whitespace case
492-
margin_len = len(line) - len(line.lstrip())
443+
stripped = line.strip()
444+
if stripped: # Non-blank line
445+
leading = line[:len(line) - len(line.lstrip())]
446+
non_blank_whites.append(leading)
447+
# Early detection of zero margin case
448+
if not leading:
449+
has_zero_margin = True
450+
break # No need to check more lines
451+
452+
# If all lines are blank, normalize them
453+
if not non_blank_whites:
454+
# Preallocate result list
455+
return "".join(["\n" if line.endswith("\n") else "" for line in lines])
456+
457+
# Skip commonprefix calculation if we already know there's no margin
458+
if has_zero_margin:
459+
margin_len = 0
493460
else:
494-
# Find common prefix of first two non-blank lines
495-
a, b = non_blank
496-
min_len = min(len(a), len(b))
497-
i = 0
498-
499-
if only_whitespace:
500-
# Manual loop is faster than character-by-character comparison
501-
while i < min_len and a[i] == b[i] and a[i] in " \t":
502-
i += 1
503-
else:
504-
while i < min_len and a[i] == b[i]:
505-
i += 1
506-
507-
margin_len = i
461+
common = os.path.commonprefix(non_blank_whites)
462+
margin_len = len(common)
508463

509-
# No margin to remove - return original with blank line normalization
464+
# No common margin case - just normalize blank lines
510465
if margin_len == 0:
511-
result = []
512-
append = result.append
513-
for line in lines:
514-
if _strip(line): # Non-blank line
515-
append(line)
516-
else: # Blank line
517-
append("\n" if _endswith(line, "\n") else "")
518-
return "".join(result)
519-
520-
# Get margin string once for repeated comparison
521-
margin = non_blank[0][:margin_len]
522-
523-
# Pre-allocate result list with a size hint for better memory efficiency
524-
result = []
525-
append = result.append
526-
527-
# Process all lines with optimized operations
528-
for line in lines:
529-
if not _strip(line): # Blank line (including whitespace-only lines)
530-
append("\n" if _endswith(line, "\n") else "")
531-
elif _startswith(line, margin): # Has margin
532-
# Slice operation is very fast in Python
533-
append(line[margin_len:])
534-
else: # No matching margin
535-
append(line)
536-
537-
# Single join is faster than incremental string building
538-
return "".join(result)
466+
return "".join([line if line.strip() else "\n" if line.endswith("\n") else "" for line in lines])
467+
468+
# Apply margin removal (most common case) with minimal operations
469+
return "".join([line[margin_len:] if line.strip() else "\n" if line.endswith("\n") else "" for line in lines])
539470

540471

541472
def indent(text, prefix, predicate=None):

0 commit comments

Comments
 (0)