| 
9 | 9 | from pydantic import GetCoreSchemaHandler  | 
10 | 10 | from pydantic_core import PydanticCustomError, core_schema  | 
11 | 11 | 
 
  | 
 | 12 | +MINIMUM_LENGTH: int = 14  | 
 | 13 | +ALLOWED_CHUNK_COUNTS: tuple[int, int, int] = (6, 8, 20)  | 
 | 14 | + | 
12 | 15 | 
 
  | 
13 | 16 | class MacAddress(str):  | 
14 | 17 |     """Represents a MAC address and provides methods for conversion, validation, and serialization.  | 
@@ -63,36 +66,41 @@ def _validate(cls, __input_value: str, _: Any) -> str:  | 
63 | 66 |     @staticmethod  | 
64 | 67 |     def validate_mac_address(value: bytes) -> str:  | 
65 | 68 |         """Validate a MAC Address from the provided byte value."""  | 
66 |  | -        string = value.decode()  | 
67 |  | -        if len(string) < 14:  | 
 | 69 | +        raw = value.decode()  | 
 | 70 | +        if len(raw) < MINIMUM_LENGTH:  | 
68 | 71 |             raise PydanticCustomError(  | 
69 | 72 |                 'mac_address_len',  | 
70 | 73 |                 'Length for a {mac_address} MAC address must be {required_length}',  | 
71 |  | -                {'mac_address': string, 'required_length': 14},  | 
 | 74 | +                {'mac_address': raw, 'required_length': MINIMUM_LENGTH},  | 
72 | 75 |             )  | 
73 |  | -        for sep, partbytes in ((':', 2), ('-', 2), ('.', 4)):  | 
74 |  | -            if sep in string:  | 
75 |  | -                parts = string.split(sep)  | 
76 |  | -                if any(len(part) != partbytes for part in parts):  | 
77 |  | -                    raise PydanticCustomError(  | 
78 |  | -                        'mac_address_format',  | 
79 |  | -                        f'Must have the format xx{sep}xx{sep}xx{sep}xx{sep}xx{sep}xx',  | 
80 |  | -                    )  | 
81 |  | -                if len(parts) * partbytes // 2 not in (6, 8, 20):  | 
82 |  | -                    raise PydanticCustomError(  | 
83 |  | -                        'mac_address_format',  | 
84 |  | -                        'Length for a {mac_address} MAC address must be {required_length}',  | 
85 |  | -                        {'mac_address': string, 'required_length': (6, 8, 20)},  | 
86 |  | -                    )  | 
87 |  | -                mac_address = []  | 
 | 76 | + | 
 | 77 | +        for seperator, chunk_len in ((':', 2), ('-', 2), ('.', 4)):  | 
 | 78 | +            if seperator not in raw:  | 
 | 79 | +                continue  | 
 | 80 | + | 
 | 81 | +            parts = raw.split(seperator)  | 
 | 82 | +            if any(len(p) != chunk_len for p in parts):  | 
 | 83 | +                raise PydanticCustomError(  | 
 | 84 | +                    'mac_address_format',  | 
 | 85 | +                    f'Must have the format xx{seperator}xx{seperator}xx{seperator}xx{seperator}xx{seperator}xx',  | 
 | 86 | +                )  | 
 | 87 | + | 
 | 88 | +            total_bytes = (len(parts) * chunk_len) // 2  | 
 | 89 | +            if total_bytes not in ALLOWED_CHUNK_COUNTS:  | 
 | 90 | +                raise PydanticCustomError(  | 
 | 91 | +                    'mac_address_format',  | 
 | 92 | +                    'Length for a {mac_address} MAC address must be {required_length}',  | 
 | 93 | +                    {'mac_address': raw, 'required_length': ALLOWED_CHUNK_COUNTS},  | 
 | 94 | +                )  | 
 | 95 | + | 
 | 96 | +            try:  | 
 | 97 | +                mac_bytes: list[int] = []  | 
88 | 98 |                 for part in parts:  | 
89 |  | -                    for idx in range(0, partbytes, 2):  | 
90 |  | -                        try:  | 
91 |  | -                            byte_value = int(part[idx : idx + 2], 16)  | 
92 |  | -                        except ValueError as exc:  | 
93 |  | -                            raise PydanticCustomError('mac_address_format', 'Unrecognized format') from exc  | 
94 |  | -                        else:  | 
95 |  | -                            mac_address.append(byte_value)  | 
96 |  | -                return ':'.join(f'{b:02x}' for b in mac_address)  | 
97 |  | -        else:  | 
98 |  | -            raise PydanticCustomError('mac_address_format', 'Unrecognized format')  | 
 | 99 | +                    for i in range(0, chunk_len, 2):  | 
 | 100 | +                        mac_bytes.append(int(part[i:i + 2], base=16))  | 
 | 101 | +            except ValueError as exc:  | 
 | 102 | +                raise PydanticCustomError('mac_address_format', 'Unrecognized format') from exc  | 
 | 103 | + | 
 | 104 | +            return ':'.join(f'{b:02x}' for b in mac_bytes)  | 
 | 105 | + | 
 | 106 | +        raise PydanticCustomError('mac_address_format', 'Unrecognized format')  | 
0 commit comments