laforge has submitted this change. ( https://gerrit.osmocom.org/c/python/pyosmocom/+/38202?usp=email )
Change subject: osmocom.utils: Make b2h/i2h/swap_nibbles return hexstr type ......................................................................
osmocom.utils: Make b2h/i2h/swap_nibbles return hexstr type
Change-Id: I4c72c33baf6e4b3a39fb28d72ad66fbd3e957e95 --- M src/osmocom/utils.py 1 file changed, 40 insertions(+), 38 deletions(-)
Approvals: fixeria: Looks good to me, but someone else must approve laforge: Looks good to me, approved Jenkins Builder: Verified
diff --git a/src/osmocom/utils.py b/src/osmocom/utils.py index 183bbb8..6c91783 100644 --- a/src/osmocom/utils.py +++ b/src/osmocom/utils.py @@ -27,7 +27,39 @@ # along with this program. If not, see http://www.gnu.org/licenses/. #
-# just to differentiate strings of hex nibbles from everything else +class hexstr(str): + """Class derived from 'str', represeting a string of hexadecimal digits. It differs in that + comparisons are case-insensitive, and it offers encoding-free conversion from hexstr to bytes + and vice-versa.""" + def __new__(cls, s: str): + if not all(c in string.hexdigits for c in s): + raise ValueError('Input must be hexadecimal digits only') + # store as lower case digits + return super().__new__(cls, s.lower()) + + def __eq__(self, other: str) -> bool: + # make sure comparison is done case-insensitive + return str(self) == other.lower() + + def __getitem__(self, val) -> 'hexstr': + # make sure slicing a hexstr will return a hexstr + return hexstr(super().__getitem__(val)) + + def to_bytes(self) -> bytes: + """return hex-string converted to bytes""" + s = str(self) + if len(s) & 1: + raise ValueError('Cannot convert hex string with odd number of digits') + return h2b(s) + + @classmethod + def from_bytes(cls, bt: bytes) -> 'hexstr': + """instantiate hex-string from bytes""" + return cls(b2h(bt)) + +# just to differentiate strings of hex nibbles from everything else; only used for typing +# hints. New code should typically use the 'class hexstr' type above to get the benefit +# of case-insensitive comparison. Hexstr = NewType('Hexstr', str)
def h2b(s: Hexstr) -> bytearray: @@ -35,9 +67,9 @@ return bytearray.fromhex(s)
-def b2h(b: bytearray) -> Hexstr: +def b2h(b: bytearray) -> hexstr: """convert from a sequence of bytes to a string of hex nibbles""" - return ''.join(['%02x' % (x) for x in b]) + return hexstr(''.join(['%02x' % (x) for x in b]))
def h2i(s: Hexstr) -> List[int]: @@ -45,9 +77,9 @@ return [(int(x, 16) << 4)+int(y, 16) for x, y in zip(s[0::2], s[1::2])]
-def i2h(s: List[int]) -> Hexstr: +def i2h(s: List[int]) -> hexstr: """convert from a list of integers to a string of hex nibbles""" - return ''.join(['%02x' % (x) for x in s]) + return hexstr(''.join(['%02x' % (x) for x in s]))
def h2s(s: Hexstr) -> str: @@ -56,7 +88,7 @@ if int(x + y, 16) != 0xff])
-def s2h(s: str) -> Hexstr: +def s2h(s: str) -> hexstr: """convert from an ASCII string to a string of hex nibbles""" b = bytearray() b.extend(map(ord, s)) @@ -68,9 +100,9 @@ return ''.join([chr(x) for x in s])
-def swap_nibbles(s: Hexstr) -> Hexstr: +def swap_nibbles(s: Hexstr) -> hexstr: """swap the nibbles in a hex string""" - return ''.join([x+y for x, y in zip(s[1::2], s[0::2])]) + return hexstr(''.join([x+y for x, y in zip(s[1::2], s[0::2])]))
def rpad(s: str, l: int, c='f') -> str: @@ -138,36 +170,6 @@ except: return False
-class hexstr(str): - """Class derived from 'str', represeting a string of hexadecimal digits. It differs in that - comparisons are case-insensitive, and it offers encoding-free conversion from hexstr to bytes - and vice-versa.""" - def __new__(cls, s: str): - if not all(c in string.hexdigits for c in s): - raise ValueError('Input must be hexadecimal digits only') - # store as lower case digits - return super().__new__(cls, s.lower()) - - def __eq__(self, other: str) -> bool: - # make sure comparison is done case-insensitive - return str(self) == other.lower() - - def __getitem__(self, val) -> 'hexstr': - # make sure slicing a hexstr will return a hexstr - return hexstr(super().__getitem__(val)) - - def to_bytes(self) -> bytes: - """return hex-string converted to bytes""" - s = str(self) - if len(s) & 1: - raise ValueError('Cannot convert hex string with odd number of digits') - return h2b(s) - - @classmethod - def from_bytes(cls, bt: bytes) -> 'hexstr': - """instantiate hex-string from bytes""" - return cls(b2h(bt)) - ######################################################################### # ARGPARSE HELPERS #########################################################################