Package netaddr :: Module strategy |
[hide private]
[frames] | no frames]

# Source Code for Module netaddr.strategy

      1  #!/usr/bin/env python
      2  #-----------------------------------------------------------------------------
      3  #   Copyright (c) 2008-2009, David P. D. Moss. All rights reserved.
      4  #
      5  #   Released under the BSD license. See the LICENSE file for details.
      6  #-----------------------------------------------------------------------------
      7  """
      8  network address conversion logic, constants and shared strategy objects.
      9  """
     10  import struct as _struct
     11  import re as _re
     12  from netaddr.util import BYTES_TO_BITS as _BYTES_TO_BITS
     13
     14  #   Check whether we need to use fallback code or not.
     15  try:
     16      import socket as _socket
     17      #   Check for a common bug on Windows and some other socket modules.
     18      _socket.inet_aton('255.255.255.255')
     19      from socket import inet_aton as _inet_aton, \
     20                         inet_ntoa as _inet_ntoa, \
     21                         AF_INET as _AF_INET
     22  except:
     23      from netaddr.fallback import inet_aton as _inet_aton, \
     24                                   inet_ntoa as _inet_ntoa, \
     25                                   AF_INET as _AF_INET
     26  try:
     27      import socket as _socket
     28      #   These might all generate exceptions on different platforms.
     29      _socket.inet_pton
     30      _socket.AF_INET6
     31      from socket import inet_pton as _inet_pton, \
     32                         inet_ntop as _inet_ntop, \
     33                         AF_INET6 as _AF_INET6
     34  except:
     35      from netaddr.fallback import inet_pton as _inet_pton, \
     36                                   inet_ntop as _inet_ntop, \
     37                                   AF_INET6 as _AF_INET6
     38
     39  from netaddr import BIG_ENDIAN_PLATFORM, AT_UNSPEC, AT_INET, AT_INET6, \
     40                      AT_LINK, AT_EUI64, AT_NAMES, AddrFormatError
     41
     42  #-----------------------------------------------------------------------------
     43 -class AddrStrategy(object):
     44      """Basic support for common operations performed on each address type"""
     45
     46      #: Lookup table for struct module format strings.
     47      STRUCT_FORMATS = {
     48           8 : 'B',   # unsigned char
     49          16 : 'H',   # unsigned short
     50          32 : 'I',   # unsigned int
     51      }
     52
     53 -    def __init__(self, width, word_size, word_sep, word_fmt='%x',
     54                   addr_type=AT_UNSPEC, word_base=16):
     55          """
     56          Constructor.
     57
     58          @param width: size of address in bits.
     59              (e.g. 32 - IPv4, 48 - MAC, 128 - IPv6)
     60
     61          @param word_size: size of each word.
     62              (e.g. 8 - octets, 16 - hextets)
     63
     64          @param word_sep: separator between each word.
     65              (e.g. '.' - IPv4, ':' - IPv6, '-' - EUI-48)
     66
     67          @param word_fmt: format string for each word.
     68              (Default: '%x')
     69
     70          @param addr_type: address type.
     71              (Default: AT_UNSPEC)
     72
     73          @param word_base: number base used to convert each word using int().
     74              (Default: 16)
     75          """
     76
     77          self.width = width
     78          self.max_int = 2 ** width - 1
     79          self.word_size = word_size
     80          self.num_words = width / word_size
     81          self.max_word = 2 ** word_size - 1
     82          self.word_sep = word_sep
     83          self.word_fmt  = word_fmt
     84          self.word_base = word_base
     85          self.addr_type = addr_type
     86
     87          try:
     88              self.name = AT_NAMES[addr_type]
     89          except KeyError:
     90              self.name = AT_NAMES[AT_UNSPEC]
     91
     92 -    def __repr__(self):
     93          """@return: executable Python string to recreate equivalent object"""
     94          return "%s(%r, %r, %r, %r, %r, %r)" %  (self.__class__.__name__,
     95              self.width, self.word_size, self.word_sep, self.addr_type,
     96              self.word_base)
     97
     98      #-------------------------------------------------------------------------
     99      #   Binary methods.
    100      #-------------------------------------------------------------------------
    101
    102 -    def valid_bits(self, bits):
    103          """
    104          @param bits: A network address in readable binary form.
    105
    106          @return: C{True} if network address is valid for this address type,
    107              C{False} otherwise.
    108          """
    109          if not isinstance(bits, (str, unicode)):
    110              return False
    111
    112          bits = bits.replace(self.word_sep, '')
    113
    114          if len(bits) != self.width:
    115              return False
    116
    117          try:
    118              if 0 <= int(bits, 2) <= self.max_int:
    119                  return True
    120          except ValueError:
    121              return False
    122          return False
    123
    124 -    def bits_to_int(self, bits):
    125          """
    126          @param bits: A network address in readable binary form.
    127
    128          @return: An unsigned integer that is equivalent to value represented
    129              by network address in readable binary form.
    130          """
    131          if not self.valid_bits(bits):
    132              raise ValueError('%r is not a valid binary form string for ' \
    133                  'address type!' % bits)
    134
    135          return int(bits.replace(self.word_sep, ''), 2)
    136
    137      #-------------------------------------------------------------------------
    138      #   Integer methods.
    139      #-------------------------------------------------------------------------
    140
    141 -    def valid_int(self, int_val):
    142          """
    143          @param int_val: An unsigned integer.
    144
    145          @return: C{True} if integer falls within the boundaries of this
    146              address type, C{False} otherwise.
    147          """
    148          if not isinstance(int_val, (int, long)):
    149              return False
    150
    151          return 0 <= int_val <= self.max_int
    152
    153 -    def int_to_str(self, int_val):
    154          """
    155          @param int_val: An unsigned integer.
    156
    157          @return: A network address in string form that is equivalent to value
    158              represented by an unsigned integer.
    159          """
    160          words = self.int_to_words(int_val)
    161          tokens = [self.word_fmt % i for i in words]
    162          addr = self.word_sep.join(tokens)
    163
    164          return addr
    165
    166 -    def int_to_bits(self, int_val, word_sep=None):
    167          """
    168          @param int_val: An unsigned integer.
    169
    170          @param word_sep: (optional) the separator to insert between words.
    171              Default: None - use default separator for address type.
    172
    173          @return: A network address in readable binary form that is equivalent
    174              to value represented by an unsigned integer.
    175          """
    176          bit_words = []
    177
    178          for word in self.int_to_words(int_val):
    179              bits = []
    180              while word:
    181                  bits.append(_BYTES_TO_BITS[word&255])
    182                  word >>= 8
    183              bits.reverse()
    184              bit_str = ''.join(bits) or '0'*self.word_size
    185              bits = ('0'*self.word_size+bit_str)[-self.word_size:]
    186              bit_words.append(bits)
    187
    188          if word_sep is not None:
    189              #   Custom separator.
    190              if not hasattr(word_sep, 'join'):
    191                  raise ValueError('Word separator must be a string!')
    192              return word_sep.join(bit_words)
    193
    194          #   Default separator.
    195          return self.word_sep.join(bit_words)
    196
    197 -    def int_to_bin(self, int_val):
    198          """
    199          @param int_val: An unsigned integer.
    200
    201          @return: A network address in standard binary representation format
    202              that is equivalent to integer address value. Essentially a back
    203              port of the bin() builtin in Python 2.6.x and higher.
    204          """
    205          bit_words = []
    206
    207          for word in self.int_to_words(int_val):
    208              bits = []
    209              while word:
    210                  bits.append(_BYTES_TO_BITS[word&255])
    211                  word >>= 8
    212              bits.reverse()
    213              bit_str = ''.join(bits) or '0'*self.word_size
    214              bits = ('0'*self.word_size+bit_str)[-self.word_size:]
    215              bit_words.append(bits)
    216
    217          return '0b' + ''.join(bit_words)
    218
    219 -    def int_to_words(self, int_val, num_words=None, word_size=None):
    220          """
    221          @param int_val: An unsigned integer to be divided up into words.
    222
    223          @param num_words: (optional) number of words expected in return value
    224              tuple. Uses address type default if not specified.
    225
    226          @param word_size: (optional) size/width of individual words (in bits).
    227               Uses address type default if not specified.
    228          """
    229          if not self.valid_int(int_val):
    230              raise IndexError('integer %r is out of bounds!' % hex(int_val))
    231
    232          #   Set defaults for optional args.
    233          if num_words is None:
    234              num_words = self.num_words
    235          if word_size is None:
    236              word_size = self.word_size
    237
    238          max_word_size = 2 ** word_size - 1
    239
    240          words = []
    241          for _ in range(num_words):
    242              word = int_val & max_word_size
    243              words.append(int(word))
    244              int_val >>= word_size
    245
    246          return tuple(reversed(words))
    247
    248 -    def int_to_packed(self, int_val):
    249          """
    250          @param int_val: the integer to be packed.
    251
    252          @return: a packed string that is equivalent to value represented by an
    253          unsigned integer.
    254          """
    255
    256          words = self.int_to_words(int_val, self.num_words, self.word_size)
    257
    258          try:
    259              fmt = '>%d%s' % (self.num_words, AddrStrategy.STRUCT_FORMATS[
    260                  self.word_size])
    261          except KeyError:
    262              raise ValueError('unsupported word size: %d!' % self.word_size)
    263
    264          return _struct.pack(fmt, *words)
    265
    266
    267      #-------------------------------------------------------------------------
    268      #   Packed string methods.
    269      #-------------------------------------------------------------------------
    270
    271 -    def packed_to_int(self, packed_int):
    272          """
    273          @param packed_int: a packed string containing an unsigned integer.
    274              It is assumed that the string is packed in network byte order.
    275
    276          @return: An unsigned integer equivalent to value of network address
    277              represented by packed binary string.
    278          """
    279          try:
    280              fmt = '>%d%s' % (self.num_words, AddrStrategy.STRUCT_FORMATS[
    281                  self.word_size])
    282          except KeyError:
    283              raise ValueError('unsupported word size: %d!' % self.word_size)
    284
    285          words = list(_struct.unpack(fmt, packed_int))
    286
    287          int_val = 0
    288          for i, num in enumerate(reversed(words)):
    289              word = num
    290              word = word << self.word_size * i
    291              int_val = int_val | word
    292
    293          return int_val
    294
    295      #-------------------------------------------------------------------------
    296      #   String methods.
    297      #-------------------------------------------------------------------------
    298
    299 -    def valid_str(self, addr):
    300          """
    301          @param addr: A network address in string form.
    302
    303          @return: C{True} if network address in string form is valid for this
    304              address type, C{False} otherwise.
    305          """
    306          if not isinstance(addr, (str, unicode)):
    307              return False
    308
    309          tokens = addr.split(self.word_sep)
    310          if len(tokens) != self.num_words:
    311              return False
    312
    313          try:
    314              for token in tokens:
    315                  if not 0 <= int(token, self.word_base) <= \
    316                         self.max_word:
    317                      return False
    318          except TypeError:
    319              return False
    320          except ValueError:
    321              return False
    322          return True
    323
    324 -    def str_to_int(self, addr):
    325          """
    326          @param addr: A network address in string form.
    327
    328          @return: An unsigned integer equivalent to value represented by network
    329              address in string form.
    330          """
    331          if not self.valid_str(addr):
    332              raise ValueError('%r is not a recognised string representation' \
    333                  ' of this address type!' % addr)
    334
    335          tokens = addr.split(self.word_sep)
    336          words = [ int(token, self.word_base) for token in tokens ]
    337
    338          return self.words_to_int(words)
    339
    340      #-------------------------------------------------------------------------
    341      #   Word list methods.
    342      #-------------------------------------------------------------------------
    343
    344 -    def valid_words(self, words):
    345          """
    346          @param words: A sequence containing integer word values.
    347
    348          @return: C{True} if word sequence is valid for this address type,
    349              C{False} otherwise.
    350          """
    351          if not hasattr(words, '__iter__'):
    352              return False
    353
    354          if len(words) != self.num_words:
    355              return False
    356
    357          for i in words:
    358              if not isinstance(i, (int, long)):
    359                  return False
    360
    361              if not 0 <= i <= self.max_word:
    362                  return False
    363          return True
    364
    365 -    def words_to_int(self, words):
    366          """
    367          @param words: A list or tuple containing integer word values.
    368
    369          @return: An unsigned integer that is equivalent to value represented
    370              by word sequence.
    371          """
    372          if not self.valid_words(words):
    373              raise ValueError('%r is not a valid word list!' % words)
    374
    375          int_val = 0
    376          for i, num in enumerate(reversed(words)):
    377              word = num
    378              word = word << self.word_size * i
    379              int_val = int_val | word
    380
    381          return int_val
    382
    383  #-----------------------------------------------------------------------------
    384 -class IPv4Strategy(AddrStrategy):
    385      """An L{AddrStrategy} for IPv4 address processing."""
    386 -    def __init__(self):
    387          """Constructor."""
    388          super(IPv4Strategy, self).__init__(width=32, word_size=8,
    389                word_fmt='%d', word_sep='.', addr_type=AT_INET, word_base=10)
    390
    391 -    def valid_str(self, addr):
    392          """
    393          @param addr: An IP address in presentation (string) format.
    394
    395          @return: C{True} if network address in string form is valid for this
    396              address type, C{False} otherwise.
    397          """
    398          if addr == '':
    399              raise AddrFormatError('Empty strings are not supported!')
    400
    401          try:
    402              _inet_aton(addr)
    403          except:
    404              return False
    405          return True
    406
    407 -    def str_to_int(self, addr):
    408          """
    409          @param addr: An IPv4 dotted decimal address in string form.
    410
    411          @return: An unsigned integer that is equivalent to value represented
    412              by the IPv4 dotted decimal address string.
    413          """
    414          if addr == '':
    415              raise AddrFormatError('Empty strings are not supported!')
    416          try:
    417              return _struct.unpack('>I', _inet_aton(addr))[0]
    418          except:
    419              raise AddrFormatError('%r is not a valid IPv4 address string!' \
    420                  % addr)
    421
    422 -    def int_to_str(self, int_val):
    423          """
    424          @param int_val: An unsigned integer.
    425
    426          @return: An IPv4 dotted decimal address string that is equivalent to
    427              value represented by a 32 bit unsigned integer.
    428          """
    429          if self.valid_int(int_val):
    430              return _inet_ntoa(_struct.pack('>I', int_val))
    431          else:
    432              raise ValueError('%r is not a valid 32-bit unsigned integer!' \
    433                  % int_val)
    434
    435 -    def int_to_words(self, int_val, num_words=None, word_size=None):
    436          """
    437          @param int_val: An unsigned integer.
    438
    439          @param num_words: (unused) *** interface compatibility only ***
    440
    441          @param word_size: (unused) *** interface compatibility only ***
    442
    443          @return: An integer word (octet) sequence that is equivalent to value
    444              represented by an unsigned integer.
    445          """
    446          if not self.valid_int(int_val):
    447              raise ValueError('%r is not a valid integer value supported ' \
    448                  'by this address type!' % int_val)
    449          return _struct.unpack('4B', _struct.pack('>I', int_val))
    450
    451 -    def words_to_int(self, octets):
    452          """
    453          @param octets: A list or tuple containing integer octets.
    454
    455          @return: An unsigned integer that is equivalent to value represented
    456              by word (octet) sequence.
    457          """
    458          if not self.valid_words(octets):
    459              raise ValueError('%r is not a valid octet list for an IPv4 ' \
    460                  'address!' % octets)
    461          return _struct.unpack('>I', _struct.pack('4B', *octets))[0]
    462
    463 -    def int_to_arpa(self, int_val):
    464          """
    465          @param int_val: An unsigned integer.
    466
    467          @return: The reverse DNS lookup for an IPv4 address in network byte
    468              order integer form.
    469          """
    470          words = ["%d" % i for i in self.int_to_words(int_val)]
    471          words.reverse()
    472          words.extend(['in-addr', 'arpa', ''])
    473          return '.'.join(words)
    474
    475  #-----------------------------------------------------------------------------
    476 -class IPv6Strategy(AddrStrategy):
    477      """
    478      An L{AddrStrategy} for IPv6 address processing.
    479
    480      Implements the operations that can be performed on an IPv6 network address
    481      in accordance with RFC 4291.
    482      """
    483 -    def __init__(self):
    484          """Constructor."""
    485          super(IPv6Strategy, self).__init__(addr_type=AT_INET6,
    486              width=128, word_size=16, word_fmt='%x', word_sep=':')
    487
    488 -    def valid_str(self, addr):
    489          """
    490          @param addr: An IPv6 address in string form.
    491
    492          @return: C{True} if IPv6 network address string is valid, C{False}
    493              otherwise.
    494          """
    495          if addr == '':
    496              raise AddrFormatError('Empty strings are not supported!')
    497
    498          try:
    499              _inet_pton(_AF_INET6, addr)
    500          except _socket.error:
    501              return False
    502          except TypeError:
    503              return False
    504          except ValueError:
    505              return False
    506          return True
    507
    508 -    def str_to_int(self, addr):
    509          """
    510          @param addr: An IPv6 address in string form.
    511
    512          @return: The equivalent unsigned integer for a given IPv6 address.
    513          """
    514          if addr == '':
    515              raise AddrFormatError('Empty strings are not supported!')
    516          try:
    517              packed_int = _inet_pton(_AF_INET6, addr)
    518              return self.packed_to_int(packed_int)
    519          except Exception, e:
    520              raise AddrFormatError('%r is not a valid IPv6 address string!' \
    521                  % addr)
    522
    523 -    def int_to_str(self, int_val, compact=True, word_fmt=None):
    524          """
    525          @param int_val: An unsigned integer.
    526
    527          @param compact: (optional) A boolean flag indicating if compact
    528              formatting should be used. If True, this method uses the '::'
    529              string to represent the first adjacent group of words with a value
    530              of zero. Default: True
    531
    532          @param word_fmt: (optional) The Python format string used to override
    533              formatting for each word. Only applies when compact is False.
    534
    535          @return: The IPv6 string form equal to the unsigned integer provided.
    536          """
    537          try:
    538              packed_int = self.int_to_packed(int_val)
    539              if compact:
    540                  #   Default return value.
    541                  return _inet_ntop(_AF_INET6, packed_int)
    542              else:
    543                  #   Custom return value.
    544                  if word_fmt is None:
    545                      word_fmt = self.word_fmt
    546                  words = list(_struct.unpack('>8H', packed_int))
    547                  tokens = [word_fmt % word for word in words]
    548                  return self.word_sep.join(tokens)
    549          except Exception, e:
    550              raise ValueError('%r is not a valid 128-bit unsigned integer!' \
    551                  % int_val)
    552
    553 -    def int_to_packed(self, int_val):
    554          """
    555          @param int_val: the integer to be packed.
    556
    557          @return: a packed string that is equivalent to value represented by an
    558          unsigned integer (in network byte order).
    559          """
    560          #   Here we've over-ridden the normal values to get the fastest
    561          #   possible conversion speeds. Still quite slow versus IPv4 speed ups.
    562          num_words = 4
    563          word_size = 32
    564
    565          words = self.int_to_words(int_val, num_words, word_size)
    566
    567          try:
    568              fmt = '>%d%s'% (num_words, AddrStrategy.STRUCT_FORMATS[word_size])
    569          except KeyError:
    570              raise ValueError('unsupported word size: %d!' % word_size)
    571
    572          return _struct.pack(fmt, *words)
    573
    574 -    def packed_to_int(self, packed_int):
    575          """
    576          @param packed_int: a packed string containing an unsigned integer.
    577              It is assumed that string is packed in network byte order.
    578
    579          @return: An unsigned integer that is equivalent to value of network
    580              address represented by packed binary string.
    581          """
    582          #   Here we've over-ridden the normal values to get the fastest
    583          #   conversion speeds. Still quite slow versus IPv4 speed ups.
    584          num_words = 4
    585          word_size = 32
    586
    587          try:
    588              fmt = '>%d%s'% (num_words, AddrStrategy.STRUCT_FORMATS[word_size])
    589          except KeyError:
    590              raise ValueError('unsupported word size: %d!' % word_size)
    591
    592          words = list(_struct.unpack(fmt, packed_int))
    593
    594          int_val = 0
    595          for i, num in enumerate(reversed(words)):
    596              word = num
    597              word = word << word_size * i
    598              int_val = int_val | word
    599
    600          return int_val
    601
    602 -    def int_to_arpa(self, int_val):
    603          """
    604          @param int_val: An unsigned integer.
    605
    606          @return: The reverse DNS lookup for an IPv6 address in network byte
    607              order integer form.
    608          """
    609          addr = self.int_to_str(int_val, compact=False, word_fmt='%.4x')
    610          tokens = list(addr.replace(':', ''))
    611          tokens.reverse()
    612          #   We won't support ip6.int here - see RFC 3152 for details.
    613          tokens = tokens + ['ip6', 'arpa', '']
    614          return '.'.join(tokens)
    615
    616  #-----------------------------------------------------------------------------
    617 -class EUI48Strategy(AddrStrategy):
    618      """
    619      Implements the operations that can be performed on an IEEE 48-bit EUI
    620      (Extended Unique Identifer) a.k.a. a MAC (Media Access Control) layer 2
    621      address.
    622
    623      Supports all common (and some less common MAC string formats including
    624      Cisco's 'triple hextet' format and also bare MACs that contain no
    625      delimiters.
    626      """
    627      #   Regular expression to match the numerous different MAC formats.
    628      RE_MAC_FORMATS = (
    629          #   2 bytes x 6 (UNIX, Windows, EUI-48)
    630          '^' + ':'.join(['([0-9A-F]{1,2})'] * 6) + '$',
    631          '^' + '-'.join(['([0-9A-F]{1,2})'] * 6) + '$',
    632
    633          #   4 bytes x 3 (Cisco)
    634          '^' + ':'.join(['([0-9A-F]{1,4})'] * 3) + '$',
    635          '^' + '-'.join(['([0-9A-F]{1,4})'] * 3) + '$',
    636
    637          #   6 bytes x 2 (PostgreSQL)
    638          '^' + '-'.join(['([0-9A-F]{5,6})'] * 2) + '$',
    639          '^' + ':'.join(['([0-9A-F]{5,6})'] * 2) + '$',
    640
    641          #   12 bytes (bare, no delimiters)
    642          '^(' + ''.join(['[0-9A-F]'] * 12) + ')$',
    643          '^(' + ''.join(['[0-9A-F]'] * 11) + ')$',
    644      )
    645      #   For efficiency, replace each string regexp with its compiled
    646      #   equivalent.
    647      RE_MAC_FORMATS = [_re.compile(_, _re.IGNORECASE) for _ in RE_MAC_FORMATS]
    648
    649 -    def __init__(self, word_fmt='%.2X', word_sep='-'):
    650          """
    651          Constructor.
    652
    653          @param word_sep: separator between each word.
    654              (Default: '-')
    655
    656          @param word_fmt: format string for each hextet.
    657              (Default: '%02x')
    658          """
    659          super(self.__class__, self).__init__(addr_type=AT_LINK, width=48,
    660                word_size=8, word_fmt=word_fmt, word_sep=word_sep)
    661
    662
    663 -    def reset(self):
    664          """
    665          Resets the internal state of this strategy to safe default values.
    666          """
    667          #   These are the settings for EUI-48 specific formatting.
    668          self.width = 48
    669          self.max_int = 2 ** self.width - 1
    670          self.word_size = 8
    671          self.num_words = self.width / self.word_size
    672          self.max_word = 2 ** self.word_size - 1
    673          self.word_sep = '-'
    674          self.word_fmt  = '%.2X'
    675          self.word_base = 16
    676          self.addr_type = AT_LINK
    677
    678 -    def valid_str(self, addr):
    679          """
    680          @param addr: An EUI-48 or MAC address in string form.
    681
    682          @return: C{True} if MAC address string is valid, C{False} otherwise.
    683          """
    684          if not isinstance(addr, (str, unicode)):
    685              return False
    686
    687          for regexp in EUI48Strategy.RE_MAC_FORMATS:
    688              match_result = regexp.findall(addr)
    689              if len(match_result) != 0:
    690                  return True
    691          return False
    692
    693 -    def str_to_int(self, addr):
    694          """
    695          @param addr: An EUI-48 or MAC address in string form.
    696
    697          @return: An unsigned integer that is equivalent to value represented
    698              by EUI-48/MAC string address.
    699          """
    700          words = []
    701          if isinstance(addr, (str, unicode)):
    702              found_match = False
    703              for regexp in EUI48Strategy.RE_MAC_FORMATS:
    704                  match_result = regexp.findall(addr)
    705                  if len(match_result) != 0:
    706                      found_match = True
    707                      if isinstance(match_result[0], tuple):
    708                          words = match_result[0]
    709                      else:
    710                          words = (match_result[0],)
    711                      break
    712              if not found_match:
    713                  raise AddrFormatError('%r is not a supported MAC format!' \
    714                      % addr)
    715          else:
    716              raise TypeError('%r is not str() or unicode()!' % addr)
    717
    718          int_val = None
    719
    720          if len(words) == 6:
    721              #   2 bytes x 6 (UNIX, Windows, EUI-48)
    722              int_val = int(''.join(['%.2x' % int(w, 16) for w in words]), 16)
    723          elif len(words) == 3:
    724              #   4 bytes x 3 (Cisco)
    725              int_val = int(''.join(['%.4x' % int(w, 16) for w in words]), 16)
    726          elif len(words) == 2:
    727              #   6 bytes x 2 (PostgreSQL)
    728              int_val = int(''.join(['%.6x' % int(w, 16) for w in words]), 16)
    729          elif len(words) == 1:
    730              #   12 bytes (bare, no delimiters)
    731              int_val = int('%012x' % int(words[0], 16), 16)
    732          else:
    733              raise AddrFormatError('unexpected word count in MAC address %r!' \
    734                  % addr)
    735
    736          return int_val
    737
    738 -    def int_to_str(self, int_val, word_sep=None, word_fmt=None):
    739          """
    740          @param int_val: An unsigned integer.
    741
    742          @param word_sep: (optional) The separator used between words in an
    743              address string.
    744
    745          @param word_fmt: (optional) A Python format string used to format
    746              each word of address.
    747
    748          @return: A MAC address in string form that is equivalent to value
    749              represented by an unsigned integer.
    750          """
    751          _word_sep = self.word_sep
    752          if word_sep is not None:
    753              _word_sep = word_sep
    754
    755          _word_fmt = self.word_fmt
    756          if word_fmt is not None:
    757              _word_fmt = word_fmt
    758
    759          words = self.int_to_words(int_val)
    760          tokens = [_word_fmt % i for i in words]
    761          addr = _word_sep.join(tokens)
    762
    763          return addr
    764
    765  #-----------------------------------------------------------------------------
    766  #   Shared strategy objects for supported address types.
    767  #-----------------------------------------------------------------------------
    768
    769  #: A shared strategy object supporting all operations on IPv4 addresses.
    770  ST_IPV4 = IPv4Strategy()
    771
    772  #: A shared strategy object supporting all operations on IPv6 addresses.
    773  ST_IPV6 = IPv6Strategy()
    774
    775  #: A shared strategy object supporting all operations on EUI-48/MAC addresses.
    776  ST_EUI48 = EUI48Strategy()
    777
    778  #: A shared strategy object supporting all operations on EUI-64 addresses.
    779  ST_EUI64 = AddrStrategy(addr_type=AT_EUI64, width=64, word_size=8, \
    780                           word_fmt='%.2X', word_sep='-')
    781
    782  #-----------------------------------------------------------------------------
    783  if __name__ == '__main__':
    784      pass
    785