Module par_ai_core.utils
Various types, utility functions and decorators for the par_ai_core package.
This module provides a wide range of utility functions and decorators to support common operations in AI and data processing tasks. It includes functions for:
- String manipulation (e.g., camel case to snake case conversion)
- Data structure operations (e.g., nested dictionary access)
- File and I/O operations (e.g., CSV parsing, file reading)
- Type checking and conversion
- Hashing and encryption
- Command execution and shell interactions
- Timing and performance measurement
- Exception handling and logging
- UUID validation
- Environment variable management
The module also includes several context managers for temporary modifications to system state or execution environment.
These utilities are designed to streamline development and improve code readability across the par_ai_core package and related projects.
Functions
def add_module_path(path: str) ‑> Generator[None, None, None]
-
Expand source code
@contextmanager def add_module_path(path: str) -> Generator[None, None, None]: """Add a module path to sys.path temporarily.""" sys.path.append(path) try: yield finally: if path in sys.path: sys.path.remove(path)
Add a module path to sys.path temporarily.
def all_subclasses(cls: type) ‑> set[type]
-
Expand source code
def all_subclasses(cls: type) -> set[type]: """Return all subclasses of a given class.""" return set(cls.__subclasses__()).union([s for c in cls.__subclasses__() for s in all_subclasses(c)])
Return all subclasses of a given class.
def camel_to_snake(name: str) ‑> str
-
Expand source code
def camel_to_snake(name: str) -> str: """Convert name from CamelCase to snake_case. Args: name: A symbol name, such as a class name. Returns: Name in snake case. Examples: >>> camel_to_snake("camelCase") 'camel_case' >>> camel_to_snake("ThisIsATest") 'this_is_a_test' >>> camel_to_snake("ABC") 'abc' """ # Special case for all uppercase strings if name.isupper(): return name.lower() pattern = re.compile(r"(?<!^)(?<!_)(?:[A-Z][a-z]+|[A-Z]+(?=[A-Z][a-z]|\d|\W|$))") return pattern.sub(lambda m: f"_{m.group(0).lower()}", name).lower()
Convert name from CamelCase to snake_case.
Args
name
- A symbol name, such as a class name.
Returns
Name in snake case.
Examples
>>> camel_to_snake("camelCase") 'camel_case' >>> camel_to_snake("ThisIsATest") 'this_is_a_test' >>> camel_to_snake("ABC") 'abc'
def catch_to_logger(logger: object, re_throw: bool = False) ‑> Generator[None, None, None]
-
Expand source code
@contextmanager def catch_to_logger(logger: object, re_throw: bool = False) -> Generator[None, None, None]: """Catch exceptions and log them to a logger.""" try: yield except Exception as e: if logger and hasattr(logger, "exception"): logger.exception(e) # type: ignore if re_throw: raise e else: raise e
Catch exceptions and log them to a logger.
def chunks(lst: list[Any], n: int) ‑> Generator[list[typing.Any], None, None]
-
Expand source code
def chunks(lst: list[Any], n: int) -> Generator[list[Any], None, None]: """Yield successive n-sized chunks from a list. Args: lst: The list to split into chunks n: The size of each chunk Returns: Generator[list[Any], None, None]: Generator yielding chunks of the list """ for i in range(0, len(lst), n): yield lst[i : i + n]
Yield successive n-sized chunks from a list.
Args
lst
- The list to split into chunks
n
- The size of each chunk
Returns
Generator[list[Any], None, None]
- Generator yielding chunks of the list
def coalesce(*arg: Any) ‑> Any
-
Expand source code
def coalesce(*arg: Any) -> Any: """Return first non-None item from the provided arguments. Args: *arg: Variable number of arguments to check. Returns: Any: The first non-None item in the arguments. If all arguments are None, returns None. Example: >>> coalesce(None, "", 0, "hello") '' >>> coalesce(None, None, 42) 42 """ return next((a for a in arg if a is not None), None)
Return first non-None item from the provided arguments.
Args
*arg
- Variable number of arguments to check.
Returns
Any
- The first non-None item in the arguments.
If all arguments are None, returns None.
Example
>>> coalesce(None, "", 0, "hello") '' >>> coalesce(None, None, 42) 42
def detect_syntax(text: str) ‑> str | None
-
Expand source code
def detect_syntax(text: str) -> str | None: """Detect the syntax of the text.""" lines = text.split("\n") if len(lines) > 0: line = lines[0] if line.startswith("#!"): if line.endswith("/bash") or line.endswith("/sh") or line.endswith(" bash") or line.endswith(" sh"): return "bash" return None
Detect the syntax of the text.
def dict_keys_to_lower(dictionary: dict) ‑> dict
-
Expand source code
def dict_keys_to_lower(dictionary: dict) -> dict: """ Return a new dictionary with all keys lowercase @param dictionary: dict with keys that you want to lowercase @return: new dictionary with lowercase keys """ return {k.lower(): v for k, v in dictionary.items()}
Return a new dictionary with all keys lowercase @param dictionary: dict with keys that you want to lowercase @return: new dictionary with lowercase keys
def gather_files_for_context(file_patterns: list[str | Path], max_context_length: int = 0) ‑> str
-
Expand source code
def gather_files_for_context(file_patterns: list[str | Path], max_context_length: int = 0) -> str: """ Gather files for context. Args: file_patterns (list[str | Path]): List of file glob patterns to match max_context_length (int, optional): Maximum context length. Defaults to 0 (no limit). Returns: str: xml formatted list of files and their contents """ files = get_file_list_for_context(file_patterns) if not files: return "<files>\n</files>\n" if max_context_length < 0: max_context_length = 0 doc = StringIO() doc.write("<files>\n") i: int = 0 curr_len = 17 for file in files: try: st = f"""<file index="{i}">\n<source>{html.escape(file.as_posix(), quote=True)}</source>\n<file-content>{html.escape(file.read_text(encoding="utf-8"), quote=True)}</file-content>\n</file>\n""" if max_context_length and curr_len + len(st) > max_context_length: break doc.write(st) curr_len += len(st) i += 1 except Exception as _: pass doc.write("</files>\n") return doc.getvalue()
Gather files for context.
Args
file_patterns
:list[str | Path]
- List of file glob patterns to match
max_context_length
:int
, optional- Maximum context length. Defaults to 0 (no limit).
Returns
str
- xml formatted list of files and their contents
def get_file_list_for_context(file_patterns: list[str | Path]) ‑> list[pathlib.Path]
-
Expand source code
def get_file_list_for_context(file_patterns: list[str | Path]) -> list[Path]: """ Gather files for context. Args: file_patterns (list[str | Path]): List of file glob patterns to match Returns: list[Path]: List of files matching the patterns """ files = [] for pattern in file_patterns: try: if isinstance(pattern, Path): pattern = pattern.as_posix() if sys.version_info >= (3, 11): files += glob.glob(pattern, recursive=True, include_hidden=False) else: files += glob.glob(pattern, recursive=True) except Exception as _: raise _ result = [] for file in files: f = Path(file) if f.is_file(): f_path = str(f.as_posix()) if ( f.name.startswith(".") or "/.git/" in f_path or "/.venv/" in f_path or "/venv/" in f_path or "/node_modules/" in f_path or "/__pycache__/" in f_path ): continue result.append(f) return result
Gather files for context.
Args
file_patterns
:list[str | Path]
- List of file glob patterns to match
Returns
list[Path]
- List of files matching the patterns
def get_files(path: str | Path | os.PathLike[str], ext: str = '') ‑> list[str]
-
Expand source code
def get_files(path: str | Path | os.PathLike[str], ext: str = "") -> list[str]: """Get list of files in a directory, optionally filtered by extension. Args: path: Directory path to search ext: File extension to filter by. If empty, returns all files. Defaults to "". Returns: list[str]: Alphabetically sorted list of filenames in the directory, excluding files ending with the specified extension if provided. """ ret = [f for f in os.listdir(path) if isfile(join(path, f)) and (not ext or not f.endswith(ext))] ret.sort() return ret
Get list of files in a directory, optionally filtered by extension.
Args
path
- Directory path to search
ext
- File extension to filter by. If empty, returns all files. Defaults to "".
Returns
list[str]
- Alphabetically sorted list of filenames in the directory, excluding files ending with the specified extension if provided.
def get_url_file_suffix(url: str, default='.jpg') ‑> str
-
Expand source code
def get_url_file_suffix(url: str, default=".jpg") -> str: """ Get url file suffix Args: url (str): URL default (str): Default file suffix if none found Returns: str: File suffix in lowercase with leading dot """ parsed_url = urlparse(url) filename = os.path.basename(parsed_url.path) suffix = os.path.splitext(filename)[1].lower() return suffix or default
Get url file suffix
Args
url
:str
- URL
default
:str
- Default file suffix if none found
Returns
str
- File suffix in lowercase with leading dot
def has_stdin_content() ‑> bool
-
Expand source code
def has_stdin_content() -> bool: """Check if there is content available on stdin. Returns: bool: True if there is content available on stdin, False otherwise. """ if sys.stdin.isatty(): return False # For Windows if os.name == "nt": import msvcrt return msvcrt.kbhit() # For Unix-like systems (Linux and macOS) else: # First check if stdin is readable if hasattr(sys.stdin, "readable") and not sys.stdin.readable(): return False import select rlist, _, _ = select.select([sys.stdin], [], [], 0) return bool(rlist)
Check if there is content available on stdin.
Returns
bool
- True if there is content available on stdin, False otherwise.
def has_value(v: Any, search: str, depth: int = 0) ‑> bool
-
Expand source code
def has_value(v: Any, search: str, depth: int = 0) -> bool: """Recursively search a data structure for a value. Args: v: The data structure to search (can be dict, list, or primitive type). search: The string value to search for. depth: Current recursion depth (used internally, defaults to 0). Returns: bool: True if the search value is found, False otherwise. Notes: - Searches dictionaries recursively up to depth of 4 - For integers, trims .00 from search string before comparing - For floats, truncates to length of search string before comparing - For strings, checks if they start or end with search value (case-insensitive) """ # don't go more than 3 levels deep if depth > 4: return False # if is a dict, search all dict values recursively if isinstance(v, dict): for dv in v.values(): if has_value(dv, search, depth + 1): return True # if is a list, search all list values recursively if isinstance(v, list): for li in v: if has_value(li, search, depth + 1): return True # if is an int, trim off .00 for search if it exists then compare if isinstance(v, int): search = search.rstrip(".00") if str(v) == search: return True # if is a float, truncate string version of float to same size as search if isinstance(v, float): v = str(v)[0 : len(search)] if search == v: return True # if is a string, strip and lowercase it then check if string starts with search if isinstance(v, str): if v.strip().lower().startswith(search) or v.strip().lower().endswith(search): return True return False
Recursively search a data structure for a value.
Args
v
- The data structure to search (can be dict, list, or primitive type).
search
- The string value to search for.
depth
- Current recursion depth (used internally, defaults to 0).
Returns
bool
- True if the search value is found, False otherwise.
Notes
- Searches dictionaries recursively up to depth of 4
- For integers, trims .00 from search string before comparing
- For floats, truncates to length of search string before comparing
- For strings, checks if they start or end with search value (case-insensitive)
def hash_list_by_key(data: list[dict], id_key: str = 'message_id') ‑> dict
-
Expand source code
def hash_list_by_key(data: list[dict], id_key: str = "message_id") -> dict: """Hash a list of dictionaries by a key.""" return {item[id_key]: item for item in data}
Hash a list of dictionaries by a key.
def id_generator(size: int = 6, chars: str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') ‑> str
-
Expand source code
def id_generator(size: int = 6, chars: str = string.ascii_uppercase + string.digits) -> str: """Generate a random string of uppercase letters and digits. Args: size: Length of the string to generate. Defaults to 6. chars: Characters to use for the string. Defaults to uppercase letters and digits. Returns: str: The generated random string """ return "".join(random.choice(chars) for _ in range(size))
Generate a random string of uppercase letters and digits.
Args
size
- Length of the string to generate. Defaults to 6.
chars
- Characters to use for the string. Defaults to uppercase letters and digits.
Returns
str
- The generated random string
def is_date(date_text: str, fmt: str = '%Y/%m/%d') ‑> bool
-
Expand source code
def is_date(date_text: str, fmt: str = "%Y/%m/%d") -> bool: """Test if a string represents a valid date in the specified format. Args: date_text: String to test as a date. fmt: Date format string using strftime format codes. Defaults to "%Y/%m/%d". Returns: bool: True if the string represents a valid date in the specified format, False otherwise. Example: >>> is_date("2024/01/20") True >>> is_date("2024-01-20", fmt="%Y-%m-%d") True """ try: datetime.strptime(date_text, fmt) return True except ValueError: return False
Test if a string represents a valid date in the specified format.
Args
date_text
- String to test as a date.
fmt
- Date format string using strftime format codes. Defaults to "%Y/%m/%d".
Returns
bool
- True if the string represents a valid date in the specified format,
False otherwise.
Example
>>> is_date("2024/01/20") True >>> is_date("2024-01-20", fmt="%Y-%m-%d") True
def is_float(s: Any) ‑> bool
-
Expand source code
def is_float(s: Any) -> bool: """Test if a value can be converted to float. Args: s: Any value to test. Returns: bool: True if the value can be converted to float, False otherwise. Example: >>> is_float("3.14") True >>> is_float("abc") False """ try: float(s) return True except (ValueError, TypeError): return False
Test if a value can be converted to float.
Args
s
- Any value to test.
Returns
bool
- True if the value can be converted to float, False otherwise.
Example
>>> is_float("3.14") True >>> is_float("abc") False
def is_int(s: Any) ‑> bool
-
Expand source code
def is_int(s: Any) -> bool: """Test if a value can be converted to integer. Args: s: Any value to test. Returns: bool: True if the value can be converted to integer, False otherwise. Example: >>> is_int("42") True >>> is_int("3.14") False """ try: int(s) return True except (ValueError, TypeError): return False
Test if a value can be converted to integer.
Args
s
- Any value to test.
Returns
bool
- True if the value can be converted to integer, False otherwise.
Example
>>> is_int("42") True >>> is_int("3.14") False
def is_url(url: str) ‑> bool
-
Expand source code
def is_url(url: str) -> bool: """ Return True if the given string is a valid URL. Args: url (str): The string to check. Returns: bool: True if the string is a valid URL, False otherwise. """ try: result = urlparse(url) matches = re.match(r"^https?://", url) is not None return all([result.scheme, result.netloc, matches]) except ValueError: return False
Return True if the given string is a valid URL.
Args
url
:str
- The string to check.
Returns
bool
- True if the string is a valid URL, False otherwise.
def is_valid_uuid_v4(value: str) ‑> bool
-
Expand source code
def is_valid_uuid_v4(value: str) -> bool: """Test if value is a valid UUID v4.""" try: uuid_obj = uuid.UUID(value, version=4) return str(uuid_obj) == value # Check if the string representation matches except ValueError: return False
Test if value is a valid UUID v4.
def is_zero(val: Any) ‑> bool
-
Expand source code
def is_zero(val: Any) -> bool: """Test if a value equals zero, handling different numeric types. Args: val: Value to test (can be int, float, or Decimal). Returns: bool: True if the value equals zero, False otherwise. Returns False for None values. Notes: - For Decimal, rounds to DECIMAL_PRECESSION before comparing - For float, uses math.isclose() with relative tolerance of 1e-05 - For int, uses exact comparison """ if val is None: return False t = type(val) if t is Decimal: return val.quantize(Decimal(f"1e-{DECIMAL_PRECESSION}")).is_zero() if t is float: return math.isclose(round(val, 5), 0, rel_tol=1e-05) if t is int: return 0 == val return False
Test if a value equals zero, handling different numeric types.
Args
val
- Value to test (can be int, float, or Decimal).
Returns
bool
- True if the value equals zero, False otherwise.
Returns False for None values.
Notes
- For Decimal, rounds to DECIMAL_PRECESSION before comparing
- For float, uses math.isclose() with relative tolerance of 1e-05
- For int, uses exact comparison
def json_serial(obj: Any) ‑> str
-
Expand source code
def json_serial(obj: Any) -> str: """ JSON serializer for objects not serializable by default json code. :param obj: The object to serialize. :return: The serialized object. """ if isinstance(obj, datetime | date): return obj.isoformat() raise TypeError(f"Type {type(obj)} not serializable")
JSON serializer for objects not serializable by default json code.
:param obj: The object to serialize. :return: The serialized object.
def md(soup: BeautifulSoup, **options: Any) ‑> str
-
Expand source code
def md(soup: BeautifulSoup, **options: Any) -> str: """Convert BeautifulSoup object to Markdown. Args: soup: The BeautifulSoup object to convert **options: Additional options to pass to the MarkdownConverter Returns: str: The converted Markdown string """ return MarkdownConverter(**options).convert_soup(soup)
Convert BeautifulSoup object to Markdown.
Args
soup
- The BeautifulSoup object to convert
**options
- Additional options to pass to the MarkdownConverter
Returns
str
- The converted Markdown string
def md5_hash(data: str) ‑> str
-
Expand source code
def md5_hash(data: str) -> str: """ Returns a md5 hash of the input data. Args: data (str): The input data. Returns: str: The md5 hash of the input data. """ md5 = hashlib.md5(data.encode("utf-8")) return md5.hexdigest()
Returns a md5 hash of the input data.
Args
data
:str
- The input data.
Returns
str
- The md5 hash of the input data.
def nested_get(dictionary: dict, keys: str | list[str]) ‑> Any
-
Expand source code
def nested_get(dictionary: dict, keys: str | list[str]) -> Any: """ Returns the value for a given key in a nested dictionary. Args: dictionary (dict): The nested dictionary to search. keys (str | list[str]): The key or list of keys to search for. Returns: Any: The value for the given key or None if the key does not exist. """ if isinstance(keys, str): keys = keys.split(".") if keys and dictionary: element = keys[0] if element in dictionary: if len(keys) == 1: return dictionary[element] return nested_get(dictionary[element], keys[1:]) return None
Returns the value for a given key in a nested dictionary.
Args
dictionary
:dict
- The nested dictionary to search.
keys
:str | list[str]
- The key or list of keys to search for.
Returns
Any
- The value for the given key or None if the key does not exist.
def non_zero(val: Any) ‑> bool
-
Expand source code
def non_zero(val: Any) -> bool: """Test if a value is not equal to zero. Args: val: Value to test (can be int, float, or Decimal). Returns: bool: True if the value is not zero, False if it is zero. Returns True for None values. Note: This is the inverse of is_zero(). """ return not is_zero(val)
Test if a value is not equal to zero.
Args
val
- Value to test (can be int, float, or Decimal).
Returns
bool
- True if the value is not zero, False if it is zero.
Returns True for None values.
Note
This is the inverse of is_zero().
def output_to_dicts(output: str) ‑> list[dict[str, typing.Any]]
-
Expand source code
def output_to_dicts(output: str) -> list[dict[str, Any]]: """Convert a tab-delimited output to a list of dicts.""" if not output: return [] # split string on newline loop over each line and convert # Use csv module to parse the tab-delimited output reader = csv.DictReader(StringIO(output), delimiter="\t") ret = [] for model in reader: mod = {} for key, value in model.items(): mod[key.strip().lower()] = value.strip() ret.append(mod) return ret
Convert a tab-delimited output to a list of dicts.
def parse_csv_text(csv_data: StringIO, has_header: bool = True) ‑> list[dict]
-
Expand source code
def parse_csv_text(csv_data: StringIO, has_header: bool = True) -> list[dict]: """ Reads in a CSV file as text and returns it as a list of dictionaries. Args: csv_data (StringIO): The CSV file as text. has_header (bool): Whether the CSV has a header row. Defaults to True. Returns: list[dict]: The CSV data as a list of dictionaries. Raises: csv.Error: If there's an issue parsing the CSV data. """ try: if has_header: reader = csv.DictReader(csv_data, strict=True) try: rows = [] for row in reader: rows.append(row) return rows except Exception as e: raise csv.Error(f"Error parsing CSV data: {str(e)}") else: reader = csv.reader(csv_data, strict=True) try: rows = list(reader) if not rows: return [] # Use column indices as keys when no header headers = [str(i) for i in range(len(rows[0]))] return [dict(zip(headers, row)) for row in rows] except Exception as e: raise csv.Error(f"Error parsing CSV data: {str(e)}") except Exception as e: raise csv.Error(f"Error parsing CSV data: {str(e)}")
Reads in a CSV file as text and returns it as a list of dictionaries.
Args
csv_data
:StringIO
- The CSV file as text.
has_header
:bool
- Whether the CSV has a header row. Defaults to True.
Returns
list[dict]
- The CSV data as a list of dictionaries.
Raises
csv.Error
- If there's an issue parsing the CSV data.
def read_env_file(filename: str, console: Console | None = None) ‑> dict[str, str]
-
Expand source code
def read_env_file(filename: str, console: Console | None = None) -> dict[str, str]: """ Read environment variables from a file into a dictionary Lines starting with # are ignored Args: filename (str): The name of the file to read console (Console, optional): The console to use for output Returns: Dict[str, str]: A dictionary containing the environment variables """ env_vars: dict[str, str] = {} if not os.path.exists(filename): return env_vars with open(filename, encoding="utf-8") as f: for line in f: line = line.strip() if not line or line.startswith("#"): continue try: key, value = line.split("=", 1) env_vars[key.strip()] = value.strip() except ValueError: if not console: console = console_err console.print("Invalid line format") return env_vars
Read environment variables from a file into a dictionary Lines starting with # are ignored
Args
filename
:str
- The name of the file to read
console
:Console
, optional- The console to use for output
Returns
Dict[str, str]
- A dictionary containing the environment variables
def read_text_file_to_stringio(file_path: str, encoding: str = 'utf-8') ‑> _io.StringIO
-
Expand source code
def read_text_file_to_stringio(file_path: str, encoding: str = "utf-8") -> StringIO: """ Reads in a text file and returns it as a StringIO object. Args: file_path (str): The path to the file to read. encoding (str): The encoding of the file. Returns: StringIO: The text file as a StringIO object. """ with open(file_path, encoding=encoding) as file: return StringIO(file.read())
Reads in a text file and returns it as a StringIO object.
Args
file_path
:str
- The path to the file to read.
encoding
:str
- The encoding of the file.
Returns
StringIO
- The text file as a StringIO object.
def run_cmd(params: list[str], console: Console | None = None, check: bool = True) ‑> str | None
-
Expand source code
def run_cmd(params: list[str], console: Console | None = None, check: bool = True) -> str | None: """Run a command and return the output. Args: params: Command and arguments as list of strings console: Optional console for error output check: Whether to raise CalledProcessError on command failure Returns: Command output as string, or None if command failed """ try: result = subprocess.run(params, capture_output=True, text=True, check=check) if result.returncode != 0: if not console: console = console_err console.print(f"Error running command: {result.stderr}") return None ret = result.stdout.strip() # Split the output into lines lines = [line for line in ret.splitlines() if not line.startswith("failed to get console mode")] # Get the last two lines return "\n".join(lines) except FileNotFoundError as e: if not console: console = console_err console.print(f"Error running command: {e}") return None except subprocess.CalledProcessError as e: if not console: console = console_err console.print(f"Error running command: {e.stderr}") return None
Run a command and return the output.
Args
params
- Command and arguments as list of strings
console
- Optional console for error output
check
- Whether to raise CalledProcessError on command failure
Returns
Command output as string, or None if command failed
def run_shell_cmd(cmd: str) ‑> str | None
-
Expand source code
def run_shell_cmd(cmd: str) -> str | None: """Run a command and return the output.""" try: return subprocess.run( shlex.split(cmd), shell=False, capture_output=True, check=True, encoding="utf-8" ).stdout.strip() except Exception as _: return None
Run a command and return the output.
def sha1_hash(data: str) ‑> str
-
Expand source code
def sha1_hash(data: str) -> str: """ Returns a SHA1 hash of the input data. Args: data (str): The input data. Returns: str: The SHA1 hash of the input data. """ sha1 = hashlib.sha1(data.encode("utf-8")) return sha1.hexdigest()
Returns a SHA1 hash of the input data.
Args
data
:str
- The input data.
Returns
str
- The SHA1 hash of the input data.
def sha256_hash(data: str) ‑> str
-
Expand source code
def sha256_hash(data: str) -> str: """ Returns a SHA256 hash of the input data. Args: data (str): The input data. Returns: str: The SHA256 hash of the input data. """ sha256 = hashlib.sha256(data.encode("utf-8")) return sha256.hexdigest()
Returns a SHA256 hash of the input data.
Args
data
:str
- The input data.
Returns
str
- The SHA256 hash of the input data.
def str_ellipsis(s: str, max_len: int, pad_char: str = ' ') ‑> str
-
Expand source code
def str_ellipsis(s: str, max_len: int, pad_char: str = " ") -> str: """Return a left space padded string exactly max_len with ellipsis if it exceeds max_len.""" if len(s) <= max_len: if pad_char: return s.ljust(max_len, pad_char) return s return s[: max_len - 3] + "..."
Return a left space padded string exactly max_len with ellipsis if it exceeds max_len.
def suppress_output()
-
Expand source code
@contextmanager def suppress_output(): """Context manager to suppress stdout and stderr.""" with open(os.devnull, "w", encoding="utf-8") as devnull: old_stdout = sys.stdout old_stderr = sys.stderr try: sys.stdout = devnull sys.stderr = devnull yield finally: sys.stdout = old_stdout sys.stderr = old_stderr
Context manager to suppress stdout and stderr.
def timer_block(label: str = 'Timer', console: Console | None = None) ‑> Generator[None, None, None]
-
Expand source code
@contextmanager def timer_block(label: str = "Timer", console: Console | None = None) -> Generator[None, None, None]: """Time a block of code.""" start_time = time.time() try: yield finally: end_time = time.time() elapsed_time = end_time - start_time if not console: console = console_err console.print(f"{label} took {elapsed_time:.4f} seconds.")
Time a block of code.
def to_camel_case(snake_str: str) ‑> str
-
Expand source code
def to_camel_case(snake_str: str) -> str: """Convert a snake_case string to camelCase. Args: snake_str: The snake_case string to convert Returns: str: The converted camelCase string Example: >>> to_camel_case("hello_world") 'helloWorld' """ components = snake_str.split("_") # We capitalize the first letter of each component except the first one # with the 'title' method and join them together. return components[0] + "".join(x.title() for x in components[1:])
Convert a snake_case string to camelCase.
Args
snake_str
- The snake_case string to convert
Returns
str
- The converted camelCase string
Example
>>> to_camel_case("hello_world") 'helloWorld'
def to_class_case(snake_str: str) ‑> str
-
Expand source code
def to_class_case(snake_str: str) -> str: """Convert a snake_case string to PascalCase (ClassCase). Spaces are converted to underscores before conversion. Args: snake_str: The snake_case string to convert Returns: str: The converted PascalCase string Example: >>> to_class_case("hello_world") 'HelloWorld' >>> to_class_case("hello world") 'HelloWorld' """ components = snake_str.replace(" ", "_").split("_") # We capitalize the first letter of each component # with the 'title' method and join them together. return "".join(x.title() for x in components[0:])
Convert a snake_case string to PascalCase (ClassCase).
Spaces are converted to underscores before conversion.
Args
snake_str
- The snake_case string to convert
Returns
str
- The converted PascalCase string
Example
>>> to_class_case("hello_world") 'HelloWorld' >>> to_class_case("hello world") 'HelloWorld'