Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/netaddr/eui/__init__.py: 24%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

363 statements  

1# ----------------------------------------------------------------------------- 

2# Copyright (c) 2008 by David P. D. Moss. All rights reserved. 

3# 

4# Released under the BSD license. See the LICENSE file for details. 

5# ----------------------------------------------------------------------------- 

6""" 

7Classes and functions for dealing with MAC addresses, EUI-48, EUI-64, OUI, IAB 

8identifiers. 

9""" 

10 

11from netaddr.core import NotRegisteredError, AddrFormatError, DictDotLookup 

12from netaddr.strategy import eui48 as _eui48, eui64 as _eui64 

13from netaddr.strategy.eui48 import mac_eui48 

14from netaddr.strategy.eui64 import eui64_base 

15from netaddr.ip import IPAddress 

16from netaddr.compat import _open_binary 

17 

18 

19class BaseIdentifier(object): 

20 """Base class for all IEEE identifiers.""" 

21 

22 __slots__ = ('_value', '__weakref__') 

23 

24 def __init__(self): 

25 self._value = None 

26 

27 def __int__(self): 

28 """:return: integer value of this identifier""" 

29 return self._value 

30 

31 def __index__(self): 

32 """ 

33 :return: return the integer value of this identifier. 

34 """ 

35 return self._value 

36 

37 

38class OUI(BaseIdentifier): 

39 """ 

40 An individual IEEE OUI (Organisationally Unique Identifier). 

41 

42 For online details see - http://standards.ieee.org/regauth/oui/ 

43 

44 """ 

45 

46 __slots__ = ('records',) 

47 

48 def __init__(self, oui): 

49 """ 

50 Constructor 

51 

52 :param oui: an OUI string ``XX-XX-XX`` or an unsigned integer. \ 

53 Also accepts and parses full MAC/EUI-48 address strings (but not \ 

54 MAC/EUI-48 integers)! 

55 """ 

56 super(OUI, self).__init__() 

57 

58 # Lazy loading of IEEE data structures. 

59 from netaddr.eui import ieee 

60 

61 self.records = [] 

62 

63 if isinstance(oui, str): 

64 # TODO: Improve string parsing here. 

65 # TODO: Accept full MAC/EUI-48 addresses as well as XX-XX-XX 

66 # TODO: and just take /16 (see IAB for details) 

67 self._value = int(oui.replace('-', ''), 16) 

68 elif isinstance(oui, int): 

69 if 0 <= oui <= 0xFFFFFF: 

70 self._value = oui 

71 else: 

72 raise ValueError('OUI int outside expected range: %r' % (oui,)) 

73 else: 

74 raise TypeError('unexpected OUI format: %r' % (oui,)) 

75 

76 # Discover offsets. 

77 if self._value in ieee.OUI_INDEX: 

78 fh = _open_binary(__package__, 'oui.txt') 

79 for offset, size in ieee.OUI_INDEX[self._value]: 

80 fh.seek(offset) 

81 data = fh.read(size).decode('UTF-8') 

82 self._parse_data(data, offset, size) 

83 fh.close() 

84 else: 

85 raise NotRegisteredError('OUI %r not registered!' % (oui,)) 

86 

87 def __hash__(self): 

88 return hash(self._value) 

89 

90 def __eq__(self, other): 

91 if not isinstance(other, OUI): 

92 try: 

93 other = self.__class__(other) 

94 except Exception: 

95 return NotImplemented 

96 return self._value == other._value 

97 

98 def __ne__(self, other): 

99 if not isinstance(other, OUI): 

100 try: 

101 other = self.__class__(other) 

102 except Exception: 

103 return NotImplemented 

104 return self._value != other._value 

105 

106 def __getstate__(self): 

107 """:returns: Pickled state of an `OUI` object.""" 

108 return self._value, self.records 

109 

110 def __setstate__(self, state): 

111 """:param state: data used to unpickle a pickled `OUI` object.""" 

112 self._value, self.records = state 

113 

114 def _parse_data(self, data, offset, size): 

115 """Returns a dict record from raw OUI record data""" 

116 record = { 

117 'idx': 0, 

118 'oui': '', 

119 'org': '', 

120 'address': [], 

121 'offset': offset, 

122 'size': size, 

123 } 

124 

125 for line in data.split('\n'): 

126 line = line.strip() 

127 if not line: 

