utils.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. # File: utils.py
  2. # Library: DOPAL - DO Python Azureus Library
  3. #
  4. # This program is free software; you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation; version 2 of the License.
  7. #
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details ( see the COPYING file ).
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with this program; if not, write to the Free Software
  15. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. '''
  17. General utility functions.
  18. '''
  19. __pychecker__ = 'unusednames=metaclass_,attr_,no-unreachable'
  20. # Converts a binary string character into a hexidecimal equivalent.
  21. #
  22. # >>> byte_to_hex_form('N')
  23. # u'4E'
  24. def byte_to_hex_form(byte):
  25. # ord is used to convert the byte into it's numeric equivalent.
  26. # hex will turn the number into a string like "0x5" or "0xa3"
  27. # the slicing will chop off the "0x" prefix.
  28. # zfill will change any single digit values to have a leading 0 character.
  29. # upper will force all the A-F characters to be uppercase.
  30. return unicode(hex(ord(byte))[2:].zfill(2).upper())
  31. # Converts a binary string into a hexidecimal equivalent.
  32. #
  33. # >>> string_to_hex_form('xK3-')
  34. # u'784B332D'
  35. def string_to_hex_form(chars):
  36. if not chars:
  37. raise ValueError, "cannot convert empty string"
  38. return ''.join([byte_to_hex_form(char) for char in chars])
  39. # Converts a 2 character hexidecimal string into a binary character.
  40. #
  41. # >>> hex_form_to_byte(u'4E')
  42. # 'L'
  43. def hex_pair_to_byte(hex_pair):
  44. return chr(int(hex_pair.encode('utf-8'), 16))
  45. # Converts a hexidecimal string (a string containing only characters valid
  46. # in use for displaying a hexidecimal number, i.e. 0123456789ABCDEF) into
  47. # a binary string.
  48. #
  49. # >>> hex_string_to_binary(u'784B332D')
  50. # 'xK3-'
  51. def hex_string_to_binary(hex_string):
  52. if len(hex_string) % 2:
  53. raise ValueError, "string given has odd-number of characters, must be even"
  54. if not hex_string:
  55. raise ValueError, "cannot convert empty string"
  56. return ''.join([hex_pair_to_byte(hex_string[i:i+2]) for i in range(0, len(hex_string), 2)])
  57. def make_short_object_id(object_id):
  58. # Due to the way Azureus generates object ID's (it tends to be a
  59. # long integer which is then incremented for each object it
  60. # generates), we don't bother rendering the entire object ID
  61. # as it would be too long (it can be as long as 20 characters).
  62. #
  63. # So instead, we only use the last 6 digits - turning the ID into
  64. # hexidecimal. This gives us a range of 16**6 = 16.7 million. So
  65. # Azureus would have to generate more than 16 million objects
  66. # before it generates an ID which has the same short ID form as
  67. # another object.
  68. #
  69. # We show the short ID as a way of easily seeing whether two
  70. # objects represent the same remote object or not.
  71. hex_id = hex(object_id)
  72. if hex_id[-1] == 'L':
  73. hex_short_id = hex_id[-7:-1]
  74. else:
  75. hex_short_id = hex_id[-6:]
  76. return hex_short_id
  77. def parse_azureus_version_string(ver_string):
  78. ver_bits = ver_string.split('_', 2)
  79. if len(ver_bits) == 1:
  80. major_ver, minor_ver = ver_string, None
  81. else:
  82. major_ver, minor_ver = ver_bits
  83. ver_segments = [int(bit) for bit in major_ver.split('.')]
  84. if minor_ver:
  85. if minor_ver[0].lower() == 'b':
  86. ver_segments.append('b')
  87. try:
  88. beta_ver = int(minor_ver[1:])
  89. except ValueError:
  90. pass
  91. else:
  92. ver_segments.append(beta_ver)
  93. return tuple(ver_segments)
  94. #
  95. # I love this code. :)
  96. #
  97. # I might turn it into something more generic, and use it elsewhere..
  98. #
  99. # Would be nicer if there was a better API for doing this, but given the amount
  100. # of hackery that I'm doing right now, I won't complain. :)
  101. #
  102. # What a lot of effort just to act as if these methods were defined in the
  103. # class itself.
  104. import new
  105. class MethodFactory(object):
  106. def __init__(self, method_object):
  107. _codeobj = method_object.func_code
  108. code_arguments = [
  109. _codeobj.co_argcount, _codeobj.co_nlocals, _codeobj.co_stacksize,
  110. _codeobj.co_flags, _codeobj.co_code, _codeobj.co_consts,
  111. _codeobj.co_names, _codeobj.co_varnames, _codeobj.co_filename,
  112. _codeobj.co_name, _codeobj.co_firstlineno, _codeobj.co_lnotab,
  113. ]
  114. self.code_arguments = code_arguments
  115. def _build_function(self, name):
  116. code_args = self.code_arguments[:]
  117. code_args[9] = name
  118. # code_args[8] = <modulename>
  119. codeobj = new.code(*code_args)
  120. return new.function(codeobj, {'__funcname__': name, '__builtins__': __builtins__}, name)
  121. def make_instance_method(self, name, instanceobj):
  122. method = self._build_function(name)
  123. return new.instancemethod(method, instanceobj, type(instanceobj))
  124. def make_class_method(self, name, classobj):
  125. method = self._build_function(name)
  126. return new.instancemethod(method, None, classobj)
  127. def _not_implemented(self, *args, **kwargs):
  128. class_name = self.__class__.__name__
  129. funcname = __funcname__
  130. raise NotImplementedError, "%(class_name)s.%(funcname)s" % locals()
  131. not_implemented_factory = MethodFactory(_not_implemented)
  132. make_not_implemented_class_method = not_implemented_factory.make_class_method
  133. del _not_implemented, not_implemented_factory
  134. def handle_kwargs(kwargs, *required, **optional):
  135. result = {}
  136. result.update(optional)
  137. required_args = dict([(x, None) for x in required])
  138. for kwarg_key, kwarg_value in kwargs.iteritems():
  139. if optional.has_key(kwarg_key):
  140. pass
  141. else:
  142. try:
  143. required_args.pop(kwarg_key)
  144. except KeyError:
  145. raise TypeError, "unexpected keyword argument: %r" % kwarg_key
  146. result[kwarg_key] = kwarg_value
  147. if required_args:
  148. missing_key = required_args.popitem()[0]
  149. raise TypeError, "missing keyword argument: %r" % missing_key
  150. return result
  151. class Sentinel(object):
  152. def __init__(self, value):
  153. self.value = value
  154. def __str__(self):
  155. return str(self.value)
  156. def __repr__(self):
  157. return '<sentinel object (%r) at 0x%08X>' % (self.value, id(self))