#!/usr/bin/env python
# cardinal_pythonlib/convert.py
"""
===============================================================================
Original code copyright (C) 2009-2022 Rudolf Cardinal (rudolf@pobox.com).
This file is part of cardinal_pythonlib.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
===============================================================================
**Miscellaneous other conversions.**
"""
import base64
import binascii
import re
from typing import Any, Iterable, Optional
from cardinal_pythonlib.logs import get_brace_style_log_with_null_handler
log = get_brace_style_log_with_null_handler(__name__)
# =============================================================================
# Simple type converters
# =============================================================================
[docs]def convert_to_bool(x: Any, default: bool = None) -> bool:
"""
Transforms its input to a ``bool`` (or returns ``default`` if ``x`` is
falsy but not itself a boolean). Accepts various common string versions.
"""
if isinstance(x, bool):
return x
if not x: # None, zero, blank string...
return default
try:
return int(x) != 0
except (TypeError, ValueError):
pass
try:
return float(x) != 0
except (TypeError, ValueError):
pass
if not isinstance(x, str):
raise Exception(f"Unknown thing being converted to bool: {x!r}")
x = x.upper()
if x in ["Y", "YES", "T", "TRUE"]:
return True
if x in ["N", "NO", "F", "FALSE"]:
return False
raise Exception(f"Unknown thing being converted to bool: {x!r}")
[docs]def convert_to_int(x: Any, default: int = None) -> int:
"""
Transforms its input into an integer, or returns ``default``.
"""
try:
return int(x)
except (TypeError, ValueError):
return default
# =============================================================================
# Attribute converters
# =============================================================================
[docs]def convert_attrs_to_bool(
obj: Any, attrs: Iterable[str], default: bool = None
) -> None:
"""
Applies :func:`convert_to_bool` to the specified attributes of an object,
modifying it in place.
"""
for a in attrs:
setattr(obj, a, convert_to_bool(getattr(obj, a), default=default))
[docs]def convert_attrs_to_uppercase(obj: Any, attrs: Iterable[str]) -> None:
"""
Converts the specified attributes of an object to upper case, modifying
the object in place.
"""
for a in attrs:
value = getattr(obj, a)
if value is None:
continue
setattr(obj, a, value.upper())
[docs]def convert_attrs_to_lowercase(obj: Any, attrs: Iterable[str]) -> None:
"""
Converts the specified attributes of an object to lower case, modifying
the object in place.
"""
for a in attrs:
value = getattr(obj, a)
if value is None:
continue
setattr(obj, a, value.lower())
[docs]def convert_attrs_to_int(
obj: Any, attrs: Iterable[str], default: int = None
) -> None:
"""
Applies :func:`convert_to_int` to the specified attributes of an object,
modifying it in place.
"""
for a in attrs:
value = convert_to_int(getattr(obj, a), default=default)
setattr(obj, a, value)
# =============================================================================
# Encoding: binary as hex in X'...' format
# =============================================================================
REGEX_HEX_XFORMAT = re.compile(
"""
^X' # begins with X'
([a-fA-F0-9][a-fA-F0-9])+ # one or more hex pairs
'$ # ends with '
""",
re.X,
) # re.X allows whitespace/comments in regex
REGEX_BASE64_64FORMAT = re.compile(
"""
^64' # begins with 64'
(?: [A-Za-z0-9+/]{4} )* # zero or more quads, followed by...
(?:
[A-Za-z0-9+/]{2} [AEIMQUYcgkosw048] = # a triple then an =
| # or
[A-Za-z0-9+/] [AQgw] == # a pair then ==
)?
'$ # ends with '
""",
re.X,
) # re.X allows whitespace/comments in regex
# =============================================================================
# Encoding: binary as hex in 64'...' format (which is idiosyncratic!)
# =============================================================================