128 continue 

129 

130 if '(hex)' in line: 

131 record['idx'] = self._value 

132 record['org'] = line.split(None, 2)[2] 

133 record['oui'] = str(self) 

134 elif '(base 16)' in line: 

135 continue 

136 else: 

137 record['address'].append(line) 

138 

139 self.records.append(record) 

140 

141 @property 

142 def reg_count(self): 

143 """Number of registered organisations with this OUI""" 

144 return len(self.records) 

145 

146 def registration(self, index=0): 

147 """ 

148 The IEEE registration details for this OUI. 

149 

150 :param index: the index of record (may contain multiple registrations) 

151 (Default: 0 - first registration) 

152 

153 :return: Objectified Python data structure containing registration 

154 details. 

155 """ 

156 return DictDotLookup(self.records[index]) 

157 

158 def __str__(self): 

159 """:return: string representation of this OUI""" 

160 int_val = self._value 

161 return '%02X-%02X-%02X' % ((int_val >> 16) & 0xFF, (int_val >> 8) & 0xFF, int_val & 0xFF) 

162 

163 def __repr__(self): 

164 """:return: executable Python string to recreate equivalent object.""" 

165 return "OUI('%s')" % self 

166 

167 

168class IAB(BaseIdentifier): 

169 IAB_EUI_VALUES = (0x0050C2, 0x40D855) 

170 

171 """ 

172 An individual IEEE IAB (Individual Address Block) identifier. 

173 

174 For online details see - http://standards.ieee.org/regauth/oui/ 

175 

176 """ 

177 __slots__ = ('record',) 

178 

179 @classmethod 

180 def split_iab_mac(cls, eui_int, strict=False): 

181 """ 

182 :param eui_int: a MAC IAB as an unsigned integer. 

183 

184 :param strict: If True, raises a ValueError if the last 12 bits of 

185 IAB MAC/EUI-48 address are non-zero, ignores them otherwise. 

186 (Default: False) 

187 """ 

188 if (eui_int >> 12) in cls.IAB_EUI_VALUES: 

189 return eui_int, 0 

190 

191 user_mask = 2**12 - 1 

192 iab_mask = (2**48 - 1) ^ user_mask 

193 iab_bits = eui_int >> 12 

194 user_bits = (eui_int | iab_mask) - iab_mask 

195 

196 if (iab_bits >> 12) in cls.IAB_EUI_VALUES: 

197 if strict and user_bits != 0: 

198 raise ValueError('%r is not a strict IAB!' % hex(user_bits)) 

199 else: 

200 raise ValueError('%r is not an IAB address!' % hex(eui_int)) 

201 

202 return iab_bits, user_bits 

203 

204 def __init__(self, iab, strict=False): 

205 """ 

206 Constructor 

207 

208 :param iab: an IAB string ``00-50-C2-XX-X0-00`` or an unsigned \ 

209 integer. This address looks like an EUI-48 but it should not \ 

210 have any non-zero bits in the last 3 bytes. 

211 

212 :param strict: If True, raises a ValueError if the last 12 bits \ 

213 of IAB MAC/EUI-48 address are non-zero, ignores them otherwise. \ 

214 (Default: False) 

215 """ 

216 super(IAB, self).__init__() 

217 

218 # Lazy loading of IEEE data structures. 

219 from netaddr.eui import ieee 

220 

221 self.record = { 

222 'idx': 0, 

223 'iab': '', 

224 'org': '', 

225 'address': [], 

226 'offset': 0, 

227 'size': 0, 

228 } 

229 

230 if isinstance(iab, str): 

231 # TODO: Improve string parsing here. 

232 # TODO: '00-50-C2' is actually invalid. 

233 # TODO: Should be '00-50-C2-00-00-00' (i.e. a full MAC/EUI-48) 

234 int_val = int(iab.replace('-', ''), 16) 

235 iab_int, user_int = self.split_iab_mac(int_val, strict=strict) 

236 self._value = iab_int 

237 elif isinstance(iab, int): 

238 iab_int, user_int = self.split_iab_mac(iab, strict=strict) 

239 self._value = iab_int 

240 else: 

241 raise TypeError('unexpected IAB format: %r!' % (iab,)) 

242 

243 # Discover offsets. 

244 if self._value in ieee.IAB_INDEX: 

245 fh = _open_binary(__package__, 'iab.txt') 

