| Total Complexity | 60 | 
| Total Lines | 354 | 
| Duplicated Lines | 6.21 % | 
| Changes | 0 | ||
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like ige.IMarshal often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
| 1 | # | ||
| 2 | # Copyright 2001 - 2016 Ludek Smid [http://www.ospace.net/] | ||
| 3 | # | ||
| 4 | # This file is part of Outer Space. | ||
| 5 | # | ||
| 6 | # Outer Space is free software; you can redistribute it and/or modify | ||
| 7 | # it under the terms of the GNU General Public License as published by | ||
| 8 | # the Free Software Foundation; either version 2 of the License, or | ||
| 9 | # (at your option) any later version. | ||
| 10 | # | ||
| 11 | # Outer Space is distributed in the hope that it will be useful, | ||
| 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | # GNU General Public License for more details. | ||
| 15 | # | ||
| 16 | # You should have received a copy of the GNU General Public License | ||
| 17 | # along with Outer Space; if not, write to the Free Software | ||
| 18 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 19 | # | ||
| 20 | import types | ||
| 21 | |||
| 22 | from IDataHolder import IDataHolder | ||
| 23 | import zlib, string | ||
| 24 | |||
| 25 | __all__ = ('EncodeException', 'DecodeException', 'IPacket', 'IMarshal') | ||
| 26 | |||
| 27 | class EncodeException(Exception): | ||
| 28 | pass | ||
| 29 | |||
| 30 | class DecodeException(Exception): | ||
| 31 | pass | ||
| 32 | |||
| 33 | class IPacket: | ||
| 34 | |||
| 35 | def __init__(self): | ||
| 36 | self.sid = None | ||
| 37 | self.method = None | ||
| 38 | self.params = None | ||
| 39 | self.result = None | ||
| 40 | self.messages = None | ||
| 41 | self.exception = None | ||
| 42 | self.clientAddr = None | ||
| 43 | |||
| 44 | def __repr__(self): | ||
| 45 | result = '<%s.%s %d ' % (self.__class__.__module__, self.__class__.__name__, id(self)) | ||
| 46 | for key, value in self.__dict__.items(): | ||
| 47 | result += '%s=%s, ' % (key, repr(value)) | ||
| 48 | result += '>' | ||
| 49 | return result | ||
| 50 | |||
| 51 | class IMarshal: | ||
| 52 | |||
| 53 | def __init__(self): | ||
| 54 | pass | ||
| 55 | |||
| 56 | def encode(self, data, version = "V20"): | ||
| 57 | if version == "V11": | ||
| 58 | return u'V11%s' % string.join(self.__encodeV11(data), '') | ||
| 59 | # Profiling code TODO remove profiling code | ||
| 60 | data = u'V11%s' % self.__encodeV11(data) | ||
| 61 | stats.encBytes += len(data) | ||
| 62 | # TODO remove zlib profiling | ||
| 63 | stats.zipBytes += len(zlib.compress(data)) | ||
| 64 | # End of profiling code | ||
| 65 | return data | ||
| 66 | elif version == "V20": | ||
| 67 | return "V20%s" % zlib.compress(pickle.dumps(data, 1)) | ||
| 68 | else: | ||
| 69 |             raise EncodeException("Cannot handle version %s." % version) | ||
| 70 | |||
| 71 | def __encodeV11(self, data, isKey = 0): | ||
| 72 | t = type(data) | ||
| 73 | if t == types.NoneType: | ||
| 74 | return [u'|N'] | ||
| 75 | elif t == types.IntType: | ||
| 76 | return [u'|I%d' % (data,)] | ||
| 77 | elif t == types.LongType: | ||
| 78 | return [u'|G%d' % (data,)] | ||
| 79 | elif t == types.FloatType: | ||
| 80 | return [u'|F%.2f' % (data,)] | ||
| 81 | elif t == types.StringType: | ||
| 82 | # Profiling code TODO comment this code | ||
| 83 | #if isKey: | ||
| 84 | # global stats | ||
| 85 | # stats.data[data] = stats.data.get(data, 0) + 1 | ||
| 86 | # stats.total += 1 | ||
| 87 | # stats.totalBytes += len(data) | ||
| 88 | # if stats.total % 100 == 0: | ||
| 89 | # saveStats() | ||
| 90 | # if compress.has_key(data): | ||
| 91 | # stats.hits += 1 | ||
| 92 | # stats.savedBytes += len(data) - len(compress[data]) | ||
| 93 | # End of profiling code | ||
| 94 | if compress.has_key(data): | ||
| 95 | return [u'|C%s' % (compress[data])] | ||
| 96 |             return [u'|S%s' % (data.replace('\\','\\\\').replace('|', '\\/').replace('\n', '\\n').replace('\r', '\\r'),)] | ||
| 97 | elif t == types.UnicodeType: | ||
| 98 |             return [u'|U%s' % (data.replace('\\','\\\\').replace('|', '\\/').replace('\n', '\\n').replace('\r', '\\r'),)] | ||
| 99 | elif t == types.ListType: | ||
| 100 | result = [u'|L'] | ||
| 101 | for item in data: | ||
| 102 | result.extend(self.__encodeV11(item)) | ||
| 103 | result.append(u'|E') | ||
| 104 | return result | ||
| 105 | elif t == types.TupleType: | ||
| 106 | result = [u'|T'] | ||
| 107 | for item in data: | ||
| 108 | result.extend(self.__encodeV11(item)) | ||
| 109 | result.append(u'|E') | ||
| 110 | return result | ||
| 111 | elif t == types.DictType: | ||
| 112 | result = [u'|D'] | ||
| 113 | for item in data.keys(): | ||
| 114 | result.extend(self.__encodeV11(item, 1)) | ||
| 115 | result.extend(self.__encodeV11(data[item])) | ||
| 116 | result.append(u'|E') | ||
| 117 | return result | ||
| 118 | elif t == types.InstanceType: | ||
| 119 | result = [u'|O'] | ||
| 120 |             result.extend(self.__encodeV11('%s.%s' % (data.__class__.__module__, data.__class__.__name__), 1)) | ||
| 121 | for item in data.__dict__.keys(): | ||
| 122 | result.extend(self.__encodeV11(item, 1)) | ||
| 123 | result.extend(self.__encodeV11(data.__dict__[item])) | ||
| 124 | result.append(u'|E') | ||
| 125 | return result | ||
| 126 | else: | ||
| 127 |             raise EncodeException('Cannot handle ' + repr(t)) | ||
| 128 | |||
| 129 | def decode(self, str): | ||
| 130 | prefix = str[:3] | ||
| 131 | if prefix == u'V11': | ||
| 132 | self.index = 0 | ||
| 133 |             list = str.split('|') | ||
| 134 | self.list = list[1:] | ||
| 135 | data = self.__decodeV11() | ||
| 136 | elif prefix == u'V20': | ||
| 137 | data = pickle.loads(zlib.decompress(str[3:])) | ||
| 138 | else: | ||
| 139 |             raise DecodeException('Cannot handle version %s [message: %s]' % (prefix, str)) | ||
| 140 | return data | ||
| 141 | |||
| 142 | def __decodeV11(self): | ||
| 143 | item = self.list[self.index] | ||
| 144 | self.index += 1 | ||
| 145 | #@print self.index, | ||
| 146 | t = item[0] | ||
| 147 | if t == u'N': | ||
| 148 | #@print 'None' | ||
| 149 | return None | ||
| 150 | if t == u'I': | ||
| 151 | return int(item[1:]) | ||
| 152 | if t == u'G': | ||
| 153 | return long(item[1:]) | ||
|  | |||
| 154 | if t == u'F': | ||
| 155 | return float(item[1:]) | ||
| 156 | elif t == u'S': | ||
| 157 | #@print 'String', item[1:] | ||
| 158 | str = item[1:] | ||
| 159 |             i = str.find('\\') | ||
| 160 | index = 0 | ||
| 161 | result = '' | ||
| 162 | View Code Duplication | while i > -1: | |
| 163 | #@print str, i, | ||
| 164 | result += str[index:i] | ||
| 165 | index = i + 2 | ||
| 166 | c = str[i + 1] | ||
| 167 | if c == 'n': result += '\n' | ||
| 168 | elif c == 'r': result += '\r' | ||
| 169 | elif c == '/': result += '|' | ||
| 170 | else: result += c | ||
| 171 | #@print repr(result) | ||
| 172 |                 i = str.find('\\', i + 2) | ||
| 173 | result += str[index:] | ||
| 174 |             return result.encode('ascii') | ||
| 175 | elif t == u'U': | ||
| 176 | #@print 'String', item[1:] | ||
| 177 | str = item[1:] | ||
| 178 |             i = str.find('\\') | ||
| 179 | index = 0 | ||
| 180 | result = '' | ||
| 181 | View Code Duplication | while i > -1: | |
| 182 | #@print str, i, | ||
| 183 | result += str[index:i] | ||
| 184 | index = i + 2 | ||
| 185 | c = str[i + 1] | ||
| 186 | if c == 'n': result += '\n' | ||
| 187 | elif c == 'r': result += '\r' | ||
| 188 | elif c == '/': result += '|' | ||
| 189 | else: result += c | ||
| 190 | #@print repr(result) | ||
| 191 |                 i = str.find('\\', i + 2) | ||
| 192 | result += str[index:] | ||
| 193 | return result | ||
| 194 | elif t == u'C': | ||
| 195 | return decompress[item[1:]] | ||
| 196 | elif t == u'T': | ||
| 197 | #@print 'Tuple' | ||
| 198 | result = [] | ||
| 199 | while self.list[self.index][0] != 'E': | ||
| 200 | result.append(self.__decodeV11()) | ||
| 201 | self.index += 1 | ||
| 202 | #@print ':=', result | ||
| 203 | return tuple(result) | ||
| 204 | elif t == u'L': | ||
| 205 | #@print 'Tuple' | ||
| 206 | result = [] | ||
| 207 | while self.list[self.index][0] != 'E': | ||
| 208 | result.append(self.__decodeV11()) | ||
| 209 | self.index += 1 | ||
| 210 | #@print ':=', result | ||
| 211 | return result | ||
| 212 | elif t == u'D': | ||
| 213 | #@print 'Dict' | ||
| 214 |             result = {} | ||
| 215 | while self.list[self.index][0] != 'E': | ||
| 216 | key = self.__decodeV11() | ||
| 217 | value = self.__decodeV11() | ||
| 218 | result[key] = value | ||
| 219 | self.index += 1 | ||
| 220 | #@print ':=', result | ||
| 221 | return result | ||
| 222 | elif t == u'O': | ||
| 223 | #@print 'Dict' | ||
| 224 | str = self.__decodeV11() | ||
| 225 | if str == 'ige.IMarshal.IPacket': | ||
| 226 | result = IPacket() | ||
| 227 | elif str == 'ige.IDataHolder.IDataHolder': | ||
| 228 | result = IDataHolder() | ||
| 229 | else: | ||
| 230 |                 DecodeException('Unsupported class %s' % str) | ||
| 231 | while self.list[self.index][0] != 'E': | ||
| 232 | key = self.__decodeV11() | ||
| 233 | value = self.__decodeV11() | ||
| 234 | setattr(result, key ,value) | ||
| 235 | self.index += 1 | ||
| 236 | #@print ':=', result | ||
| 237 | return result | ||
| 238 | else: | ||
| 239 |             raise DecodeException('Cannot handle type <%s>' % t) | ||
| 240 | |||
| 241 | # (de)compress dictionary | ||
| 242 | #try: | ||
| 243 | # from ICompressScheme import compress | ||
| 244 | #except ImportError: | ||
| 245 | compress = {} | ||
| 246 | |||
| 247 | decompress = {} | ||
| 248 | |||
| 249 | for key, value in compress.items(): | ||
| 250 | decompress[str(value)] = key | ||
| 251 | |||
| 252 | # statistics | ||
| 253 | import cPickle as pickle | ||
| 254 | |||
| 255 | class Stats: | ||
| 256 | def __init__(self): | ||
| 257 |         self.data = {} | ||
| 258 | self.total = 0 | ||
| 259 | self.hits = 0 | ||
| 260 | self.totalBytes = 0 | ||
| 261 | self.savedBytes = 0 | ||
| 262 | self.encBytes = 0 | ||
| 263 | self.zipBytes = 0 | ||
| 264 | |||
| 265 | # load stats TODO remove profiling code | ||
| 266 | # TODO change dir according to config file | ||
| 267 | try: | ||
| 268 |     fh = open('var/marshal.stats.data', 'rb') | ||
| 269 | stats = pickle.load(fh) | ||
| 270 | fh.close() | ||
| 271 | except IOError, e: | ||
| 272 | stats = Stats() | ||
| 273 | except EOFError, e: | ||
| 274 | stats = Stats() | ||
| 275 | |||
| 276 | def saveStats(directory): | ||
| 277 | print 'Saving IMarshal statistics' | ||
| 278 | # stats | ||
| 279 | fh = open(os.path.join(directory, 'marshal.stats.data'), 'wb') | ||
| 280 | pickle.dump(stats, fh, 1) | ||
| 281 | fh.close() | ||
| 282 | # various data | ||
| 283 | keys = [] | ||
| 284 | for key in stats.data.keys(): | ||
| 285 | keys.append((len(key) * stats.data[key],key)) | ||
| 286 | keys.sort() | ||
| 287 | keys.reverse() | ||
| 288 | fstats = open(os.path.join(directory, 'marshal.stats'), 'w') | ||
| 289 | fscheme = open(os.path.join(directory, 'marshal.cscheme'), 'w') | ||
| 290 | fpysrc = open(os.path.join(directory, 'marshal.cscheme.py'), 'w') | ||
| 291 |     print >> fpysrc, 'compress = {' | ||
| 292 | print >> fstats, '# Summary' | ||
| 293 | print >> fstats, '# Total strings:', stats.total | ||
| 294 | print >> fstats, '# Compressed strings:', stats.hits | ||
| 295 | print >> fstats, '# Uncompressed strings:', stats.total - stats.hits | ||
| 296 | print >> fstats, '# Ratio:', stats.hits / stats.total * 100L, '%' | ||
| 297 | print >> fstats, '# Uncompressed size:', stats.totalBytes | ||
| 298 | print >> fstats, '# Compressed size:', stats.totalBytes - stats.savedBytes | ||
| 299 | print >> fstats, '# Saved bytes:', stats.savedBytes | ||
| 300 | print >> fstats, '# Ratio:', stats.savedBytes / stats.totalBytes * 100L, '%' | ||
| 301 | print >> fstats, '# Encoded pckt bytes total:', stats.encBytes | ||
| 302 | print >> fstats, '# Encoded pckt bytes total (no compression, est.):', stats.encBytes + stats.savedBytes | ||
| 303 | print >> fstats, '# Ratio:', stats.encBytes / (stats.encBytes + stats.savedBytes) * 100L, '%' | ||
| 304 | print >> fstats, '# Encoded pckt bytes total (zipped):', stats.zipBytes | ||
| 305 | print >> fstats, '# Ratio (to compressed):', stats.zipBytes / stats.encBytes * 100L, '%' | ||
| 306 | print >> fstats, '# Ratio (to uncompressed):', stats.zipBytes / (stats.encBytes + stats.savedBytes)* 100L , '%' | ||
| 307 | print >> fstats, '# total bytes,number of items,string' | ||
| 308 | index = 0 | ||
| 309 | for key in keys: | ||
| 310 | count, name = key | ||
| 311 | print >> fstats, '%d,%d,%s' % (count, stats.data[name], name) | ||
| 312 | code = makeCode(index) | ||
| 313 | # include in scheme when there is save in bytes | ||
| 314 | if len(code) < len(name): | ||
| 315 | print >> fscheme, code, name | ||
| 316 | print >> fpysrc, " '%s' : '%s'," % (name, code) | ||
| 317 | index += 1 | ||
| 318 | print >>fpysrc, '}' | ||
| 319 | fstats.close() | ||
| 320 | fscheme.close() | ||
| 321 | fpysrc.close() | ||
| 322 | |||
| 323 | codeChars = '0123456789abcdefghjiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' | ||
| 324 | |||
| 325 | def makeCode(index): | ||
| 326 | if index < len(codeChars): | ||
| 327 | return codeChars[index] | ||
| 328 | elif index / len(codeChars) - 1 < len(codeChars): | ||
| 329 | return '%s%s' % ( | ||
| 330 | codeChars[index / len(codeChars) - 1], | ||
| 331 | codeChars[index % len(codeChars)] | ||
| 332 | ) | ||
| 333 | else: | ||
| 334 | return None | ||
| 335 | |||
| 336 | # testing only | ||
| 337 | if __name__ == '__main__': | ||
| 338 | |||
| 339 | packet = IPacket() | ||
| 340 | packet.sid = '0123456789' | ||
| 341 | packet.method = 'test' | ||
| 342 |     packet.params = { 'name':u'Corvus', 'componentOf':1001, 'rules':[1,2], 'isA': (1,2) } | ||
| 343 | marshal = IMarshal() | ||
| 344 | |||
| 345 | str = marshal.encode(packet) | ||
| 346 | |||
| 347 | print repr(str) | ||
| 348 | print len(str) | ||
| 349 | packet = marshal.decode(str) | ||
| 350 | print packet.params | ||
| 351 | |||
| 352 | import pprint | ||
| 353 | pprint.pprint(packet.params) | ||
| 354 | |||
| 355 |