|
1 #!/usr/bin/env python |
|
2 ## vim:ts=4:et:nowrap |
|
3 """A user-defined wrapper around string objects |
|
4 |
|
5 Note: string objects have grown methods in Python 1.6 |
|
6 This module requires Python 1.6 or later. |
|
7 """ |
|
8 import sys |
|
9 import collections |
|
10 |
|
11 __all__ = ["UserString","MutableString"] |
|
12 |
|
13 class UserString(collections.Sequence): |
|
14 def __init__(self, seq): |
|
15 if isinstance(seq, basestring): |
|
16 self.data = seq |
|
17 elif isinstance(seq, UserString): |
|
18 self.data = seq.data[:] |
|
19 else: |
|
20 self.data = str(seq) |
|
21 def __str__(self): return str(self.data) |
|
22 def __repr__(self): return repr(self.data) |
|
23 def __int__(self): return int(self.data) |
|
24 def __long__(self): return long(self.data) |
|
25 def __float__(self): return float(self.data) |
|
26 def __complex__(self): return complex(self.data) |
|
27 def __hash__(self): return hash(self.data) |
|
28 |
|
29 def __cmp__(self, string): |
|
30 if isinstance(string, UserString): |
|
31 return cmp(self.data, string.data) |
|
32 else: |
|
33 return cmp(self.data, string) |
|
34 def __contains__(self, char): |
|
35 return char in self.data |
|
36 |
|
37 def __len__(self): return len(self.data) |
|
38 def __getitem__(self, index): return self.__class__(self.data[index]) |
|
39 def __getslice__(self, start, end): |
|
40 start = max(start, 0); end = max(end, 0) |
|
41 return self.__class__(self.data[start:end]) |
|
42 |
|
43 def __add__(self, other): |
|
44 if isinstance(other, UserString): |
|
45 return self.__class__(self.data + other.data) |
|
46 elif isinstance(other, basestring): |
|
47 return self.__class__(self.data + other) |
|
48 else: |
|
49 return self.__class__(self.data + str(other)) |
|
50 def __radd__(self, other): |
|
51 if isinstance(other, basestring): |
|
52 return self.__class__(other + self.data) |
|
53 else: |
|
54 return self.__class__(str(other) + self.data) |
|
55 def __mul__(self, n): |
|
56 return self.__class__(self.data*n) |
|
57 __rmul__ = __mul__ |
|
58 def __mod__(self, args): |
|
59 return self.__class__(self.data % args) |
|
60 |
|
61 # the following methods are defined in alphabetical order: |
|
62 def capitalize(self): return self.__class__(self.data.capitalize()) |
|
63 def center(self, width, *args): |
|
64 return self.__class__(self.data.center(width, *args)) |
|
65 def count(self, sub, start=0, end=sys.maxint): |
|
66 return self.data.count(sub, start, end) |
|
67 def decode(self, encoding=None, errors=None): # XXX improve this? |
|
68 if encoding: |
|
69 if errors: |
|
70 return self.__class__(self.data.decode(encoding, errors)) |
|
71 else: |
|
72 return self.__class__(self.data.decode(encoding)) |
|
73 else: |
|
74 return self.__class__(self.data.decode()) |
|
75 def encode(self, encoding=None, errors=None): # XXX improve this? |
|
76 if encoding: |
|
77 if errors: |
|
78 return self.__class__(self.data.encode(encoding, errors)) |
|
79 else: |
|
80 return self.__class__(self.data.encode(encoding)) |
|
81 else: |
|
82 return self.__class__(self.data.encode()) |
|
83 def endswith(self, suffix, start=0, end=sys.maxint): |
|
84 return self.data.endswith(suffix, start, end) |
|
85 def expandtabs(self, tabsize=8): |
|
86 return self.__class__(self.data.expandtabs(tabsize)) |
|
87 def find(self, sub, start=0, end=sys.maxint): |
|
88 return self.data.find(sub, start, end) |
|
89 def index(self, sub, start=0, end=sys.maxint): |
|
90 return self.data.index(sub, start, end) |
|
91 def isalpha(self): return self.data.isalpha() |
|
92 def isalnum(self): return self.data.isalnum() |
|
93 def isdecimal(self): return self.data.isdecimal() |
|
94 def isdigit(self): return self.data.isdigit() |
|
95 def islower(self): return self.data.islower() |
|
96 def isnumeric(self): return self.data.isnumeric() |
|
97 def isspace(self): return self.data.isspace() |
|
98 def istitle(self): return self.data.istitle() |
|
99 def isupper(self): return self.data.isupper() |
|
100 def join(self, seq): return self.data.join(seq) |
|
101 def ljust(self, width, *args): |
|
102 return self.__class__(self.data.ljust(width, *args)) |
|
103 def lower(self): return self.__class__(self.data.lower()) |
|
104 def lstrip(self, chars=None): return self.__class__(self.data.lstrip(chars)) |
|
105 def partition(self, sep): |
|
106 return self.data.partition(sep) |
|
107 def replace(self, old, new, maxsplit=-1): |
|
108 return self.__class__(self.data.replace(old, new, maxsplit)) |
|
109 def rfind(self, sub, start=0, end=sys.maxint): |
|
110 return self.data.rfind(sub, start, end) |
|
111 def rindex(self, sub, start=0, end=sys.maxint): |
|
112 return self.data.rindex(sub, start, end) |
|
113 def rjust(self, width, *args): |
|
114 return self.__class__(self.data.rjust(width, *args)) |
|
115 def rpartition(self, sep): |
|
116 return self.data.rpartition(sep) |
|
117 def rstrip(self, chars=None): return self.__class__(self.data.rstrip(chars)) |
|
118 def split(self, sep=None, maxsplit=-1): |
|
119 return self.data.split(sep, maxsplit) |
|
120 def rsplit(self, sep=None, maxsplit=-1): |
|
121 return self.data.rsplit(sep, maxsplit) |
|
122 def splitlines(self, keepends=0): return self.data.splitlines(keepends) |
|
123 def startswith(self, prefix, start=0, end=sys.maxint): |
|
124 return self.data.startswith(prefix, start, end) |
|
125 def strip(self, chars=None): return self.__class__(self.data.strip(chars)) |
|
126 def swapcase(self): return self.__class__(self.data.swapcase()) |
|
127 def title(self): return self.__class__(self.data.title()) |
|
128 def translate(self, *args): |
|
129 return self.__class__(self.data.translate(*args)) |
|
130 def upper(self): return self.__class__(self.data.upper()) |
|
131 def zfill(self, width): return self.__class__(self.data.zfill(width)) |
|
132 |
|
133 class MutableString(UserString, collections.MutableSequence): |
|
134 """mutable string objects |
|
135 |
|
136 Python strings are immutable objects. This has the advantage, that |
|
137 strings may be used as dictionary keys. If this property isn't needed |
|
138 and you insist on changing string values in place instead, you may cheat |
|
139 and use MutableString. |
|
140 |
|
141 But the purpose of this class is an educational one: to prevent |
|
142 people from inventing their own mutable string class derived |
|
143 from UserString and than forget thereby to remove (override) the |
|
144 __hash__ method inherited from UserString. This would lead to |
|
145 errors that would be very hard to track down. |
|
146 |
|
147 A faster and better solution is to rewrite your program using lists.""" |
|
148 def __init__(self, string=""): |
|
149 from warnings import warnpy3k |
|
150 warnpy3k('the class UserString.MutableString has been removed in ' |
|
151 'Python 3.0', stacklevel=2) |
|
152 self.data = string |
|
153 |
|
154 # We inherit object.__hash__, so we must deny this explicitly |
|
155 __hash__ = None |
|
156 |
|
157 def __setitem__(self, index, sub): |
|
158 if isinstance(index, slice): |
|
159 if isinstance(sub, UserString): |
|
160 sub = sub.data |
|
161 elif not isinstance(sub, basestring): |
|
162 sub = str(sub) |
|
163 start, stop, step = index.indices(len(self.data)) |
|
164 if step == -1: |
|
165 start, stop = stop+1, start+1 |
|
166 sub = sub[::-1] |
|
167 elif step != 1: |
|
168 # XXX(twouters): I guess we should be reimplementing |
|
169 # the extended slice assignment/deletion algorithm here... |
|
170 raise TypeError, "invalid step in slicing assignment" |
|
171 start = min(start, stop) |
|
172 self.data = self.data[:start] + sub + self.data[stop:] |
|
173 else: |
|
174 if index < 0: |
|
175 index += len(self.data) |
|
176 if index < 0 or index >= len(self.data): raise IndexError |
|
177 self.data = self.data[:index] + sub + self.data[index+1:] |
|
178 def __delitem__(self, index): |
|
179 if isinstance(index, slice): |
|
180 start, stop, step = index.indices(len(self.data)) |
|
181 if step == -1: |
|
182 start, stop = stop+1, start+1 |
|
183 elif step != 1: |
|
184 # XXX(twouters): see same block in __setitem__ |
|
185 raise TypeError, "invalid step in slicing deletion" |
|
186 start = min(start, stop) |
|
187 self.data = self.data[:start] + self.data[stop:] |
|
188 else: |
|
189 if index < 0: |
|
190 index += len(self.data) |
|
191 if index < 0 or index >= len(self.data): raise IndexError |
|
192 self.data = self.data[:index] + self.data[index+1:] |
|
193 def __setslice__(self, start, end, sub): |
|
194 start = max(start, 0); end = max(end, 0) |
|
195 if isinstance(sub, UserString): |
|
196 self.data = self.data[:start]+sub.data+self.data[end:] |
|
197 elif isinstance(sub, basestring): |
|
198 self.data = self.data[:start]+sub+self.data[end:] |
|
199 else: |
|
200 self.data = self.data[:start]+str(sub)+self.data[end:] |
|
201 def __delslice__(self, start, end): |
|
202 start = max(start, 0); end = max(end, 0) |
|
203 self.data = self.data[:start] + self.data[end:] |
|
204 def immutable(self): |
|
205 return UserString(self.data) |
|
206 def __iadd__(self, other): |
|
207 if isinstance(other, UserString): |
|
208 self.data += other.data |
|
209 elif isinstance(other, basestring): |
|
210 self.data += other |
|
211 else: |
|
212 self.data += str(other) |
|
213 return self |
|
214 def __imul__(self, n): |
|
215 self.data *= n |
|
216 return self |
|
217 def insert(self, index, value): |
|
218 self[index:index] = value |
|
219 |
|
220 if __name__ == "__main__": |
|
221 # execute the regression test to stdout, if called as a script: |
|
222 import os |
|
223 called_in_dir, called_as = os.path.split(sys.argv[0]) |
|
224 called_as, py = os.path.splitext(called_as) |
|
225 if '-q' in sys.argv: |
|
226 from test import test_support |
|
227 test_support.verbose = 0 |
|
228 __import__('test.test_' + called_as.lower()) |