Source code for autojob.utils
"""Miscellaneous `autojob` utility functions."""
from collections.abc import Iterable
from typing import TypeVar
from autojob.utils.files import get_slurm_job_id
from autojob.utils.files import get_uri
from autojob.utils.parsing import parse_job_stats_file
__all__ = [
"alphanum_key",
"alphanum_sort",
"get_slurm_job_id",
"get_uri",
"iter_to_native",
"parse_job_stats_file",
"val_to_native",
]
PRIMITIVE_TYPE = bool | float | int | str | None
_T = TypeVar("_T")
[docs]
def alphanum_key(val: str) -> tuple[str, PRIMITIVE_TYPE]:
"""Provides key to alphanumerically sort primitive types.
Args:
val: String representation of object for which to provide key.
Raises:
TypeError: The type of 'val' is invalid.
Returns:
A 2-tuple where the first element is a string indicating the type of
the value (e.g., n = number, b = boolean, N = None, s = string) and
the second element is the value.
Note:
Numbers are converted to floats.
"""
if not isinstance(val, str):
msg = (
f"Type: {type(val)} not supported. alphanum_key "
"only supports arguments of type: str."
)
raise TypeError(msg)
try:
val_key = float(val)
type_key = "n"
except ValueError:
match val:
case "True" | "False":
type_key = "b"
val_key = val == "True"
case "None":
type_key = "N"
val_key = None
case _:
type_key = "s"
val_key = val
return type_key, val_key
[docs]
def alphanum_sort(vals: Iterable[str]) -> list[str]:
"""Alphanumerically sorts an iterable of strings.
Args:
vals: an iterable to be sorted.
Returns:
Alphanumerically sorted copy of ``vals``.
"""
return sorted(vals, key=alphanum_key)
[docs]
def val_to_native(val: float | int | str | None) -> PRIMITIVE_TYPE:
"""Converts string representations to their native types.
Only floats, ints, or strings are supported.
Args:
val: a value to be converted.
Returns:
The value converted into a :attr:`PRIMITIVE_TYPE`.
"""
try:
float_val = float(val)
try:
int_val = int(val)
native_val = int_val if int_val == float_val else float_val
except ValueError:
native_val = float_val
except (ValueError, TypeError):
if val == "True":
native_val = True
elif val == "False":
native_val = False
elif val == "None":
native_val = None
else:
native_val = val
return native_val
[docs]
def iter_to_native(
vals: Iterable[float | int | str | None],
) -> Iterable[float | int | str | None]:
"""Converts values within an Iterable to their native types.
Args:
vals: an iterable of values to convert.
Returns:
Iterable: A shallow copy of the converted iterable.
Example:
>>> from autojob.utils import iter_to_native
>>> iter_to_native(["0.1", "None", "-1", "dog"])
[0.1, None, -1, 'dog']
"""
new_vals = []
constructor = type(vals)
for val in vals:
new_vals.append(val_to_native(val))
try:
return constructor(new_vals)
except (TypeError, ValueError):
return list(new_vals)