246 (offset, size) = ieee.IAB_INDEX[self._value][0] 

247 self.record['offset'] = offset 

248 self.record['size'] = size 

249 fh.seek(offset) 

250 data = fh.read(size).decode('UTF-8') 

251 self._parse_data(data, offset, size) 

252 fh.close() 

253 else: 

254 raise NotRegisteredError('IAB %r not unregistered!' % (iab,)) 

255 

256 def __eq__(self, other): 

257 if not isinstance(other, IAB): 

258 try: 

259 other = self.__class__(other) 

260 except Exception: 

261 return NotImplemented 

262 return self._value == other._value 

263 

264 def __ne__(self, other): 

265 if not isinstance(other, IAB): 

266 try: 

267 other = self.__class__(other) 

268 except Exception: 

269 return NotImplemented 

270 return self._value != other._value 

271 

272 def __getstate__(self): 

273 """:returns: Pickled state of an `IAB` object.""" 

274 return self._value, self.record 

275 

276 def __setstate__(self, state): 

277 """:param state: data used to unpickle a pickled `IAB` object.""" 

278 self._value, self.record = state 

279 

280 def _parse_data(self, data, offset, size): 

281 """Returns a dict record from raw IAB record data""" 

282 for line in data.split('\n'): 

283 line = line.strip() 

284 if not line: 

285 continue 

286 

287 if '(hex)' in line: 

288 self.record['idx'] = self._value 

289 self.record['org'] = line.split(None, 2)[2] 

290 self.record['iab'] = str(self) 

291 elif '(base 16)' in line: 

292 continue 

293 else: 

294 self.record['address'].append(line) 

295 

296 def registration(self): 

297 """The IEEE registration details for this IAB""" 

298 return DictDotLookup(self.record) 

299 

300 def __str__(self): 

301 """:return: string representation of this IAB""" 

302 int_val = self._value << 4 

303 

304 return '%02X-%02X-%02X-%02X-%02X-00' % ( 

305 (int_val >> 32) & 0xFF, 

306 (int_val >> 24) & 0xFF, 

307 (int_val >> 16) & 0xFF, 

308 (int_val >> 8) & 0xFF, 

309 int_val & 0xFF, 

310 ) 

311 

312 def __repr__(self): 

313 """:return: executable Python string to recreate equivalent object.""" 

314 return "IAB('%s')" % self 

315 

316 

317class EUI(BaseIdentifier): 

318 """ 

319 An IEEE EUI (Extended Unique Identifier). 

320 

321 Both EUI-48 (used for layer 2 MAC addresses) and EUI-64 are supported. 

322 

323 Input parsing for EUI-48 addresses is flexible, supporting many MAC 

324 variants. 

325 

326 """ 

327 

328 __slots__ = ('_module', '_dialect') 

329 

330 def __init__(self, addr, version=None, dialect=None): 

331 """ 

332 Constructor. 

333 

334 :param addr: an EUI-48 (MAC) or EUI-64 address in string format or \ 

335 an unsigned integer. May also be another EUI object (copy \ 

336 construction). 

337 

338 :param version: (optional) the explicit EUI address version, either \ 

339 48 or 64. Mainly used to distinguish EUI-48 and EUI-64 identifiers \ 

340 specified as integers which may be numerically equivalent. 

341 

342 :param dialect: (optional) one of the :ref:`mac_formatting_dialects` to 

343 be used to configure the formatting of EUI-48 (MAC) addresses. 

344 """ 

345 super(EUI, self).__init__() 

346 

347 self._module = None 

348 

349 if isinstance(addr, EUI): 

350 # Copy constructor. 

351 if version is not None and version != addr._module.version: 

352 raise ValueError('cannot switch EUI versions using ' 'copy constructor!') 

353 self._module = addr._module 

354 self._value = addr._value 

355 self.dialect = dialect or addr.dialect 

356 return 

357 

358 if version is not None: 

359 if version == 48: 

360 self._module = _eui48 

361 elif version == 64: 

362 self._module = _eui64 

363 else: 

364 raise ValueError('unsupported EUI version %r' % version) 

365 else: 

366 # Choose a default version when addr is an integer and version is 

367 # not specified. 

368 if isinstance(addr, int): 

369 if 0 <= addr <= 0xFFFFFFFFFFFF: 

370 self._module = _eui48 

