Coverage for muutils/misc/string.py: 85%
34 statements
« prev ^ index » next coverage.py v7.6.1, created at 2025-04-04 03:33 -0600
« prev ^ index » next coverage.py v7.6.1, created at 2025-04-04 03:33 -0600
1from __future__ import annotations
4from muutils.misc.hashing import stable_hash
7def sanitize_name(
8 name: str | None,
9 additional_allowed_chars: str = "",
10 replace_invalid: str = "",
11 when_none: str | None = "_None_",
12 leading_digit_prefix: str = "",
13) -> str:
14 """sanitize a string, leaving only alphanumerics and `additional_allowed_chars`
16 # Parameters:
17 - `name : str | None`
18 input string
19 - `additional_allowed_chars : str`
20 additional characters to allow, none by default
21 (defaults to `""`)
22 - `replace_invalid : str`
23 character to replace invalid characters with
24 (defaults to `""`)
25 - `when_none : str | None`
26 string to return if `name` is `None`. if `None`, raises an exception
27 (defaults to `"_None_"`)
28 - `leading_digit_prefix : str`
29 character to prefix the string with if it starts with a digit
30 (defaults to `""`)
32 # Returns:
33 - `str`
34 sanitized string
35 """
37 if name is None:
38 if when_none is None:
39 raise ValueError("name is None")
40 else:
41 return when_none
43 sanitized: str = ""
44 for char in name:
45 if char.isalnum():
46 sanitized += char
47 elif char in additional_allowed_chars:
48 sanitized += char
49 else:
50 sanitized += replace_invalid
52 if sanitized[0].isdigit():
53 sanitized = leading_digit_prefix + sanitized
55 return sanitized
58def sanitize_fname(fname: str | None, **kwargs) -> str:
59 """sanitize a filename to posix standards
61 - leave only alphanumerics, `_` (underscore), '-' (dash) and `.` (period)
62 """
63 return sanitize_name(fname, additional_allowed_chars="._-", **kwargs)
66def sanitize_identifier(fname: str | None, **kwargs) -> str:
67 """sanitize an identifier (variable or function name)
69 - leave only alphanumerics and `_` (underscore)
70 - prefix with `_` if it starts with a digit
71 """
72 return sanitize_name(
73 fname, additional_allowed_chars="_", leading_digit_prefix="_", **kwargs
74 )
77def dict_to_filename(
78 data: dict,
79 format_str: str = "{key}_{val}",
80 separator: str = ".",
81 max_length: int = 255,
82):
83 # Convert the dictionary items to a list of strings using the format string
84 formatted_items: list[str] = [
85 format_str.format(key=k, val=v) for k, v in data.items()
86 ]
88 # Join the formatted items using the separator
89 joined_str: str = separator.join(formatted_items)
91 # Remove special characters and spaces
92 sanitized_str: str = sanitize_fname(joined_str)
94 # Check if the length is within limits
95 if len(sanitized_str) <= max_length:
96 return sanitized_str
98 # If the string is too long, generate a hash
99 return f"h_{stable_hash(sanitized_str)}"
102def dynamic_docstring(**doc_params):
103 def decorator(func):
104 if func.__doc__:
105 func.__doc__ = func.__doc__.format(**doc_params)
106 return func
108 return decorator