| 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 |