(root)/
Python-3.11.7/
Lib/
test/
test_textwrap.py
       1  #
       2  # Test suite for the textwrap module.
       3  #
       4  # Original tests written by Greg Ward <gward@python.net>.
       5  # Converted to PyUnit by Peter Hansen <peter@engcorp.com>.
       6  # Currently maintained by Greg Ward.
       7  #
       8  # $Id$
       9  #
      10  
      11  import unittest
      12  
      13  from textwrap import TextWrapper, wrap, fill, dedent, indent, shorten
      14  
      15  
      16  class ESC[4;38;5;81mBaseTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      17      '''Parent class with utility methods for textwrap tests.'''
      18  
      19      def show(self, textin):
      20          if isinstance(textin, list):
      21              result = []
      22              for i in range(len(textin)):
      23                  result.append("  %d: %r" % (i, textin[i]))
      24              result = "\n".join(result) if result else "  no lines"
      25          elif isinstance(textin, str):
      26              result = "  %s\n" % repr(textin)
      27          return result
      28  
      29  
      30      def check(self, result, expect):
      31          self.assertEqual(result, expect,
      32              'expected:\n%s\nbut got:\n%s' % (
      33                  self.show(expect), self.show(result)))
      34  
      35      def check_wrap(self, text, width, expect, **kwargs):
      36          result = wrap(text, width, **kwargs)
      37          self.check(result, expect)
      38  
      39      def check_split(self, text, expect):
      40          result = self.wrapper._split(text)
      41          self.assertEqual(result, expect,
      42                           "\nexpected %r\n"
      43                           "but got  %r" % (expect, result))
      44  
      45  
      46  class ESC[4;38;5;81mWrapTestCase(ESC[4;38;5;149mBaseTestCase):
      47  
      48      def setUp(self):
      49          self.wrapper = TextWrapper(width=45)
      50  
      51      def test_simple(self):
      52          # Simple case: just words, spaces, and a bit of punctuation
      53  
      54          text = "Hello there, how are you this fine day?  I'm glad to hear it!"
      55  
      56          self.check_wrap(text, 12,
      57                          ["Hello there,",
      58                           "how are you",
      59                           "this fine",
      60                           "day?  I'm",
      61                           "glad to hear",
      62                           "it!"])
      63          self.check_wrap(text, 42,
      64                          ["Hello there, how are you this fine day?",
      65                           "I'm glad to hear it!"])
      66          self.check_wrap(text, 80, [text])
      67  
      68      def test_empty_string(self):
      69          # Check that wrapping the empty string returns an empty list.
      70          self.check_wrap("", 6, [])
      71          self.check_wrap("", 6, [], drop_whitespace=False)
      72  
      73      def test_empty_string_with_initial_indent(self):
      74          # Check that the empty string is not indented.
      75          self.check_wrap("", 6, [], initial_indent="++")
      76          self.check_wrap("", 6, [], initial_indent="++", drop_whitespace=False)
      77  
      78      def test_whitespace(self):
      79          # Whitespace munging and end-of-sentence detection
      80  
      81          text = """\
      82  This is a paragraph that already has
      83  line breaks.  But some of its lines are much longer than the others,
      84  so it needs to be wrapped.
      85  Some lines are \ttabbed too.
      86  What a mess!
      87  """
      88  
      89          expect = ["This is a paragraph that already has line",
      90                    "breaks.  But some of its lines are much",
      91                    "longer than the others, so it needs to be",
      92                    "wrapped.  Some lines are  tabbed too.  What a",
      93                    "mess!"]
      94  
      95          wrapper = TextWrapper(45, fix_sentence_endings=True)
      96          result = wrapper.wrap(text)
      97          self.check(result, expect)
      98  
      99          result = wrapper.fill(text)
     100          self.check(result, '\n'.join(expect))
     101  
     102          text = "\tTest\tdefault\t\ttabsize."
     103          expect = ["        Test    default         tabsize."]
     104          self.check_wrap(text, 80, expect)
     105  
     106          text = "\tTest\tcustom\t\ttabsize."
     107          expect = ["    Test    custom      tabsize."]
     108          self.check_wrap(text, 80, expect, tabsize=4)
     109  
     110      def test_fix_sentence_endings(self):
     111          wrapper = TextWrapper(60, fix_sentence_endings=True)
     112  
     113          # SF #847346: ensure that fix_sentence_endings=True does the
     114          # right thing even on input short enough that it doesn't need to
     115          # be wrapped.
     116          text = "A short line. Note the single space."
     117          expect = ["A short line.  Note the single space."]
     118          self.check(wrapper.wrap(text), expect)
     119  
     120          # Test some of the hairy end cases that _fix_sentence_endings()
     121          # is supposed to handle (the easy stuff is tested in
     122          # test_whitespace() above).
     123          text = "Well, Doctor? What do you think?"
     124          expect = ["Well, Doctor?  What do you think?"]
     125          self.check(wrapper.wrap(text), expect)
     126  
     127          text = "Well, Doctor?\nWhat do you think?"
     128          self.check(wrapper.wrap(text), expect)
     129  
     130          text = 'I say, chaps! Anyone for "tennis?"\nHmmph!'
     131          expect = ['I say, chaps!  Anyone for "tennis?"  Hmmph!']
     132          self.check(wrapper.wrap(text), expect)
     133  
     134          wrapper.width = 20
     135          expect = ['I say, chaps!', 'Anyone for "tennis?"', 'Hmmph!']
     136          self.check(wrapper.wrap(text), expect)
     137  
     138          text = 'And she said, "Go to hell!"\nCan you believe that?'
     139          expect = ['And she said, "Go to',
     140                    'hell!"  Can you',
     141                    'believe that?']
     142          self.check(wrapper.wrap(text), expect)
     143  
     144          wrapper.width = 60
     145          expect = ['And she said, "Go to hell!"  Can you believe that?']
     146          self.check(wrapper.wrap(text), expect)
     147  
     148          text = 'File stdio.h is nice.'
     149          expect = ['File stdio.h is nice.']
     150          self.check(wrapper.wrap(text), expect)
     151  
     152      def test_wrap_short(self):
     153          # Wrapping to make short lines longer
     154  
     155          text = "This is a\nshort paragraph."
     156  
     157          self.check_wrap(text, 20, ["This is a short",
     158                                     "paragraph."])
     159          self.check_wrap(text, 40, ["This is a short paragraph."])
     160  
     161  
     162      def test_wrap_short_1line(self):
     163          # Test endcases
     164  
     165          text = "This is a short line."
     166  
     167          self.check_wrap(text, 30, ["This is a short line."])
     168          self.check_wrap(text, 30, ["(1) This is a short line."],
     169                          initial_indent="(1) ")
     170  
     171  
     172      def test_hyphenated(self):
     173          # Test breaking hyphenated words
     174  
     175          text = ("this-is-a-useful-feature-for-"
     176                  "reformatting-posts-from-tim-peters'ly")
     177  
     178          self.check_wrap(text, 40,
     179                          ["this-is-a-useful-feature-for-",
     180                           "reformatting-posts-from-tim-peters'ly"])
     181          self.check_wrap(text, 41,
     182                          ["this-is-a-useful-feature-for-",
     183                           "reformatting-posts-from-tim-peters'ly"])
     184          self.check_wrap(text, 42,
     185                          ["this-is-a-useful-feature-for-reformatting-",
     186                           "posts-from-tim-peters'ly"])
     187          # The test tests current behavior but is not testing parts of the API.
     188          expect = ("this-|is-|a-|useful-|feature-|for-|"
     189                    "reformatting-|posts-|from-|tim-|peters'ly").split('|')
     190          self.check_wrap(text, 1, expect, break_long_words=False)
     191          self.check_split(text, expect)
     192  
     193          self.check_split('e-mail', ['e-mail'])
     194          self.check_split('Jelly-O', ['Jelly-O'])
     195          # The test tests current behavior but is not testing parts of the API.
     196          self.check_split('half-a-crown', 'half-|a-|crown'.split('|'))
     197  
     198      def test_hyphenated_numbers(self):
     199          # Test that hyphenated numbers (eg. dates) are not broken like words.
     200          text = ("Python 1.0.0 was released on 1994-01-26.  Python 1.0.1 was\n"
     201                  "released on 1994-02-15.")
     202  
     203          self.check_wrap(text, 30, ['Python 1.0.0 was released on',
     204                                     '1994-01-26.  Python 1.0.1 was',
     205                                     'released on 1994-02-15.'])
     206          self.check_wrap(text, 40, ['Python 1.0.0 was released on 1994-01-26.',
     207                                     'Python 1.0.1 was released on 1994-02-15.'])
     208          self.check_wrap(text, 1, text.split(), break_long_words=False)
     209  
     210          text = "I do all my shopping at 7-11."
     211          self.check_wrap(text, 25, ["I do all my shopping at",
     212                                     "7-11."])
     213          self.check_wrap(text, 27, ["I do all my shopping at",
     214                                     "7-11."])
     215          self.check_wrap(text, 29, ["I do all my shopping at 7-11."])
     216          self.check_wrap(text, 1, text.split(), break_long_words=False)
     217  
     218      def test_em_dash(self):
     219          # Test text with em-dashes
     220          text = "Em-dashes should be written -- thus."
     221          self.check_wrap(text, 25,
     222                          ["Em-dashes should be",
     223                           "written -- thus."])
     224  
     225          # Probe the boundaries of the properly written em-dash,
     226          # ie. " -- ".
     227          self.check_wrap(text, 29,
     228                          ["Em-dashes should be written",
     229                           "-- thus."])
     230          expect = ["Em-dashes should be written --",
     231                    "thus."]
     232          self.check_wrap(text, 30, expect)
     233          self.check_wrap(text, 35, expect)
     234          self.check_wrap(text, 36,
     235                          ["Em-dashes should be written -- thus."])
     236  
     237          # The improperly written em-dash is handled too, because
     238          # it's adjacent to non-whitespace on both sides.
     239          text = "You can also do--this or even---this."
     240          expect = ["You can also do",
     241                    "--this or even",
     242                    "---this."]
     243          self.check_wrap(text, 15, expect)
     244          self.check_wrap(text, 16, expect)
     245          expect = ["You can also do--",
     246                    "this or even---",
     247                    "this."]
     248          self.check_wrap(text, 17, expect)
     249          self.check_wrap(text, 19, expect)
     250          expect = ["You can also do--this or even",
     251                    "---this."]
     252          self.check_wrap(text, 29, expect)
     253          self.check_wrap(text, 31, expect)
     254          expect = ["You can also do--this or even---",
     255                    "this."]
     256          self.check_wrap(text, 32, expect)
     257          self.check_wrap(text, 35, expect)
     258  
     259          # All of the above behaviour could be deduced by probing the
     260          # _split() method.
     261          text = "Here's an -- em-dash and--here's another---and another!"
     262          expect = ["Here's", " ", "an", " ", "--", " ", "em-", "dash", " ",
     263                    "and", "--", "here's", " ", "another", "---",
     264                    "and", " ", "another!"]
     265          self.check_split(text, expect)
     266  
     267          text = "and then--bam!--he was gone"
     268          expect = ["and", " ", "then", "--", "bam!", "--",
     269                    "he", " ", "was", " ", "gone"]
     270          self.check_split(text, expect)
     271  
     272  
     273      def test_unix_options (self):
     274          # Test that Unix-style command-line options are wrapped correctly.
     275          # Both Optik (OptionParser) and Docutils rely on this behaviour!
     276  
     277          text = "You should use the -n option, or --dry-run in its long form."
     278          self.check_wrap(text, 20,
     279                          ["You should use the",
     280                           "-n option, or --dry-",
     281                           "run in its long",
     282                           "form."])
     283          self.check_wrap(text, 21,
     284                          ["You should use the -n",
     285                           "option, or --dry-run",
     286                           "in its long form."])
     287          expect = ["You should use the -n option, or",
     288                    "--dry-run in its long form."]
     289          self.check_wrap(text, 32, expect)
     290          self.check_wrap(text, 34, expect)
     291          self.check_wrap(text, 35, expect)
     292          self.check_wrap(text, 38, expect)
     293          expect = ["You should use the -n option, or --dry-",
     294                    "run in its long form."]
     295          self.check_wrap(text, 39, expect)
     296          self.check_wrap(text, 41, expect)
     297          expect = ["You should use the -n option, or --dry-run",
     298                    "in its long form."]
     299          self.check_wrap(text, 42, expect)
     300  
     301          # Again, all of the above can be deduced from _split().
     302          text = "the -n option, or --dry-run or --dryrun"
     303          expect = ["the", " ", "-n", " ", "option,", " ", "or", " ",
     304                    "--dry-", "run", " ", "or", " ", "--dryrun"]
     305          self.check_split(text, expect)
     306  
     307      def test_funky_hyphens (self):
     308          # Screwy edge cases cooked up by David Goodger.  All reported
     309          # in SF bug #596434.
     310          self.check_split("what the--hey!", ["what", " ", "the", "--", "hey!"])
     311          self.check_split("what the--", ["what", " ", "the--"])
     312          self.check_split("what the--.", ["what", " ", "the--."])
     313          self.check_split("--text--.", ["--text--."])
     314  
     315          # When I first read bug #596434, this is what I thought David
     316          # was talking about.  I was wrong; these have always worked
     317          # fine.  The real problem is tested in test_funky_parens()
     318          # below...
     319          self.check_split("--option", ["--option"])
     320          self.check_split("--option-opt", ["--option-", "opt"])
     321          self.check_split("foo --option-opt bar",
     322                           ["foo", " ", "--option-", "opt", " ", "bar"])
     323  
     324      def test_punct_hyphens(self):
     325          # Oh bother, SF #965425 found another problem with hyphens --
     326          # hyphenated words in single quotes weren't handled correctly.
     327          # In fact, the bug is that *any* punctuation around a hyphenated
     328          # word was handled incorrectly, except for a leading "--", which
     329          # was special-cased for Optik and Docutils.  So test a variety
     330          # of styles of punctuation around a hyphenated word.
     331          # (Actually this is based on an Optik bug report, #813077).
     332          self.check_split("the 'wibble-wobble' widget",
     333                           ['the', ' ', "'wibble-", "wobble'", ' ', 'widget'])
     334          self.check_split('the "wibble-wobble" widget',
     335                           ['the', ' ', '"wibble-', 'wobble"', ' ', 'widget'])
     336          self.check_split("the (wibble-wobble) widget",
     337                           ['the', ' ', "(wibble-", "wobble)", ' ', 'widget'])
     338          self.check_split("the ['wibble-wobble'] widget",
     339                           ['the', ' ', "['wibble-", "wobble']", ' ', 'widget'])
     340  
     341          # The test tests current behavior but is not testing parts of the API.
     342          self.check_split("what-d'you-call-it.",
     343                           "what-d'you-|call-|it.".split('|'))
     344  
     345      def test_funky_parens (self):
     346          # Second part of SF bug #596434: long option strings inside
     347          # parentheses.
     348          self.check_split("foo (--option) bar",
     349                           ["foo", " ", "(--option)", " ", "bar"])
     350  
     351          # Related stuff -- make sure parens work in simpler contexts.
     352          self.check_split("foo (bar) baz",
     353                           ["foo", " ", "(bar)", " ", "baz"])
     354          self.check_split("blah (ding dong), wubba",
     355                           ["blah", " ", "(ding", " ", "dong),",
     356                            " ", "wubba"])
     357  
     358      def test_drop_whitespace_false(self):
     359          # Check that drop_whitespace=False preserves whitespace.
     360          # SF patch #1581073
     361          text = " This is a    sentence with     much whitespace."
     362          self.check_wrap(text, 10,
     363                          [" This is a", "    ", "sentence ",
     364                           "with     ", "much white", "space."],
     365                          drop_whitespace=False)
     366  
     367      def test_drop_whitespace_false_whitespace_only(self):
     368          # Check that drop_whitespace=False preserves a whitespace-only string.
     369          self.check_wrap("   ", 6, ["   "], drop_whitespace=False)
     370  
     371      def test_drop_whitespace_false_whitespace_only_with_indent(self):
     372          # Check that a whitespace-only string gets indented (when
     373          # drop_whitespace is False).
     374          self.check_wrap("   ", 6, ["     "], drop_whitespace=False,
     375                          initial_indent="  ")
     376  
     377      def test_drop_whitespace_whitespace_only(self):
     378          # Check drop_whitespace on a whitespace-only string.
     379          self.check_wrap("  ", 6, [])
     380  
     381      def test_drop_whitespace_leading_whitespace(self):
     382          # Check that drop_whitespace does not drop leading whitespace (if
     383          # followed by non-whitespace).
     384          # SF bug #622849 reported inconsistent handling of leading
     385          # whitespace; let's test that a bit, shall we?
     386          text = " This is a sentence with leading whitespace."
     387          self.check_wrap(text, 50,
     388                          [" This is a sentence with leading whitespace."])
     389          self.check_wrap(text, 30,
     390                          [" This is a sentence with", "leading whitespace."])
     391  
     392      def test_drop_whitespace_whitespace_line(self):
     393          # Check that drop_whitespace skips the whole line if a non-leading
     394          # line consists only of whitespace.
     395          text = "abcd    efgh"
     396          # Include the result for drop_whitespace=False for comparison.
     397          self.check_wrap(text, 6, ["abcd", "    ", "efgh"],
     398                          drop_whitespace=False)
     399          self.check_wrap(text, 6, ["abcd", "efgh"])
     400  
     401      def test_drop_whitespace_whitespace_only_with_indent(self):
     402          # Check that initial_indent is not applied to a whitespace-only
     403          # string.  This checks a special case of the fact that dropping
     404          # whitespace occurs before indenting.
     405          self.check_wrap("  ", 6, [], initial_indent="++")
     406  
     407      def test_drop_whitespace_whitespace_indent(self):
     408          # Check that drop_whitespace does not drop whitespace indents.
     409          # This checks a special case of the fact that dropping whitespace
     410          # occurs before indenting.
     411          self.check_wrap("abcd efgh", 6, ["  abcd", "  efgh"],
     412                          initial_indent="  ", subsequent_indent="  ")
     413  
     414      def test_split(self):
     415          # Ensure that the standard _split() method works as advertised
     416          # in the comments
     417  
     418          text = "Hello there -- you goof-ball, use the -b option!"
     419  
     420          result = self.wrapper._split(text)
     421          self.check(result,
     422               ["Hello", " ", "there", " ", "--", " ", "you", " ", "goof-",
     423                "ball,", " ", "use", " ", "the", " ", "-b", " ",  "option!"])
     424  
     425      def test_break_on_hyphens(self):
     426          # Ensure that the break_on_hyphens attributes work
     427          text = "yaba daba-doo"
     428          self.check_wrap(text, 10, ["yaba daba-", "doo"],
     429                          break_on_hyphens=True)
     430          self.check_wrap(text, 10, ["yaba", "daba-doo"],
     431                          break_on_hyphens=False)
     432  
     433      def test_bad_width(self):
     434          # Ensure that width <= 0 is caught.
     435          text = "Whatever, it doesn't matter."
     436          self.assertRaises(ValueError, wrap, text, 0)
     437          self.assertRaises(ValueError, wrap, text, -1)
     438  
     439      def test_no_split_at_umlaut(self):
     440          text = "Die Empf\xe4nger-Auswahl"
     441          self.check_wrap(text, 13, ["Die", "Empf\xe4nger-", "Auswahl"])
     442  
     443      def test_umlaut_followed_by_dash(self):
     444          text = "aa \xe4\xe4-\xe4\xe4"
     445          self.check_wrap(text, 7, ["aa \xe4\xe4-", "\xe4\xe4"])
     446  
     447      def test_non_breaking_space(self):
     448          text = 'This is a sentence with non-breaking\N{NO-BREAK SPACE}space.'
     449  
     450          self.check_wrap(text, 20,
     451                          ['This is a sentence',
     452                           'with non-',
     453                           'breaking\N{NO-BREAK SPACE}space.'],
     454                          break_on_hyphens=True)
     455  
     456          self.check_wrap(text, 20,
     457                          ['This is a sentence',
     458                           'with',
     459                           'non-breaking\N{NO-BREAK SPACE}space.'],
     460                          break_on_hyphens=False)
     461  
     462      def test_narrow_non_breaking_space(self):
     463          text = ('This is a sentence with non-breaking'
     464                  '\N{NARROW NO-BREAK SPACE}space.')
     465  
     466          self.check_wrap(text, 20,
     467                          ['This is a sentence',
     468                           'with non-',
     469                           'breaking\N{NARROW NO-BREAK SPACE}space.'],
     470                          break_on_hyphens=True)
     471  
     472          self.check_wrap(text, 20,
     473                          ['This is a sentence',
     474                           'with',
     475                           'non-breaking\N{NARROW NO-BREAK SPACE}space.'],
     476                          break_on_hyphens=False)
     477  
     478  
     479  class ESC[4;38;5;81mMaxLinesTestCase(ESC[4;38;5;149mBaseTestCase):
     480      text = "Hello there, how are you this fine day?  I'm glad to hear it!"
     481  
     482      def test_simple(self):
     483          self.check_wrap(self.text, 12,
     484                          ["Hello [...]"],
     485                          max_lines=0)
     486          self.check_wrap(self.text, 12,
     487                          ["Hello [...]"],
     488                          max_lines=1)
     489          self.check_wrap(self.text, 12,
     490                          ["Hello there,",
     491                           "how [...]"],
     492                          max_lines=2)
     493          self.check_wrap(self.text, 13,
     494                          ["Hello there,",
     495                           "how are [...]"],
     496                          max_lines=2)
     497          self.check_wrap(self.text, 80, [self.text], max_lines=1)
     498          self.check_wrap(self.text, 12,
     499                          ["Hello there,",
     500                           "how are you",
     501                           "this fine",
     502                           "day?  I'm",
     503                           "glad to hear",
     504                           "it!"],
     505                          max_lines=6)
     506  
     507      def test_spaces(self):
     508          # strip spaces before placeholder
     509          self.check_wrap(self.text, 12,
     510                          ["Hello there,",
     511                           "how are you",
     512                           "this fine",
     513                           "day? [...]"],
     514                          max_lines=4)
     515          # placeholder at the start of line
     516          self.check_wrap(self.text, 6,
     517                          ["Hello",
     518                           "[...]"],
     519                          max_lines=2)
     520          # final spaces
     521          self.check_wrap(self.text + ' ' * 10, 12,
     522                          ["Hello there,",
     523                           "how are you",
     524                           "this fine",
     525                           "day?  I'm",
     526                           "glad to hear",
     527                           "it!"],
     528                          max_lines=6)
     529  
     530      def test_placeholder(self):
     531          self.check_wrap(self.text, 12,
     532                          ["Hello..."],
     533                          max_lines=1,
     534                          placeholder='...')
     535          self.check_wrap(self.text, 12,
     536                          ["Hello there,",
     537                           "how are..."],
     538                          max_lines=2,
     539                          placeholder='...')
     540          # long placeholder and indentation
     541          with self.assertRaises(ValueError):
     542              wrap(self.text, 16, initial_indent='    ',
     543                   max_lines=1, placeholder=' [truncated]...')
     544          with self.assertRaises(ValueError):
     545              wrap(self.text, 16, subsequent_indent='    ',
     546                   max_lines=2, placeholder=' [truncated]...')
     547          self.check_wrap(self.text, 16,
     548                          ["    Hello there,",
     549                           "  [truncated]..."],
     550                          max_lines=2,
     551                          initial_indent='    ',
     552                          subsequent_indent='  ',
     553                          placeholder=' [truncated]...')
     554          self.check_wrap(self.text, 16,
     555                          ["  [truncated]..."],
     556                          max_lines=1,
     557                          initial_indent='  ',
     558                          subsequent_indent='    ',
     559                          placeholder=' [truncated]...')
     560          self.check_wrap(self.text, 80, [self.text], placeholder='.' * 1000)
     561  
     562      def test_placeholder_backtrack(self):
     563          # Test special case when max_lines insufficient, but what
     564          # would be last wrapped line so long the placeholder cannot
     565          # be added there without violence. So, textwrap backtracks,
     566          # adding placeholder to the penultimate line.
     567          text = 'Good grief Python features are advancing quickly!'
     568          self.check_wrap(text, 12,
     569                          ['Good grief', 'Python*****'],
     570                          max_lines=3,
     571                          placeholder='*****')
     572  
     573  
     574  class ESC[4;38;5;81mLongWordTestCase (ESC[4;38;5;149mBaseTestCase):
     575      def setUp(self):
     576          self.wrapper = TextWrapper()
     577          self.text = '''\
     578  Did you say "supercalifragilisticexpialidocious?"
     579  How *do* you spell that odd word, anyways?
     580  '''
     581  
     582      def test_break_long(self):
     583          # Wrap text with long words and lots of punctuation
     584  
     585          self.check_wrap(self.text, 30,
     586                          ['Did you say "supercalifragilis',
     587                           'ticexpialidocious?" How *do*',
     588                           'you spell that odd word,',
     589                           'anyways?'])
     590          self.check_wrap(self.text, 50,
     591                          ['Did you say "supercalifragilisticexpialidocious?"',
     592                           'How *do* you spell that odd word, anyways?'])
     593  
     594          # SF bug 797650.  Prevent an infinite loop by making sure that at
     595          # least one character gets split off on every pass.
     596          self.check_wrap('-'*10+'hello', 10,
     597                          ['----------',
     598                           '               h',
     599                           '               e',
     600                           '               l',
     601                           '               l',
     602                           '               o'],
     603                          subsequent_indent = ' '*15)
     604  
     605          # bug 1146.  Prevent a long word to be wrongly wrapped when the
     606          # preceding word is exactly one character shorter than the width
     607          self.check_wrap(self.text, 12,
     608                          ['Did you say ',
     609                           '"supercalifr',
     610                           'agilisticexp',
     611                           'ialidocious?',
     612                           '" How *do*',
     613                           'you spell',
     614                           'that odd',
     615                           'word,',
     616                           'anyways?'])
     617  
     618      def test_nobreak_long(self):
     619          # Test with break_long_words disabled
     620          self.wrapper.break_long_words = 0
     621          self.wrapper.width = 30
     622          expect = ['Did you say',
     623                    '"supercalifragilisticexpialidocious?"',
     624                    'How *do* you spell that odd',
     625                    'word, anyways?'
     626                    ]
     627          result = self.wrapper.wrap(self.text)
     628          self.check(result, expect)
     629  
     630          # Same thing with kwargs passed to standalone wrap() function.
     631          result = wrap(self.text, width=30, break_long_words=0)
     632          self.check(result, expect)
     633  
     634      def test_max_lines_long(self):
     635          self.check_wrap(self.text, 12,
     636                          ['Did you say ',
     637                           '"supercalifr',
     638                           'agilisticexp',
     639                           '[...]'],
     640                          max_lines=4)
     641  
     642  
     643  class ESC[4;38;5;81mLongWordWithHyphensTestCase(ESC[4;38;5;149mBaseTestCase):
     644      def setUp(self):
     645          self.wrapper = TextWrapper()
     646          self.text1 = '''\
     647  We used enyzme 2-succinyl-6-hydroxy-2,4-cyclohexadiene-1-carboxylate synthase.
     648  '''
     649          self.text2 = '''\
     650  1234567890-1234567890--this_is_a_very_long_option_indeed-good-bye"
     651  '''
     652  
     653      def test_break_long_words_on_hyphen(self):
     654          expected = ['We used enyzme 2-succinyl-6-hydroxy-2,4-',
     655                      'cyclohexadiene-1-carboxylate synthase.']
     656          self.check_wrap(self.text1, 50, expected)
     657  
     658          expected = ['We used', 'enyzme 2-', 'succinyl-', '6-hydroxy-', '2,4-',
     659                      'cyclohexad', 'iene-1-', 'carboxylat', 'e', 'synthase.']
     660          self.check_wrap(self.text1, 10, expected)
     661  
     662          expected = ['1234567890',  '-123456789', '0--this_is', '_a_very_lo',
     663                      'ng_option_', 'indeed-', 'good-bye"']
     664          self.check_wrap(self.text2, 10, expected)
     665  
     666      def test_break_long_words_not_on_hyphen(self):
     667          expected = ['We used enyzme 2-succinyl-6-hydroxy-2,4-cyclohexad',
     668                      'iene-1-carboxylate synthase.']
     669          self.check_wrap(self.text1, 50, expected, break_on_hyphens=False)
     670  
     671          expected = ['We used', 'enyzme 2-s', 'uccinyl-6-', 'hydroxy-2,',
     672                      '4-cyclohex', 'adiene-1-c', 'arboxylate', 'synthase.']
     673          self.check_wrap(self.text1, 10, expected, break_on_hyphens=False)
     674  
     675          expected = ['1234567890',  '-123456789', '0--this_is', '_a_very_lo',
     676                      'ng_option_', 'indeed-', 'good-bye"']
     677          self.check_wrap(self.text2, 10, expected)
     678  
     679      def test_break_on_hyphen_but_not_long_words(self):
     680          expected = ['We used enyzme',
     681                      '2-succinyl-6-hydroxy-2,4-cyclohexadiene-1-carboxylate',
     682                      'synthase.']
     683  
     684          self.check_wrap(self.text1, 50, expected, break_long_words=False)
     685  
     686          expected = ['We used', 'enyzme',
     687                      '2-succinyl-6-hydroxy-2,4-cyclohexadiene-1-carboxylate',
     688                      'synthase.']
     689          self.check_wrap(self.text1, 10, expected, break_long_words=False)
     690  
     691          expected = ['1234567890',  '-123456789', '0--this_is', '_a_very_lo',
     692                      'ng_option_', 'indeed-', 'good-bye"']
     693          self.check_wrap(self.text2, 10, expected)
     694  
     695  
     696      def test_do_not_break_long_words_or_on_hyphens(self):
     697          expected = ['We used enyzme',
     698                      '2-succinyl-6-hydroxy-2,4-cyclohexadiene-1-carboxylate',
     699                      'synthase.']
     700          self.check_wrap(self.text1, 50, expected,
     701                          break_long_words=False,
     702                          break_on_hyphens=False)
     703  
     704          expected = ['We used', 'enyzme',
     705                      '2-succinyl-6-hydroxy-2,4-cyclohexadiene-1-carboxylate',
     706                      'synthase.']
     707          self.check_wrap(self.text1, 10, expected,
     708                          break_long_words=False,
     709                          break_on_hyphens=False)
     710  
     711          expected = ['1234567890',  '-123456789', '0--this_is', '_a_very_lo',
     712                      'ng_option_', 'indeed-', 'good-bye"']
     713          self.check_wrap(self.text2, 10, expected)
     714  
     715  class ESC[4;38;5;81mIndentTestCases(ESC[4;38;5;149mBaseTestCase):
     716  
     717      # called before each test method
     718      def setUp(self):
     719          self.text = '''\
     720  This paragraph will be filled, first without any indentation,
     721  and then with some (including a hanging indent).'''
     722  
     723  
     724      def test_fill(self):
     725          # Test the fill() method
     726  
     727          expect = '''\
     728  This paragraph will be filled, first
     729  without any indentation, and then with
     730  some (including a hanging indent).'''
     731  
     732          result = fill(self.text, 40)
     733          self.check(result, expect)
     734  
     735  
     736      def test_initial_indent(self):
     737          # Test initial_indent parameter
     738  
     739          expect = ["     This paragraph will be filled,",
     740                    "first without any indentation, and then",
     741                    "with some (including a hanging indent)."]
     742          result = wrap(self.text, 40, initial_indent="     ")
     743          self.check(result, expect)
     744  
     745          expect = "\n".join(expect)
     746          result = fill(self.text, 40, initial_indent="     ")
     747          self.check(result, expect)
     748  
     749  
     750      def test_subsequent_indent(self):
     751          # Test subsequent_indent parameter
     752  
     753          expect = '''\
     754    * This paragraph will be filled, first
     755      without any indentation, and then
     756      with some (including a hanging
     757      indent).'''
     758  
     759          result = fill(self.text, 40,
     760                        initial_indent="  * ", subsequent_indent="    ")
     761          self.check(result, expect)
     762  
     763  
     764  # Despite the similar names, DedentTestCase is *not* the inverse
     765  # of IndentTestCase!
     766  class ESC[4;38;5;81mDedentTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     767  
     768      def assertUnchanged(self, text):
     769          """assert that dedent() has no effect on 'text'"""
     770          self.assertEqual(text, dedent(text))
     771  
     772      def test_dedent_nomargin(self):
     773          # No lines indented.
     774          text = "Hello there.\nHow are you?\nOh good, I'm glad."
     775          self.assertUnchanged(text)
     776  
     777          # Similar, with a blank line.
     778          text = "Hello there.\n\nBoo!"
     779          self.assertUnchanged(text)
     780  
     781          # Some lines indented, but overall margin is still zero.
     782          text = "Hello there.\n  This is indented."
     783          self.assertUnchanged(text)
     784  
     785          # Again, add a blank line.
     786          text = "Hello there.\n\n  Boo!\n"
     787          self.assertUnchanged(text)
     788  
     789      def test_dedent_even(self):
     790          # All lines indented by two spaces.
     791          text = "  Hello there.\n  How are ya?\n  Oh good."
     792          expect = "Hello there.\nHow are ya?\nOh good."
     793          self.assertEqual(expect, dedent(text))
     794  
     795          # Same, with blank lines.
     796          text = "  Hello there.\n\n  How are ya?\n  Oh good.\n"
     797          expect = "Hello there.\n\nHow are ya?\nOh good.\n"
     798          self.assertEqual(expect, dedent(text))
     799  
     800          # Now indent one of the blank lines.
     801          text = "  Hello there.\n  \n  How are ya?\n  Oh good.\n"
     802          expect = "Hello there.\n\nHow are ya?\nOh good.\n"
     803          self.assertEqual(expect, dedent(text))
     804  
     805      def test_dedent_uneven(self):
     806          # Lines indented unevenly.
     807          text = '''\
     808          def foo():
     809              while 1:
     810                  return foo
     811          '''
     812          expect = '''\
     813  def foo():
     814      while 1:
     815          return foo
     816  '''
     817          self.assertEqual(expect, dedent(text))
     818  
     819          # Uneven indentation with a blank line.
     820          text = "  Foo\n    Bar\n\n   Baz\n"
     821          expect = "Foo\n  Bar\n\n Baz\n"
     822          self.assertEqual(expect, dedent(text))
     823  
     824          # Uneven indentation with a whitespace-only line.
     825          text = "  Foo\n    Bar\n \n   Baz\n"
     826          expect = "Foo\n  Bar\n\n Baz\n"
     827          self.assertEqual(expect, dedent(text))
     828  
     829      def test_dedent_declining(self):
     830          # Uneven indentation with declining indent level.
     831          text = "     Foo\n    Bar\n"  # 5 spaces, then 4
     832          expect = " Foo\nBar\n"
     833          self.assertEqual(expect, dedent(text))
     834  
     835          # Declining indent level with blank line.
     836          text = "     Foo\n\n    Bar\n"  # 5 spaces, blank, then 4
     837          expect = " Foo\n\nBar\n"
     838          self.assertEqual(expect, dedent(text))
     839  
     840          # Declining indent level with whitespace only line.
     841          text = "     Foo\n    \n    Bar\n"  # 5 spaces, then 4, then 4
     842          expect = " Foo\n\nBar\n"
     843          self.assertEqual(expect, dedent(text))
     844  
     845      # dedent() should not mangle internal tabs
     846      def test_dedent_preserve_internal_tabs(self):
     847          text = "  hello\tthere\n  how are\tyou?"
     848          expect = "hello\tthere\nhow are\tyou?"
     849          self.assertEqual(expect, dedent(text))
     850  
     851          # make sure that it preserves tabs when it's not making any
     852          # changes at all
     853          self.assertEqual(expect, dedent(expect))
     854  
     855      # dedent() should not mangle tabs in the margin (i.e.
     856      # tabs and spaces both count as margin, but are *not*
     857      # considered equivalent)
     858      def test_dedent_preserve_margin_tabs(self):
     859          text = "  hello there\n\thow are you?"
     860          self.assertUnchanged(text)
     861  
     862          # same effect even if we have 8 spaces
     863          text = "        hello there\n\thow are you?"
     864          self.assertUnchanged(text)
     865  
     866          # dedent() only removes whitespace that can be uniformly removed!
     867          text = "\thello there\n\thow are you?"
     868          expect = "hello there\nhow are you?"
     869          self.assertEqual(expect, dedent(text))
     870  
     871          text = "  \thello there\n  \thow are you?"
     872          self.assertEqual(expect, dedent(text))
     873  
     874          text = "  \t  hello there\n  \t  how are you?"
     875          self.assertEqual(expect, dedent(text))
     876  
     877          text = "  \thello there\n  \t  how are you?"
     878          expect = "hello there\n  how are you?"
     879          self.assertEqual(expect, dedent(text))
     880  
     881          # test margin is smaller than smallest indent
     882          text = "  \thello there\n   \thow are you?\n \tI'm fine, thanks"
     883          expect = " \thello there\n  \thow are you?\n\tI'm fine, thanks"
     884          self.assertEqual(expect, dedent(text))
     885  
     886  
     887  # Test textwrap.indent
     888  class ESC[4;38;5;81mIndentTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     889      # The examples used for tests. If any of these change, the expected
     890      # results in the various test cases must also be updated.
     891      # The roundtrip cases are separate, because textwrap.dedent doesn't
     892      # handle Windows line endings
     893      ROUNDTRIP_CASES = (
     894        # Basic test case
     895        "Hi.\nThis is a test.\nTesting.",
     896        # Include a blank line
     897        "Hi.\nThis is a test.\n\nTesting.",
     898        # Include leading and trailing blank lines
     899        "\nHi.\nThis is a test.\nTesting.\n",
     900      )
     901      CASES = ROUNDTRIP_CASES + (
     902        # Use Windows line endings
     903        "Hi.\r\nThis is a test.\r\nTesting.\r\n",
     904        # Pathological case
     905        "\nHi.\r\nThis is a test.\n\r\nTesting.\r\n\n",
     906      )
     907  
     908      def test_indent_nomargin_default(self):
     909          # indent should do nothing if 'prefix' is empty.
     910          for text in self.CASES:
     911              self.assertEqual(indent(text, ''), text)
     912  
     913      def test_indent_nomargin_explicit_default(self):
     914          # The same as test_indent_nomargin, but explicitly requesting
     915          # the default behaviour by passing None as the predicate
     916          for text in self.CASES:
     917              self.assertEqual(indent(text, '', None), text)
     918  
     919      def test_indent_nomargin_all_lines(self):
     920          # The same as test_indent_nomargin, but using the optional
     921          # predicate argument
     922          predicate = lambda line: True
     923          for text in self.CASES:
     924              self.assertEqual(indent(text, '', predicate), text)
     925  
     926      def test_indent_no_lines(self):
     927          # Explicitly skip indenting any lines
     928          predicate = lambda line: False
     929          for text in self.CASES:
     930              self.assertEqual(indent(text, '    ', predicate), text)
     931  
     932      def test_roundtrip_spaces(self):
     933          # A whitespace prefix should roundtrip with dedent
     934          for text in self.ROUNDTRIP_CASES:
     935              self.assertEqual(dedent(indent(text, '    ')), text)
     936  
     937      def test_roundtrip_tabs(self):
     938          # A whitespace prefix should roundtrip with dedent
     939          for text in self.ROUNDTRIP_CASES:
     940              self.assertEqual(dedent(indent(text, '\t\t')), text)
     941  
     942      def test_roundtrip_mixed(self):
     943          # A whitespace prefix should roundtrip with dedent
     944          for text in self.ROUNDTRIP_CASES:
     945              self.assertEqual(dedent(indent(text, ' \t  \t ')), text)
     946  
     947      def test_indent_default(self):
     948          # Test default indenting of lines that are not whitespace only
     949          prefix = '  '
     950          expected = (
     951            # Basic test case
     952            "  Hi.\n  This is a test.\n  Testing.",
     953            # Include a blank line
     954            "  Hi.\n  This is a test.\n\n  Testing.",
     955            # Include leading and trailing blank lines
     956            "\n  Hi.\n  This is a test.\n  Testing.\n",
     957            # Use Windows line endings
     958            "  Hi.\r\n  This is a test.\r\n  Testing.\r\n",
     959            # Pathological case
     960            "\n  Hi.\r\n  This is a test.\n\r\n  Testing.\r\n\n",
     961          )
     962          for text, expect in zip(self.CASES, expected):
     963              self.assertEqual(indent(text, prefix), expect)
     964  
     965      def test_indent_explicit_default(self):
     966          # Test default indenting of lines that are not whitespace only
     967          prefix = '  '
     968          expected = (
     969            # Basic test case
     970            "  Hi.\n  This is a test.\n  Testing.",
     971            # Include a blank line
     972            "  Hi.\n  This is a test.\n\n  Testing.",
     973            # Include leading and trailing blank lines
     974            "\n  Hi.\n  This is a test.\n  Testing.\n",
     975            # Use Windows line endings
     976            "  Hi.\r\n  This is a test.\r\n  Testing.\r\n",
     977            # Pathological case
     978            "\n  Hi.\r\n  This is a test.\n\r\n  Testing.\r\n\n",
     979          )
     980          for text, expect in zip(self.CASES, expected):
     981              self.assertEqual(indent(text, prefix, None), expect)
     982  
     983      def test_indent_all_lines(self):
     984          # Add 'prefix' to all lines, including whitespace-only ones.
     985          prefix = '  '
     986          expected = (
     987            # Basic test case
     988            "  Hi.\n  This is a test.\n  Testing.",
     989            # Include a blank line
     990            "  Hi.\n  This is a test.\n  \n  Testing.",
     991            # Include leading and trailing blank lines
     992            "  \n  Hi.\n  This is a test.\n  Testing.\n",
     993            # Use Windows line endings
     994            "  Hi.\r\n  This is a test.\r\n  Testing.\r\n",
     995            # Pathological case
     996            "  \n  Hi.\r\n  This is a test.\n  \r\n  Testing.\r\n  \n",
     997          )
     998          predicate = lambda line: True
     999          for text, expect in zip(self.CASES, expected):
    1000              self.assertEqual(indent(text, prefix, predicate), expect)
    1001  
    1002      def test_indent_empty_lines(self):
    1003          # Add 'prefix' solely to whitespace-only lines.
    1004          prefix = '  '
    1005          expected = (
    1006            # Basic test case
    1007            "Hi.\nThis is a test.\nTesting.",
    1008            # Include a blank line
    1009            "Hi.\nThis is a test.\n  \nTesting.",
    1010            # Include leading and trailing blank lines
    1011            "  \nHi.\nThis is a test.\nTesting.\n",
    1012            # Use Windows line endings
    1013            "Hi.\r\nThis is a test.\r\nTesting.\r\n",
    1014            # Pathological case
    1015            "  \nHi.\r\nThis is a test.\n  \r\nTesting.\r\n  \n",
    1016          )
    1017          predicate = lambda line: not line.strip()
    1018          for text, expect in zip(self.CASES, expected):
    1019              self.assertEqual(indent(text, prefix, predicate), expect)
    1020  
    1021  
    1022  class ESC[4;38;5;81mShortenTestCase(ESC[4;38;5;149mBaseTestCase):
    1023  
    1024      def check_shorten(self, text, width, expect, **kwargs):
    1025          result = shorten(text, width, **kwargs)
    1026          self.check(result, expect)
    1027  
    1028      def test_simple(self):
    1029          # Simple case: just words, spaces, and a bit of punctuation
    1030          text = "Hello there, how are you this fine day? I'm glad to hear it!"
    1031  
    1032          self.check_shorten(text, 18, "Hello there, [...]")
    1033          self.check_shorten(text, len(text), text)
    1034          self.check_shorten(text, len(text) - 1,
    1035              "Hello there, how are you this fine day? "
    1036              "I'm glad to [...]")
    1037  
    1038      def test_placeholder(self):
    1039          text = "Hello there, how are you this fine day? I'm glad to hear it!"
    1040  
    1041          self.check_shorten(text, 17, "Hello there,$$", placeholder='$$')
    1042          self.check_shorten(text, 18, "Hello there, how$$", placeholder='$$')
    1043          self.check_shorten(text, 18, "Hello there, $$", placeholder=' $$')
    1044          self.check_shorten(text, len(text), text, placeholder='$$')
    1045          self.check_shorten(text, len(text) - 1,
    1046              "Hello there, how are you this fine day? "
    1047              "I'm glad to hear$$", placeholder='$$')
    1048  
    1049      def test_empty_string(self):
    1050          self.check_shorten("", 6, "")
    1051  
    1052      def test_whitespace(self):
    1053          # Whitespace collapsing
    1054          text = """
    1055              This is a  paragraph that  already has
    1056              line breaks and \t tabs too."""
    1057          self.check_shorten(text, 62,
    1058                               "This is a paragraph that already has line "
    1059                               "breaks and tabs too.")
    1060          self.check_shorten(text, 61,
    1061                               "This is a paragraph that already has line "
    1062                               "breaks and [...]")
    1063  
    1064          self.check_shorten("hello      world!  ", 12, "hello world!")
    1065          self.check_shorten("hello      world!  ", 11, "hello [...]")
    1066          # The leading space is trimmed from the placeholder
    1067          # (it would be ugly otherwise).
    1068          self.check_shorten("hello      world!  ", 10, "[...]")
    1069  
    1070      def test_width_too_small_for_placeholder(self):
    1071          shorten("x" * 20, width=8, placeholder="(......)")
    1072          with self.assertRaises(ValueError):
    1073              shorten("x" * 20, width=8, placeholder="(.......)")
    1074  
    1075      def test_first_word_too_long_but_placeholder_fits(self):
    1076          self.check_shorten("Helloo", 5, "[...]")
    1077  
    1078  
    1079  if __name__ == '__main__':
    1080      unittest.main()