371 elif 0xFFFFFFFFFFFF < addr <= 0xFFFFFFFFFFFFFFFF: 

372 self._module = _eui64 

373 

374 self.value = addr 

375 

376 # Choose a dialect for MAC formatting. 

377 self.dialect = dialect 

378 

379 def __getstate__(self): 

380 """:returns: Pickled state of an `EUI` object.""" 

381 return self._value, self._module.version, self.dialect 

382 

383 def __setstate__(self, state): 

384 """ 

385 :param state: data used to unpickle a pickled `EUI` object. 

386 

387 """ 

388 value, version, dialect = state 

389 

390 self._value = value 

391 

392 if version == 48: 

393 self._module = _eui48 

394 elif version == 64: 

395 self._module = _eui64 

396 else: 

397 raise ValueError('unpickling failed for object state: %s' % (state,)) 

398 

399 self.dialect = dialect 

400 

401 def _get_value(self): 

402 return self._value 

403 

404 def _set_value(self, value): 

405 if self._module is None: 

406 # EUI version is implicit, detect it from value. 

407 for module in (_eui48, _eui64): 

408 try: 

409 self._value = module.str_to_int(value) 

410 self._module = module 

411 break 

412 except AddrFormatError: 

413 try: 

414 if 0 <= int(value) <= module.max_int: 

415 self._value = int(value) 

416 self._module = module 

417 break 

418 except ValueError: 

419 pass 

420 

421 if self._module is None: 

422 raise AddrFormatError('failed to detect EUI version: %r' % (value,)) 

423 else: 

424 # EUI version is explicit. 

425 if isinstance(value, str): 

426 try: 

427 self._value = self._module.str_to_int(value) 

428 except AddrFormatError: 

429 raise AddrFormatError( 

430 'address %r is not an EUIv%d' % (value, self._module.version) 

431 ) 

432 else: 

433 if 0 <= int(value) <= self._module.max_int: 

434 self._value = int(value) 

435 else: 

436 raise AddrFormatError('bad address format: %r' % (value,)) 

437 

438 value = property( 

439 _get_value, 

440 _set_value, 

441 None, 

442 'a positive integer representing the value of this EUI identifier.', 

443 ) 

444 

445 def _get_dialect(self): 

446 return self._dialect 

447 

448 def _validate_dialect(self, value): 

449 if value is None: 

450 if self._module is _eui64: 

451 return eui64_base 

452 else: 

453 return mac_eui48 

454 else: 

455 if hasattr(value, 'word_size') and hasattr(value, 'word_fmt'): 

456 return value 

457 else: 

458 raise TypeError('custom dialects should subclass mac_eui48!') 

459 

460 def _set_dialect(self, value): 

461 self._dialect = self._validate_dialect(value) 

462 

463 dialect = property( 

464 _get_dialect, 

465 _set_dialect, 

466 None, 

467 'a Python class providing support for the interpretation of ' 

468 'various MAC\n address formats.', 

469 ) 

470 

471 @property 

472 def oui(self): 

473 """The OUI (Organisationally Unique Identifier) for this EUI.""" 

474 if self._module == _eui48: 

475 return OUI(self.value >> 24) 

476 elif self._module == _eui64: 

477 return OUI(self.value >> 40) 

478 

479 @property 

480 def ei(self): 

481 """The EI (Extension Identifier) for this EUI""" 

482 if self._module == _eui48: 

483 return '%02X-%02X-%02X' % tuple(self[3:6]) 

484 elif self._module == _eui64: 

485 return '%02X-%02X-%02X-%02X-%02X' % tuple(self[3:8]) 

486 

487 def is_iab(self): 

488 """:return: True if this EUI is an IAB address, False otherwise""" 

489 return (self._value >> 24) in IAB.IAB_EUI_VALUES 

490 

491 @property 

492 def iab(self): 

493 """ 

494 If is_iab() is True, the IAB (Individual Address Block) is returned, 

495 ``None`` otherwise. 

496 """ 

497 if self.is_iab(): 

498 return IAB(self._value >> 12) 

499 

500 @property 

501 def version(self): 

502 """The EUI version represented by this EUI object.""" 

503 return self._module.version 

504 

505 def __getitem__(self, idx): 

506 """ 

507 :return: The integer value of the word referenced by index (both \ 

508 positive and negative). Raises ``IndexError`` if index is out \ 

509 of bounds. Also supports Python list slices for accessing \ 

510 word groups. 

511 """ 

