1 # -*- test-case-name: twisted.test.test_text -*-
3 # Copyright (c) Twisted Matrix Laboratories.
4 # See LICENSE for details.
8 Miscellany of text-munging functions.
15 def stringyString(object, indentation=''):
17 Expansive string formatting for sequence types.
19 list.__str__ and dict.__str__ use repr() to display their
20 elements. This function also turns these sequence types
21 into strings, but uses str() on their elements instead.
23 Sequence elements are also displayed on seperate lines,
24 and nested sequences have nested indentation.
29 if type(object) is types.DictType:
31 for key, value in object.items():
32 value = stringyString(value, indentation + ' ')
33 if isMultiline(value):
34 if endsInNewline(value):
35 value = value[:-len('\n')]
36 sl.append("%s %s:\n%s" % (indentation, key, value))
38 # Oops. Will have to move that indentation.
39 sl.append("%s %s: %s" % (indentation, key,
40 value[len(indentation) + 3:]))
42 elif type(object) in (types.TupleType, types.ListType):
43 if type(object) is types.TupleType:
48 for element in object:
49 element = stringyString(element, indentation + ' ')
50 sl.append(string.rstrip(element) + ',')
52 sl[:] = map(lambda s, i=indentation: i+s,
53 string.split(str(object),'\n'))
56 sl.append(indentation)
59 sl[0] = indentation + braces[0] + sl[0][len(indentation) + 1:]
60 sl[-1] = sl[-1] + braces[-1]
62 s = string.join(sl, "\n")
64 if isMultiline(s) and not endsInNewline(s):
70 """Returns True if this string has a newline in it."""
71 return (string.find(s, '\n') != -1)
74 """Returns True if this string ends in a newline."""
75 return (s[-len('\n'):] == '\n')
78 def greedyWrap(inString, width=80):
79 """Given a string and a column width, return a list of lines.
81 Caveat: I'm use a stupid greedy word-wrapping
82 algorythm. I won't put two spaces at the end
83 of a sentence. I don't do full justification.
84 And no, I've never even *heard* of hypenation.
89 #eww, evil hacks to allow paragraphs delimited by two \ns :(
90 if inString.find('\n\n') >= 0:
91 paragraphs = string.split(inString, '\n\n')
92 for para in paragraphs:
93 outLines.extend(greedyWrap(para, width) + [''])
95 inWords = string.split(inString)
100 column = column + len(inWords[ptr_line])
101 ptr_line = ptr_line + 1
105 # This single word is too long, it will be the whole line.
108 # We've gone too far, stop the line one word back.
109 ptr_line = ptr_line - 1
110 (l, inWords) = (inWords[0:ptr_line], inWords[ptr_line:])
111 outLines.append(string.join(l,' '))
115 elif not (len(inWords) > ptr_line):
116 # Clean up the last bit.
117 outLines.append(string.join(inWords, ' '))
127 wordWrap = greedyWrap
129 def removeLeadingBlanks(lines):
132 if ret or line.strip():
136 def removeLeadingTrailingBlanks(s):
137 lines = removeLeadingBlanks(s.split('\n'))
139 lines = removeLeadingBlanks(lines)
141 return '\n'.join(lines)+'\n'
144 """Like string.split, but don't break substrings inside quotes.
146 >>> splitQuoted('the \"hairy monkey\" likes pie')
147 ['the', 'hairy monkey', 'likes', 'pie']
149 Another one of those \"someone must have a better solution for
150 this\" things. This implementation is a VERY DUMB hack done too
156 for word in s.split():
158 if word and (word[0] in ("\"", "'")):
166 if word and (word[-1] == quot):
169 out.append(" ".join(phrase))
176 def strFile(p, f, caseSensitive=True):
177 """Find whether string p occurs in a read()able object f
181 buf_len = max(len(p), 2**2**2**2)
182 if not caseSensitive:
185 r = f.read(buf_len-len(p))
186 if not caseSensitive:
191 l = len(buf)+bytes_read-buf_len
196 if buf.find(p) != -1: