xcast - Safe Type Conversions¶
Safe type conversion functions that return None on failure instead of raising exceptions.
xpytools.xtype.xcast.as_int
¶
Convert a value to int, returning None if unsafe or invalid.
Source code in xpytools/xtype/xcast/primitives.py
def as_int(value: Any, safe: bool = True) -> Optional[int]:
"""Convert a value to int, returning None if unsafe or invalid."""
if value is None:
return None
try:
if is_int(value):
return int(value)
if is_float(value):
return int(float(value))
if is_str(value, non_empty=True) and is_int(value, allow_str=True):
return int(value)
if is_bool(value):
return int(value)
raise ValueError("Invalid integer value")
except Exception:
if not safe:
raise
return None
xpytools.xtype.xcast.as_float
¶
Convert a value to float, returning None if invalid.
Source code in xpytools/xtype/xcast/primitives.py
def as_float(value: Any, safe: bool = True) -> Optional[float]:
"""Convert a value to float, returning None if invalid."""
if value is None:
return None
try:
if is_float(value):
return float(value)
if is_int(value):
return float(value)
if is_str(value, non_empty=True) and is_float(value, allow_str=True):
return float(value)
raise ValueError("Invalid float value")
except Exception:
if not safe:
raise
return None
xpytools.xtype.xcast.as_bool
¶
Convert a value to bool, handling numeric and string truthy values.
Source code in xpytools/xtype/xcast/primitives.py
def as_bool(value: Any, safe: bool = True) -> Optional[bool]:
"""Convert a value to bool, handling numeric and string truthy values."""
if value is None:
return None
try:
if is_bool(value):
return bool(value)
if is_int(value) or is_float(value):
return float(value) != 0.0
if is_str(value, non_empty=True):
v = value.strip().lower()
if v in {"true", "yes", "1", "on"}:
return True
if v in {"false", "no", "0", "off"}:
return False
raise ValueError("Invalid boolean value")
except Exception:
if not safe:
raise
return bool(value)
xpytools.xtype.xcast.as_str
¶
as_str(value: Any, safe: bool = True, encoding: str = 'utf-8', errors: str = 'ignore') -> Optional[str]
Convert any object to a safe string.
- datetime → ISO string (via as_datetime_str)
- dict/list → JSON string (via as_json_str)
- UUID-like → canonical UUID string
- bytes/bytearray → decoded using UTF-8 (default)
- everything else → str(value)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Any
|
Input to convert. |
required |
|
bool
|
Swallow conversion errors if True. |
True
|
|
str
|
Encoding used for byte-to-string decoding. |
"utf-8"
|
|
str
|
Error handling strategy for decoding. |
"ignore"
|
Returns:
| Type | Description |
|---|---|
str | None
|
String representation or None if conversion fails. |
Examples:
>>> as_str(b"abc")
'abc'
>>> as_str({"x": 1})
'{\n "x": 1\n}'
>>> as_str(datetime(2025,1,1))
'2025-01-01T00:00:00+00:00'
>>> as_str(None)
None
Source code in xpytools/xtype/xcast/primitives.py
def as_str(
value: Any,
safe: bool = True,
encoding: str = "utf-8",
errors: str = "ignore",
) -> Optional[str]:
"""
Convert any object to a safe string.
- datetime → ISO string (via as_datetime_str)
- dict/list → JSON string (via as_json_str)
- UUID-like → canonical UUID string
- bytes/bytearray → decoded using UTF-8 (default)
- everything else → str(value)
Parameters
----------
value : Any
Input to convert.
safe : bool, default=True
Swallow conversion errors if True.
encoding : str, default="utf-8"
Encoding used for byte-to-string decoding.
errors : str, default="ignore"
Error handling strategy for decoding.
Returns
-------
str | None
String representation or None if conversion fails.
Examples
--------
>>> as_str(b"abc")
'abc'
>>> as_str({"x": 1})
'{\\n "x": 1\\n}'
>>> as_str(datetime(2025,1,1))
'2025-01-01T00:00:00+00:00'
>>> as_str(None)
None
"""
if value is None:
return None
try:
# Specialized types
if is_datetime_like(value):
return as_datetime_str(value)
if is_json_like(value):
return as_json_str(value)
if is_uuid(value):
return str(value)
# Decode bytes safely
if isinstance(value, (bytes, bytearray)):
try:
return value.decode(encoding, errors=errors)
except Exception:
# fallback: repr-based safe string
return repr(value) if safe else value.decode(encoding)
# Pass-through for strings
if is_str(value):
return value
# Fallback to default str() conversion
raise ValueError("Invalid string value")
except Exception:
if not safe:
raise
return str(value)
xpytools.xtype.xcast.as_bytes
¶
Safely encode any value as bytes.
Behavior:
- str → encoded with UTF-8
- dict/list → JSON-encoded (via as_json_str)
- everything else → coerced via str()
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Any
|
Input to encode. |
required |
|
str
|
Encoding used for string conversion. |
"utf-8"
|
|
bool
|
Swallow encoding errors if True. |
True
|
Returns:
| Type | Description |
|---|---|
bytes | None
|
Encoded bytes or None on failure. |
Examples:
Source code in xpytools/xtype/xcast/primitives.py
def as_bytes(value: Any, safe: bool = True, encoding: str = "utf-8") -> Optional[bytes]:
"""
Safely encode any value as bytes.
Behavior:
- str → encoded with UTF-8
- dict/list → JSON-encoded (via `as_json_str`)
- everything else → coerced via str()
Parameters
----------
value : Any
Input to encode.
encoding : str, default="utf-8"
Encoding used for string conversion.
safe : bool, default=True
Swallow encoding errors if True.
Returns
-------
bytes | None
Encoded bytes or None on failure.
Examples
--------
>>> as_bytes("abc")
b'abc'
>>> as_bytes({"a": 1})
b'{\\n "a": 1\\n}'
"""
if value is None:
return None
try:
if isinstance(value, bytes):
return value
if isinstance(value, str):
return value.encode(encoding, errors="ignore")
json_str = as_json_str(value)
if json_str is not None:
return json_str.encode(encoding, errors="ignore")
raise ValueError("Invalid bytes value")
except Exception:
if not safe:
raise
return str(value).encode(encoding, errors="ignore")
xpytools.xtype.xcast.as_json
¶
Convert JSON string or compatible Python object to dict/list.
Source code in xpytools/xtype/xcast/json.py
def as_json(value: Any, safe: bool = True) -> Optional[Union[dict, list]]:
"""Convert JSON string or compatible Python object to dict/list."""
if value is None:
return None
try:
if is_json_like(value):
if is_json(value):
return value
if is_str(value, non_empty=True):
return _as_json_obj(value, safe)
raise ValueError(f"Invalid JSON value: {value}")
except Exception:
if not safe:
raise
return None
xpytools.xtype.xcast.as_json_str
¶
as_json_str(value: Any, indent: int = 2, sort_keys: bool = False, safe: bool = True) -> Optional[str]
Safely serialize Python object to JSON string.
Returns None if object cannot be serialized.
Source code in xpytools/xtype/xcast/json.py
def as_json_str(value: Any, indent: int = 2, sort_keys: bool = False, safe: bool = True) -> Optional[str]:
"""
Safely serialize Python object to JSON string.
Returns None if object cannot be serialized.
"""
try:
return json.dumps(value, indent=indent, sort_keys=sort_keys, default=str)
except Exception:
if safe:
return None
else:
raise
xpytools.xtype.xcast.as_dict
¶
Safely convert input into a Python dict.
This uses as_json() internally, which supports:
- JSON strings
- Python dicts
- List-like inputs (which will be ignored if not dict-like)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Any
|
Input value to convert. |
required |
|
bool
|
Swallow conversion errors if True. |
True
|
Returns:
| Type | Description |
|---|---|
dict | None
|
A dictionary if successfully converted, otherwise None. |
Examples:
>>> as_dict('{"a":1}')
{'a': 1}
>>> as_dict({"b": 2})
{'b': 2}
>>> as_dict('[1,2,3]')
None
Source code in xpytools/xtype/xcast/complex.py
def as_dict(value: Any, safe: bool = True) -> Optional[dict]:
"""
Safely convert input into a Python dict.
This uses `as_json()` internally, which supports:
- JSON strings
- Python dicts
- List-like inputs (which will be ignored if not dict-like)
Parameters
----------
value : Any
Input value to convert.
safe : bool, default=True
Swallow conversion errors if True.
Returns
-------
dict | None
A dictionary if successfully converted, otherwise None.
Examples
--------
>>> as_dict('{"a":1}')
{'a': 1}
>>> as_dict({"b": 2})
{'b': 2}
>>> as_dict('[1,2,3]')
None
"""
try:
parsed = as_json(value)
if isinstance(parsed, dict):
return parsed
return None
except Exception:
if not safe:
raise
return None
xpytools.xtype.xcast.as_list
¶
Safely convert input into a Python list.
This uses as_json() internally, which supports:
- JSON arrays (e.g. '[1, 2, 3]')
- List/tuple/set
- Scalars (optionally wrapped in a list)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Any
|
Input value to convert. |
required |
|
bool
|
Swallow conversion errors if True. |
True
|
|
bool
|
Wrap single non-list values in a list for convenience. |
True
|
Returns:
| Type | Description |
|---|---|
list | None
|
A list if successfully converted, otherwise None. |
Examples:
>>> as_list('[1, 2, 3]')
[1, 2, 3]
>>> as_list((1, 2))
[1, 2]
>>> as_list('scalar', wrap_scalar=True)
['scalar']
Source code in xpytools/xtype/xcast/complex.py
def as_list(value: Any, safe: bool = True, wrap_scalar: bool = True) -> Optional[list]:
"""
Safely convert input into a Python list.
This uses `as_json()` internally, which supports:
- JSON arrays (e.g. '[1, 2, 3]')
- List/tuple/set
- Scalars (optionally wrapped in a list)
Parameters
----------
value : Any
Input value to convert.
safe : bool, default=True
Swallow conversion errors if True.
wrap_scalar : bool, default=True
Wrap single non-list values in a list for convenience.
Returns
-------
list | None
A list if successfully converted, otherwise None.
Examples
--------
>>> as_list('[1, 2, 3]')
[1, 2, 3]
>>> as_list((1, 2))
[1, 2]
>>> as_list('scalar', wrap_scalar=True)
['scalar']
"""
try:
parsed = as_json(value)
if isinstance(parsed, list):
return parsed
if isinstance(value, (list, tuple, set)):
return list(value)
if wrap_scalar and value is not None:
return [value]
raise ValueError(f"Invalid JSON value: {value}")
except Exception:
if not safe:
raise
return None
xpytools.xtype.xcast.as_datetime
¶
Convert string, timestamp, or datetime-like value to datetime.
Source code in xpytools/xtype/xcast/datetime.py
def as_datetime(value: Any, safe: bool = True, assume_tz_utc: bool = True) -> Optional[datetime]:
"""Convert string, timestamp, or datetime-like value to datetime."""
try:
if is_datetime(value):
return value
if is_datetime_like(value):
return _to_datetime(value, assume_tz_utc)
if is_int(value) or is_float(value):
return _to_datetime(float(value), assume_tz_utc)
raise ValueError(f"Invalid datetime value: {value}")
except Exception:
if not safe:
raise
return None
xpytools.xtype.xcast.as_datetime_str
¶
as_datetime_str(value: datetime, include_time: bool = True, include_utc: bool = False) -> Optional[str]
Safely convert a datetime object into an ISO-like string.
This function gracefully handles None, naive datetimes, and timezone-aware datetimes. It is designed for display or JSON serialization, providing consistent formatting options.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
datetime
|
Datetime object to format. |
required |
|
bool
|
Whether to include time components (HH:MM:SS). |
True
|
|
bool
|
Whether to include timezone info (if present or assume UTC). |
True
|
Returns:
| Type | Description |
|---|---|
str | None
|
Formatted datetime string, or None if input is invalid. |
Examples:
>>> from datetime import datetime, timezone
>>> as_datetime_str(datetime(2025, 1, 1, 12, 30, tzinfo=timezone.utc))
'2025-01-01T12:30:00+00:00'
>>> as_datetime_str(datetime(2025, 1, 1),include_utc=False)
'2025-01-01T00:00:00'
Source code in xpytools/xtype/xcast/datetime.py
def as_datetime_str(value: datetime, include_time: bool = True, include_utc: bool = False) -> Optional[str]:
"""
Safely convert a datetime object into an ISO-like string.
This function gracefully handles None, naive datetimes, and timezone-aware
datetimes. It is designed for display or JSON serialization, providing
consistent formatting options.
Parameters
----------
value : datetime
Datetime object to format.
include_time : bool, default=True
Whether to include time components (HH:MM:SS).
include_utc : bool, default=True
Whether to include timezone info (if present or assume UTC).
Returns
-------
str | None
Formatted datetime string, or None if input is invalid.
Examples
--------
>>> from datetime import datetime, timezone
>>> as_datetime_str(datetime(2025, 1, 1, 12, 30, tzinfo=timezone.utc))
'2025-01-01T12:30:00+00:00'
>>> as_datetime_str(datetime(2025, 1, 1),include_utc=False)
'2025-01-01T00:00:00'
>>> as_datetime_str(None)
None
"""
value = as_datetime(value, safe = True, assume_tz_utc = include_utc)
if not is_datetime(value):
return None
try:
# Ensure timezone if requested
dt = value
if include_utc and dt.tzinfo is None:
dt = dt.replace(tzinfo=timezone.utc)
elif not include_utc and dt.tzinfo is not None:
dt = dt.replace(tzinfo=None)
if include_time:
fmt = "%Y-%m-%dT%H:%M:%S"
else:
fmt = "%Y-%m-%d"
# Append timezone offset if required
if include_utc:
fmt += "%z"
result = dt.strftime(fmt)
# Normalize "+0000" → "+00:00"
if include_utc and result.endswith("+0000"):
result = result[:-5] + "+00:00"
return result
except Exception:
return None
xpytools.xtype.xcast.as_df
¶
Safely convert various input types into a pandas DataFrame.
This function supports: - Already a DataFrame → returns as-is - Dict, list of dicts, or JSON string → converts via pandas.DataFrame - Returns None for invalid or unconvertible inputs - Returns None if pandas is not installed (instead of raising ImportError)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Any
|
Input value to convert (DataFrame, dict, list, JSON string, etc.) |
required |
|
bool
|
If True, swallows conversion errors and returns None instead of raising. |
True
|
Returns:
| Type | Description |
|---|---|
Optional[DataFrame]
|
A pandas DataFrame if conversion succeeds, otherwise None. |
Examples:
>>> import pandas as pd
>>> as_df(pd.DataFrame({'x':[1,2]}))
x
0 1
1 2
>>> as_df('[{"a":1},{"a":2}]')
a
0 1
1 2
>>> as_df({'a':[1,2,3]})
a
0 1
1 2
2 3
>>> as_df("bad json") is None
True
Source code in xpytools/xtype/xcast/dataframe.py
@requireModules(["pandas"], exc_raise=True)
def as_df(value: Any, safe: bool = True) -> Optional["pdDataFrame"]:
"""
Safely convert various input types into a pandas DataFrame.
This function supports:
- Already a DataFrame → returns as-is
- Dict, list of dicts, or JSON string → converts via pandas.DataFrame
- Returns None for invalid or unconvertible inputs
- Returns None if pandas is not installed (instead of raising ImportError)
Parameters
----------
value : Any
Input value to convert (DataFrame, dict, list, JSON string, etc.)
safe : bool, default=True
If True, swallows conversion errors and returns None instead of raising.
Returns
-------
Optional[pandas.DataFrame]
A pandas DataFrame if conversion succeeds, otherwise None.
Examples
--------
>>> import pandas as pd
>>> as_df(pd.DataFrame({'x':[1,2]}))
x
0 1
1 2
>>> as_df('[{"a":1},{"a":2}]')
a
0 1
1 2
>>> as_df({'a':[1,2,3]})
a
0 1
1 2
2 3
>>> as_df("bad json") is None
True
"""
try:
# Import inside function to avoid hard pandas dependency
from pandas import DataFrame as pdDataFrame
# Already a DataFrame → return as-is
if is_df(value):
return value
# JSON-like (dict, list, or valid JSON string)
if is_json_like(value):
parsed = as_json(value)
if parsed is None:
raise ValueError("Invalid JSON value")
return pdDataFrame(parsed)
# Try generic coercion (lists of tuples, numpy arrays, etc.)
try:
return pdDataFrame(value)
except Exception:
raise
except Exception:
if not safe:
raise
return None
xpytools.xtype.xcast.as_none
¶
Normalize any "null-like" value into Python None.
This function uses is_none() internally to detect:
- None
- NaN (float or numpy)
- pandas.NA / pandas.NaT
- string representations ("null", "nan", "n/a", "")
If the value is not considered null-like, it is returned unchanged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Any
|
Input value to normalize. |
required |
|
bool
|
If False, re-raises unexpected exceptions. |
True
|
Returns:
| Type | Description |
|---|---|
Any | None
|
None if input is null-like, otherwise original value. |
Examples:
Source code in xpytools/xtype/xcast/null.py
def as_none(value: Any, safe: bool = True) -> Optional[Any]:
"""
Normalize any "null-like" value into Python None.
This function uses `is_none()` internally to detect:
- None
- NaN (float or numpy)
- pandas.NA / pandas.NaT
- string representations ("null", "nan", "n/a", "")
If the value is not considered null-like, it is returned unchanged.
Parameters
----------
value : Any
Input value to normalize.
safe : bool, default=True
If False, re-raises unexpected exceptions.
Returns
-------
Any | None
None if input is null-like, otherwise original value.
Examples
--------
>>> as_none("NaN")
None
>>> as_none(pd.NA)
None
>>> as_none(123)
123
"""
try:
if is_none(value):
return None
else:
return value
except Exception:
if not safe:
raise
return None
xpytools.xtype.xcast.to_primitives.to_primitives
¶
Recursively coerce obj into a JSON-serializable structure.
Behavior
- Dataclasses → dict
- Pydantic models → dict (handles both v1 & v2)
- pandas.DataFrame → list[dict]
- pandas.Series → list
- NumPy scalars / arrays → native Python types
- Enum → enum.value
- NaN / pd.NA / None-like → None
- Nested containers handled recursively
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Any
|
Input of arbitrary or nested type. |
required |
Returns:
| Type | Description |
|---|---|
Any
|
JSON-safe structure: only dict, list, str, int, float, bool, or None. |
Examples:
>>> from dataclasses import dataclass
>>> import numpy as np, pandas as pd
>>> @dataclass
... class Example: x: int; y: float
>>> to_primitives(Example(1, np.nan))
{'x': 1, 'y': None}
>>> to_primitives(pd.DataFrame({'a':[1, None]}))
[{'a': 1}, {'a': None}]
>>> from xpyt_pydantic import BaseModel
>>> class M(BaseModel): a: int; b: float
>>> to_primitives(M(a=1, b=float("nan")))
{'a': 1, 'b': None}
Source code in xpytools/xtype/xcast/to_primitives.py
def to_primitives(obj: Any) -> Any:
"""
Recursively coerce `obj` into a JSON-serializable structure.
Behavior
--------
- Dataclasses → dict
- Pydantic models → dict (handles both v1 & v2)
- pandas.DataFrame → list[dict]
- pandas.Series → list
- NumPy scalars / arrays → native Python types
- Enum → enum.value
- NaN / pd.NA / None-like → None
- Nested containers handled recursively
Parameters
----------
obj : Any
Input of arbitrary or nested type.
Returns
-------
Any
JSON-safe structure: only dict, list, str, int, float, bool, or None.
Examples
--------
>>> from dataclasses import dataclass
>>> import numpy as np, pandas as pd
>>> @dataclass
... class Example: x: int; y: float
>>> to_primitives(Example(1, np.nan))
{'x': 1, 'y': None}
>>> to_primitives(pd.DataFrame({'a':[1, None]}))
[{'a': 1}, {'a': None}]
>>> from xpyt_pydantic import BaseModel
>>> class M(BaseModel): a: int; b: float
>>> to_primitives(M(a=1, b=float("nan")))
{'a': 1, 'b': None}
"""
# --- Null / None-like ---------------------------------------------------
if is_none(obj):
return None
# --- Dataclasses --------------------------------------------------------
if is_dataclass(obj):
return to_primitives(asdict(obj))
# --- Pydantic models ----------------------------------------------------
if pyd is not None:
# Pydantic v1: BaseModel
if hasattr(obj, "dict") and callable(getattr(obj, "dict", None)):
try:
return to_primitives(obj.dict())
except Exception:
pass
# Pydantic v2: BaseModel.model_dump()
if hasattr(obj, "model_dump") and callable(getattr(obj, "model_dump", None)):
try:
return to_primitives(obj.model_dump())
except Exception:
pass
# --- Dicts --------------------------------------------------------------
if is_dict(obj):
return {k: to_primitives(v) for k, v in obj.items()}
# --- Iterables / list-like ---------------------------------------------
if is_list_like(obj):
return [to_primitives(v) for v in obj]
# --- Enum ---------------------------------------------------------------
if isinstance(obj, Enum):
return to_primitives(obj.value)
# --- pandas / NumPy integration ----------------------------------------
if is_df(obj):
try:
return to_primitives(
obj.replace({pd.NA: None, np.nan: None}).to_dict(orient="records")
)
except Exception:
return None
if pd is not None and isinstance(obj, getattr(pd, "Series", ())):
try:
return to_primitives(obj.dropna().tolist())
except Exception:
return None
if np is not None:
# NumPy scalar
if isinstance(obj, np.generic):
return obj.item()
# NumPy array
if isinstance(obj, np.ndarray):
try:
return to_primitives(
np.where(pd.isna(obj), None, obj).tolist()
if pd is not None else obj.tolist()
)
except Exception:
return to_primitives(obj.tolist())
# --- Primitive / fallback ----------------------------------------------
if isinstance(obj, float) and np is not None and np.isnan(obj):
return None
if pd is not None and getattr(pd, "isna", None) and pd.isna(obj):
return None
if isinstance(obj, (str, int, float, bool)) or obj is None:
return obj
# --- Fallback -----------------------------------------------------------
# Try to stringify remaining non-serializable types
try:
return str(obj)
except Exception:
return None