blob: 016050ca230febffe6d21cb33e94d14cc709c5af [file] [log] [blame]
# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Functions for formatting things in a human-readable format."""
import datetime
import json as mod_json
import os
from typing import Optional, TextIO, Union
from chromite.utils import file_util
def timedelta(delta):
"""Returns a more human-readable version of the datetime.timedelta.
Useful when printing durations >= 1 second in logs.
Args:
delta: A datetime.timedelta.
Returns:
Formatted string of the delta like '1d2h3m4.000s'.
"""
if not isinstance(delta, datetime.timedelta):
raise TypeError("delta must be of type datetime.timedelta")
formated_delta = ""
if delta.days:
formated_delta = "%dd" % delta.days
minutes, seconds = divmod(delta.seconds, 60)
hours, minutes = divmod(minutes, 60)
if hours > 0:
formated_delta += "%dh" % hours
if minutes > 0:
formated_delta += "%dm" % minutes
formated_delta += "%i.%03is" % (seconds, delta.microseconds // 1000)
return formated_delta
def json(
obj,
fp: Optional[Union[str, os.PathLike, TextIO]] = None,
cls: Optional[mod_json.JSONEncoder] = None,
compact: bool = False,
) -> Optional[str]:
"""Convert an object to JSON with the right format.
Args:
obj: The object to serialize & format.
fp: By default, the JSON string is returned. The |fp| allows specifying
a file object (in text mode) to write to instead.
cls: Optional custom error class.
compact: Whether the output will be compact (flattened to one line), or
human-readable (spread over multiple lines).
Returns:
A string if |fp| is not specified, else None.
"""
kwargs = {
"cls": cls,
# JSON style guide says Unicode characters are fully allowed.
"ensure_ascii": False,
# We use 2 space indent to match JSON style guide.
"indent": None if compact else 2,
"separators": (",", ":") if compact else (",", ": "),
"sort_keys": True,
}
if fp:
with file_util.Open(fp, mode="w") as real_fp:
mod_json.dump(obj, real_fp, **kwargs)
if not compact:
real_fp.write("\n")
else:
ret = mod_json.dumps(obj, **kwargs)
if not compact:
ret += "\n"
return ret
def size(bytesize: float) -> str:
"""Convert bytes to human-readable format.
Args:
bytesize: Number to humanize
Returns:
Size as string in human-readable format (e.g. 1.8MiB)
"""
if bytesize < 1024:
return f"{bytesize}B"
for suffix in "BKMGTPEZY":
if bytesize < 1024:
break
bytesize /= 1024
return (
f"{bytesize:.1f}{suffix}iB" # pylint: disable=undefined-loop-variable
)