512 if isinstance(idx, int): 

513 # Indexing, including negative indexing goodness. 

514 num_words = self._dialect.num_words 

515 if not (-num_words) <= idx <= (num_words - 1): 

516 raise IndexError('index out range for address type!') 

517 return self._module.int_to_words(self._value, self._dialect)[idx] 

518 elif isinstance(idx, slice): 

519 words = self._module.int_to_words(self._value, self._dialect) 

520 return [words[i] for i in range(*idx.indices(len(words)))] 

521 else: 

522 raise TypeError('unsupported type %r!' % (idx,)) 

523 

524 def __setitem__(self, idx, value): 

525 """Set the value of the word referenced by index in this address""" 

526 if isinstance(idx, slice): 

527 # TODO - settable slices. 

528 raise NotImplementedError('settable slices are not supported!') 

529 

530 if not isinstance(idx, int): 

531 raise TypeError('index not an integer!') 

532 

533 if not 0 <= idx <= (self._dialect.num_words - 1): 

534 raise IndexError('index %d outside address type boundary!' % (idx,)) 

535 

536 if not isinstance(value, int): 

537 raise TypeError('value not an integer!') 

538 

539 if not 0 <= value <= self._dialect.max_word: 

540 raise IndexError( 

541 'value %d outside word size maximum of %d bits!' % (value, self._dialect.word_size) 

542 ) 

543 

544 words = list(self._module.int_to_words(self._value, self._dialect)) 

545 words[idx] = value 

546 self._value = self._module.words_to_int(words) 

547 

548 def __hash__(self): 

549 """:return: hash of this EUI object suitable for dict keys, sets etc""" 

550 return hash((self.version, self._value)) 

551 

552 def __eq__(self, other): 

553 """ 

554 :return: ``True`` if this EUI object is numerically the same as other, \ 

555 ``False`` otherwise. 

556 """ 

557 if not isinstance(other, EUI): 

558 try: 

559 other = self.__class__(other) 

560 except Exception: 

561 return NotImplemented 

562 return (self.version, self._value) == (other.version, other._value) 

563 

564 def __ne__(self, other): 

565 """ 

566 :return: ``True`` if this EUI object is numerically the same as other, \ 

567 ``False`` otherwise. 

568 """ 

569 if not isinstance(other, EUI): 

570 try: 

571 other = self.__class__(other) 

572 except Exception: 

573 return NotImplemented 

574 return (self.version, self._value) != (other.version, other._value) 

575 

576 def __lt__(self, other): 

577 """ 

578 :return: ``True`` if this EUI object is numerically lower in value than \ 

579 other, ``False`` otherwise. 

580 """ 

581 if not isinstance(other, EUI): 

582 try: 

583 other = self.__class__(other) 

584 except Exception: 

585 return NotImplemented 

586 return (self.version, self._value) < (other.version, other._value) 

587 

588 def __le__(self, other): 

589 """ 

590 :return: ``True`` if this EUI object is numerically lower or equal in \ 

591 value to other, ``False`` otherwise. 

592 """ 

593 if not isinstance(other, EUI): 

594 try: 

595 other = self.__class__(other) 

596 except Exception: 

597 return NotImplemented 

598 return (self.version, self._value) <= (other.version, other._value) 

599 

600 def __gt__(self, other): 

601 """ 

602 :return: ``True`` if this EUI object is numerically greater in value \ 

603 than other, ``False`` otherwise. 

604 """ 

605 if not isinstance(other, EUI): 

606 try: 

607 other = self.__class__(other) 

608 except Exception: 

609 return NotImplemented 

610 return (self.version, self._value) > (other.version, other._value) 

611 

612 def __ge__(self, other): 

613 """ 

614 :return: ``True`` if this EUI object is numerically greater or equal \ 

615 in value to other, ``False`` otherwise. 

616 """ 

617 if not isinstance(other, EUI): 

618 try: 

619 other = self.__class__(other) 

620 except Exception: 

621 return NotImplemented 

622 return (self.version, self._value) >= (other.version, other._value) 

623 

624 def bits(self, word_sep=None): 

625 """ 

626 :param word_sep: (optional) the separator to insert between words. \ 

627 Default: None - use default separator for address type. 

628 

629 :return: human-readable binary digit string of this address. 

630 """ 

