-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmacaddr.py
148 lines (119 loc) · 5.21 KB
/
macaddr.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
from typing import Optional, Callable
import re
from itertools import islice
from functools import lru_cache
__all__ = ["MacAddress"]
class MacAddress(object):
"""
MacAddress allows a Caller to declare an instance of a network MAC address string
value so that future calls to the `format` method will result is differnet format
groupings. Common formats are:
size=4, sep='.' => AABB.CCDD.EEFF
size=2, sep=':' => AA:BB:CC:DD:EE:FF
size=3, sep='-' => AAB-BDD-DDE-EFF
The `format` settings/return value are cached so that future calls using the
same format criteria are optimized. This is a desired condition since the
MAC address format value may be used multiple times across many usages; for
example when looking for a MAC address in multiple network devices.
"""
_mac_char_re = re.compile(r"[0-9a-f]", re.I)
def __init__(self, macaddr: str):
"""
Initializes the MacAddress instance with a given MAC string value, of
any format and character case. The Caller can then use the `format`
method to return back any desired format.
Parameters
----------
macaddr: str - The initial MAC address string
Raises
------
ValueError - when the provided MAC address is not 12 characters in
length; not counting any of the octet-group separators.
"""
self.chars = self._mac_char_re.findall(macaddr)
if len(self.chars) != 12:
raise ValueError(f"Invalid MAC address: {macaddr}")
@lru_cache()
def format(
self,
size: Optional[int] = 2,
sep: Optional[str] = ":",
to_case: Optional[Callable] = None,
):
"""
Format the MAC address to the defined group size and group separator.
The default settings of size=2 and sep=":" result in a MAC address
format in "AA:BB:CC:DD:EE:FF" notation, for example.
The Caller can also use the `to_case` parameter to return the value
using str.lower or str.upper case. By default if `to_case` is not
provided, then the character casing will be "as-is" passed in the
instance constructor.
The format settings/return value are cached so that future calls using
the same format criteria are optimized. This is a desired condition
since the MAC address format value may be used multiple times across
many usages; for example when looking for a MAC address in multiple
network devices.
Parameters
----------
size: int - The group size of the octets.
sep: char - The group separator.
to_case: Callable - should generally be str.lower or str.upper for case formatting
Returns
-------
str - the formatted MAC address string.
Raises
------
ValueError if `size` value is not divisible by 12; number characters in
a MAC address not counting the group separator character.
"""
i_chars = iter(self.chars)
chunks, rem = divmod(12, size)
if rem != 0:
raise ValueError(f"Invalid size {size}, not divisible from 12")
value = sep.join("".join(islice(i_chars, size)) for _ in range(chunks))
return value if not to_case else to_case(value)
@lru_cache()
def format_oui(
self,
size: Optional[int] = 2,
sep: Optional[str] = ":",
to_case: Optional[Callable] = None,
):
"""
Format the MAC address OUI to the defined group size and group
separator. The default settings of size=2 and sep=":" result in a MAC
address format in "AA:BB:CC" notation, for example.
The Caller can also use the `to_case` parameter to return the value
using str.lower or str.upper case. By default if `to_case` is not
provided, then the character casing will be "as-is" passed in the
instance constructor.
The settings/return value are cached so that future calls using the same
criteria are optimized. This is a desired condition since the MAC
address OUI format value may be used multiple times across many usages;
for example when looking for a MAC address maching an OUI in multiple
network devices.
Parameters
----------
size: int - The group size of the octets.
sep: char - The group separator.
to_case: Callable - should generally be str.lower or str.upper for case formatting
Returns
-------
str - the formatted MAC address OUI string.
Raises
------
ValueError if the `size` value is > 6; i.e. the max characters in an
OUI; not counting the separator character.
"""
i_chars = iter(self.chars)
chunks, rem = divmod(6, size)
if not chunks:
raise ValueError(f"Invalid size {size}, not <= 6")
value = sep.join("".join(islice(i_chars, size)) for _ in range(chunks))
if rem:
value = f'{value}{sep}{"".join(islice(i_chars, rem))}'
return value if not to_case else to_case(value)
def __repr__(self):
return f"{self.__class__.__name__}({str(self)})"
def __str__(self):
return self.format()