"""Utility functions and type definitions for :mod:`~qurry.capsule`.
(:mod:`qurry.capsule.utils`)"""
from typing import TypedDict, Union, Callable, Any, Optional
import warnings
from json import JSONEncoder
DEFAULT_ENCODING = "utf-8"
"""Default encoding for file operations."""
DEFAULT_INDENT = 2
"""Default indentation for JSON serialization."""
DEFAULT_MODE = "w+"
"""Default mode for file operations."""
DEFAULT_ENSURE_ASCII = False
"""Default ensure_ascii for JSON serialization."""
[docs]
class OpenArgs(TypedDict, total=False):
"""Default arguments for open function."""
mode: str
"""Mode in which the file is opened"""
buffering: int
"""Buffering policy for the file"""
encoding: Optional[str]
"""Encoding used for the file"""
errors: Optional[str]
"""Error handling scheme for the file"""
newline: Optional[str]
"""Newline character handling for the file"""
closefd: bool
"""Whether to close the file descriptor when the file is closed"""
opener: Optional[Callable[[str, int], int]]
"""Custom opener for the file, if needed"""
DEFAULT_OPEN_ARGS: OpenArgs = {
"mode": DEFAULT_MODE,
"encoding": DEFAULT_ENCODING,
}
"""Default arguments for open function.
This includes:
- `mode`: The mode in which the file is opened, default is 'w+'.
- `encoding`: The encoding used for the file, default is 'utf-8'.
"""
[docs]
def create_open_args(
open_args: Union[dict[str, Any], OpenArgs, None] = None,
is_read_only: bool = False,
) -> OpenArgs:
"""Create open arguments.
If `open_args` is not provided or a null dictionary,
it will return :const:`DEFAULT_OPEN_ARGS`
Otherwise, it will merge the provided `open_args` with :const:`DEFAULT_OPEN_ARGS`.
If `is_read_only` is True, the mode will be set to 'r'.
Args:
open_args (Union[dict[str, Any], OpenArgs]): Arguments for open function.
is_read_only (bool, optional): Whether the file is read-only. Defaults to False.
Raises:
TypeError: If 'open_args' is not a dict.
Returns:
OpenArgs: The open arguments.
"""
new_open_args = DEFAULT_OPEN_ARGS.copy()
if open_args is not None:
if not isinstance(open_args, dict):
raise TypeError("'open_args' must be a dict.")
if open_args.pop("file", None):
warnings.warn(
"Argument 'file' is ignored for it will be used by 'TagList.export'.",
UserWarning,
)
for k, v in open_args.items():
new_open_args[k] = v
open_args = new_open_args
if is_read_only:
open_args["mode"] = "r"
return open_args
[docs]
class PrintArgs(TypedDict, total=False):
"""Default arguments for print function."""
flush: bool
"""Whether to flush the output buffer after printing."""
end: str
"""String appended after the last value, default is newline."""
sep: str
"""String inserted between values, default is space."""
DEFAULT_PRINT_ARGS: PrintArgs = {}
"""Default arguments for print function.
"""
[docs]
def create_print_args(
print_args: Union[dict[str, Any], PrintArgs, None] = None,
) -> PrintArgs:
"""Create print arguments.
If `print_args` is not provided or a null dictionary,
it will return :const:`DEFAULT_PRINT_ARGS`.
Otherwise, it will merge the provided `print_args` with :const:`DEFAULT_PRINT_ARGS`.
Args:
print_args (Union[dict[str, Any], PrintArgs]): Arguments for print function.
Returns:
PrintArgs: The print arguments.
"""
new_print_args = DEFAULT_PRINT_ARGS.copy()
if print_args is not None:
if not isinstance(print_args, dict):
raise TypeError("'print_args' must be a dict.")
for k, v in print_args.items():
if k == "file":
warnings.warn(
"Argument 'file' is ignored for it will be used by 'TagList.export'.",
UserWarning,
)
new_print_args[k] = v
return new_print_args
[docs]
class JSONDumpArgs(TypedDict, total=False):
"""Default arguments for print function."""
skipkeys: bool
"""Whether to skip keys that are not serializable."""
ensure_ascii: bool
"""Whether to escape non-ASCII characters."""
check_circular: bool
"""Whether to check for circular references."""
allow_nan: bool
"""Whether to allow NaN and Infinity values."""
cls: Optional[type[JSONEncoder]]
"""Custom JSONEncoder class to use for serialization."""
indent: Union[int, str, None]
"""Indentation level for pretty-printing JSON, default is 2."""
separators: Optional[tuple[str, str]]
"""Tuple of separators for JSON serialization, default is (', ', ': ')."""
default: Optional[Callable[[Any], Any]]
"""Function to call for objects that are not serializable."""
sort_keys: bool
"""Whether to sort the keys in the JSON output."""
DEFAULT_JSON_DUMP_ARGS: JSONDumpArgs = {
"indent": DEFAULT_INDENT,
"ensure_ascii": DEFAULT_ENSURE_ASCII,
}
"""Default arguments for JSON dump function.
This includes:
- `indent`: The indentation level for pretty-printing JSON, default is 2.
- `ensure_ascii`: Whether to escape non-ASCII characters, default is False.
"""
[docs]
def create_json_dump_args(
json_dump_args: Union[dict[str, Any], JSONDumpArgs, None] = None,
) -> JSONDumpArgs:
"""Create JSON dump arguments.
If `json_dump_args` is not provided or a null dictionary,
it will return :const:`DEFAULT_JSON_DUMP_ARGS`.
Otherwise, it will merge the provided `json_dump_args` with :const:`DEFAULT_JSON_DUMP_ARGS`.
Args:
json_dump_args (Union[dict[str, Any], JSONDumpArgs]): Arguments for json.dump function.
Returns:
JSONDumpArgs: The JSON dump arguments.
"""
new_json_dump_args = DEFAULT_JSON_DUMP_ARGS.copy()
if json_dump_args is not None:
if not isinstance(json_dump_args, dict):
raise TypeError("'json_dump_args' must be a dict.")
for k, v in json_dump_args.items():
if k in ["obj", "fp"]:
warnings.warn(
f"Argument '{k}' is ignored for it will be used by 'TagList.export'.",
UserWarning,
)
new_json_dump_args[k] = v
return new_json_dump_args