631 return self._module.int_to_bits(self._value, word_sep) 

632 

633 @property 

634 def packed(self): 

635 """The value of this EUI address as a packed binary string.""" 

636 return self._module.int_to_packed(self._value) 

637 

638 @property 

639 def words(self): 

640 """A list of unsigned integer octets found in this EUI address.""" 

641 return self._module.int_to_words(self._value) 

642 

643 @property 

644 def bin(self): 

645 """ 

646 The value of this EUI address in standard Python binary 

647 representational form (0bxxx). A back port of the format provided by 

648 the builtin bin() function found in Python 2.6.x and higher. 

649 """ 

650 return self._module.int_to_bin(self._value) 

651 

652 def eui64(self): 

653 """ 

654 - If this object represents an EUI-48 it is converted to EUI-64 \ 

655 as per the standard. 

656 - If this object is already an EUI-64, a new, numerically \ 

657 equivalent object is returned instead. 

658 

659 :return: The value of this EUI object as a new 64-bit EUI object. 

660 """ 

661 if self.version == 48: 

662 # Convert 11:22:33:44:55:66 into 11:22:33:FF:FE:44:55:66. 

663 first_three = self._value >> 24 

664 last_three = self._value & 0xFFFFFF 

665 new_value = (first_three << 40) | 0xFFFE000000 | last_three 

666 else: 

667 # is already a EUI64 

668 new_value = self._value 

669 return self.__class__(new_value, version=64) 

670 

671 def modified_eui64(self): 

672 """ 

673 - create a new EUI object with a modified EUI-64 as described in RFC 4291 section 2.5.1 

674 

675 :return: a new and modified 64-bit EUI object. 

676 """ 

677 # Modified EUI-64 format interface identifiers are formed by inverting 

678 # the "u" bit (universal/local bit in IEEE EUI-64 terminology) when 

679 # forming the interface identifier from IEEE EUI-64 identifiers. In 

680 # the resulting Modified EUI-64 format, the "u" bit is set to one (1) 

681 # to indicate universal scope, and it is set to zero (0) to indicate 

682 # local scope. 

683 eui64 = self.eui64() 

684 eui64._value ^= 0x00000000000000000200000000000000 

685 return eui64 

686 

687 def ipv6(self, prefix): 

688 """ 

689 .. note:: This poses security risks in certain scenarios. \ 

690 Please read RFC 4941 for details. Reference: RFCs 4291 and 4941. 

691 

692 :param prefix: ipv6 prefix 

693 

694 :return: new IPv6 `IPAddress` object based on this `EUI` \ 

695 using the technique described in RFC 4291. 

696 """ 

697 int_val = int(prefix) + int(self.modified_eui64()) 

698 return IPAddress(int_val, version=6) 

699 

700 def ipv6_link_local(self): 

701 """ 

702 .. note:: This poses security risks in certain scenarios. \ 

703 Please read RFC 4941 for details. Reference: RFCs 4291 and 4941. 

704 

705 :return: new link local IPv6 `IPAddress` object based on this `EUI` \ 

706 using the technique described in RFC 4291. 

707 """ 

708 return self.ipv6(0xFE800000000000000000000000000000) 

709 

710 @property 

711 def info(self): 

712 """ 

713 A record dict containing IEEE registration details for this EUI 

714 (MAC-48) if available, None otherwise. 

715 """ 

716 data = {'OUI': self.oui.registration()} 

717 if self.is_iab(): 

718 data['IAB'] = self.iab.registration() 

719 

720 return DictDotLookup(data) 

721 

722 def format(self, dialect=None): 

723 """ 

724 Format the EUI into the representational format according to the given 

725 dialect 

726 

727 :param dialect: one of the :ref:`mac_formatting_dialects` defining the 

728 formatting of EUI-48 (MAC) addresses. 

729 

730 :return: EUI in representational format according to the given dialect 

731 """ 

732 validated_dialect = self._validate_dialect(dialect) 

733 return self._module.int_to_str(self._value, validated_dialect) 

734 

735 def __str__(self): 

736 """:return: EUI in representational format""" 

737 return self._module.int_to_str(self._value, self._dialect) 

738 

739 def __repr__(self): 

740 """:return: executable Python string to recreate equivalent object.""" 

741 return "EUI('%s')" % self