Files
2026-07-13 13:22:34 +08:00

191 lines
5.9 KiB
Python

import re
import shlex
from datetime import datetime
from typing import Any
from mlflow.utils.os import is_windows
def strip_prefix(original: str, prefix: str) -> str:
if original.startswith(prefix):
return original[len(prefix) :]
return original
def strip_suffix(original: str, suffix: str) -> str:
if original.endswith(suffix) and suffix != "":
return original[: -len(suffix)]
return original
def is_string_type(item: Any) -> bool:
return isinstance(item, str)
def generate_feature_name_if_not_string(s: Any) -> str:
if isinstance(s, str):
return s
return f"feature_{s}"
def truncate_str_from_middle(s: str, max_length: int) -> str:
assert max_length > 5
if len(s) <= max_length:
return s
else:
left_part_len = (max_length - 3) // 2
right_part_len = max_length - 3 - left_part_len
return f"{s[:left_part_len]}...{s[-right_part_len:]}"
def _create_table(
rows: list[list[str]], headers: list[str], column_sep: str = " " * 2, min_column_width: int = 4
) -> str:
"""
Creates a table from a list of rows and headers.
Example
=======
>>> print(_create_table([["a", "b", "c"], ["d", "e", "f"]], ["x", "y", "z"]))
x y z
---- ---- ----
a b c
d e f
"""
column_widths = [
max(len(max(col, key=len)), len(header) + 2, min_column_width)
for col, header in zip(zip(*rows), headers)
]
aligned_rows = [
column_sep.join(header.ljust(width) for header, width in zip(headers, column_widths)),
column_sep.join("-" * width for width in column_widths),
*(
column_sep.join(cell.ljust(width) for cell, width in zip(row, column_widths))
for row in rows
),
]
return "\n".join(aligned_rows)
# Source: https://github.com/smoofra/mslex/blob/3338c347324d52af619ba39cebfdf7cbf46fa51b/mslex.py#L89-L139
cmd_meta = r"([\"\^\&\|\<\>\(\)\%\!])"
cmd_meta_or_space = r"[\s\"\^\&\|\<\>\(\)\%\!]"
cmd_meta_inside_quotes = r"([\"\%\!])"
def mslex_quote(s: str, for_cmd: bool = True) -> str:
"""
Quote a string for use as a command line argument in DOS or Windows.
On windows, before a command line argument becomes a char* in a
program's argv, it must be parsed by both cmd.exe, and by
CommandLineToArgvW.
If for_cmd is true, then this will quote the string so it will
be parsed correctly by cmd.exe and then by CommandLineToArgvW.
If for_cmd is false, then this will quote the string so it will
be parsed correctly when passed directly to CommandLineToArgvW.
For some strings there is no way to quote them so they will
parse correctly in both situations.
"""
if not s:
return '""'
if not re.search(cmd_meta_or_space, s):
return s
if for_cmd and re.search(cmd_meta, s):
if not re.search(cmd_meta_inside_quotes, s):
if m := re.search(r"\\+$", s):
return '"' + s + m.group() + '"'
else:
return '"' + s + '"'
if not re.search(r"[\s\"]", s):
return re.sub(cmd_meta, r"^\1", s)
return re.sub(cmd_meta, r"^\1", mslex_quote(s, for_cmd=False))
i = re.finditer(r"(\\*)(\"+)|(\\+)|([^\\\"]+)", s)
def parts():
yield '"'
for m in i:
_, end = m.span()
slashes, quotes, onlyslashes, text = m.groups()
if quotes:
yield slashes
yield slashes
yield r"\"" * len(quotes)
elif onlyslashes:
if end == len(s):
yield onlyslashes
yield onlyslashes
else:
yield onlyslashes
else:
yield text
yield '"'
return "".join(parts())
def quote(s: str) -> str:
return mslex_quote(s) if is_windows() else shlex.quote(s)
def _backtick_quote(s: str) -> str:
"""
Quotes the given string with backticks if it is not already quoted with backticks.
"""
return f"`{s}`" if not (s.startswith("`") and s.endswith("`")) else s
def format_table_cell_value(field: str, cell_value: Any, values: list[Any] | None = None) -> str:
"""
Format cell values for table display with field-specific formatting.
Args:
field: The field name (e.g., "info.request_time")
cell_value: The value to format
values: List of extracted values (for multiple values handling)
Returns:
Formatted string value suitable for table display
"""
if values is None:
values = [cell_value] if cell_value is not None else []
# Handle empty/missing values
if not values:
return "N/A"
elif len(values) == 1:
cell_value = values[0]
else:
# Multiple values - join them
cell_value = ", ".join(str(v) for v in values[:3]) # Limit to first 3
if len(values) > 3:
cell_value += f", ... (+{len(values) - 3} more)"
# Format specific fields
if field == "info.request_time" and cell_value != "N/A":
# Convert ISO timestamp to readable format
try:
dt = datetime.fromisoformat(str(cell_value).replace("Z", "+00:00"))
cell_value = dt.strftime("%Y-%m-%d %H:%M:%S %Z")
except Exception:
pass # Keep original if conversion fails
elif field == "info.execution_duration_ms" and cell_value != "N/A" and cell_value is not None:
try:
duration_ms = float(cell_value)
if duration_ms < 1000:
cell_value = f"{int(duration_ms)}ms"
else:
cell_value = f"{duration_ms / 1000:.1f}s"
except (ValueError, TypeError):
pass # Keep original if conversion fails
elif field in ["info.request_preview", "info.response_preview"]:
# Truncate previews to keep table readable
if len(str(cell_value)) > 20:
cell_value = str(cell_value)[:17] + "..."
return str(cell_value)