你已经派生过 gprMax
镜像自地址
https://gitee.com/sunhf/gprMax.git
已同步 2025-08-08 07:24:19 +08:00
Split utilities into separate package and sub-modules.
这个提交包含在:
0
gprMax/utilities/__init__.py
普通文件
0
gprMax/utilities/__init__.py
普通文件
@@ -1,5 +1,5 @@
|
||||
# Copyright (C) 2015-2021: The University of Edinburgh
|
||||
# Authors: Craig Warren and Antonis Giannopoulos
|
||||
# Authors: Craig Warren, Antonis Giannopoulos, and John Hartley
|
||||
#
|
||||
# This file is part of gprMax.
|
||||
#
|
||||
@@ -16,259 +16,16 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import datetime
|
||||
import decimal as d
|
||||
import logging
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
import xml.dom.minidom
|
||||
from shutil import get_terminal_size
|
||||
|
||||
import gprMax.config as config
|
||||
import numpy as np
|
||||
import psutil
|
||||
from colorama import Fore, Style, init
|
||||
init()
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
from time import thread_time as timer_fn
|
||||
except ImportError:
|
||||
from time import perf_counter as timer_fn
|
||||
logger.debug('"thread_time" not currently available in macOS and bug'\
|
||||
' (https://bugs.python.org/issue36205) with "process_time", so use "perf_counter".')
|
||||
|
||||
|
||||
class CustomFormatter(logging.Formatter):
|
||||
"""Logging Formatter to add colors and count warning / errors
|
||||
(https://stackoverflow.com/a/56944256)."""
|
||||
|
||||
grey = "\x1b[38;21m"
|
||||
yellow = "\x1b[33;21m"
|
||||
red = "\x1b[31;21m"
|
||||
bold_red = "\x1b[31;1m"
|
||||
reset = "\x1b[0m"
|
||||
# format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s (%(filename)s:%(lineno)d)"
|
||||
format = "%(message)s"
|
||||
|
||||
FORMATS = {
|
||||
logging.DEBUG: grey + format + reset,
|
||||
logging.INFO: grey + format + reset,
|
||||
logging.WARNING: yellow + format + reset,
|
||||
logging.ERROR: red + format + reset,
|
||||
logging.CRITICAL: bold_red + format + reset
|
||||
}
|
||||
|
||||
def format(self, record):
|
||||
log_fmt = self.FORMATS.get(record.levelno)
|
||||
formatter = logging.Formatter(log_fmt)
|
||||
return formatter.format(record)
|
||||
|
||||
|
||||
def logging_config(name='gprMax', level=logging.INFO, log_file=False):
|
||||
"""Setup and configure logging.
|
||||
|
||||
Args:
|
||||
name (str): name of logger to create.
|
||||
level (logging level): set logging level to stdout.
|
||||
log_file (bool): additional logging to file.
|
||||
"""
|
||||
|
||||
# Adds a custom log level to the root logger
|
||||
# from which new loggers are derived
|
||||
BASIC_NUM = 25
|
||||
logging.addLevelName(BASIC_NUM, "BASIC")
|
||||
def basic(self, message, *args, **kws):
|
||||
if self.isEnabledFor(BASIC_NUM):
|
||||
self._log(BASIC_NUM, message, args, **kws)
|
||||
logging.Logger.basic = basic
|
||||
|
||||
# Create main top-level logger
|
||||
logger = logging.getLogger(name)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
# Config for logging to console
|
||||
handler = logging.StreamHandler(sys.stdout)
|
||||
formatter = CustomFormatter()
|
||||
handler.setLevel(level)
|
||||
handler.setFormatter(formatter)
|
||||
logger.addHandler(handler)
|
||||
|
||||
# Config for logging to file if required
|
||||
if log_file:
|
||||
filename = name + '-log-' + datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
|
||||
handler = logging.FileHandler(filename, mode='w')
|
||||
formatter = logging.Formatter('%(asctime)s:%(name)s:%(levelname)s: %(message)s')
|
||||
handler.setLevel(logging.DEBUG)
|
||||
handler.setFormatter(formatter)
|
||||
logger.addHandler(handler)
|
||||
|
||||
|
||||
def get_terminal_width():
|
||||
"""Get/set width of terminal being used.
|
||||
|
||||
Returns:
|
||||
terminalwidth (int): Terminal width
|
||||
"""
|
||||
|
||||
terminalwidth = get_terminal_size()[0]
|
||||
if terminalwidth == 0:
|
||||
terminalwidth = 100
|
||||
|
||||
return terminalwidth
|
||||
|
||||
|
||||
def logo(version):
|
||||
"""Print gprMax logo, version, and licencing/copyright information.
|
||||
|
||||
Args:
|
||||
version (str): Version number.
|
||||
|
||||
Returns:
|
||||
(str): Containing logo, version, and licencing/copyright information.
|
||||
"""
|
||||
|
||||
description = '\n=== Electromagnetic modelling software based on the Finite-Difference Time-Domain (FDTD) method'
|
||||
current_year = datetime.datetime.now().year
|
||||
copyright = f'Copyright (C) 2015-{current_year}: The University of Edinburgh'
|
||||
authors = 'Authors: Craig Warren and Antonis Giannopoulos'
|
||||
licenseinfo1 = 'gprMax is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n'
|
||||
licenseinfo2 = 'gprMax is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.'
|
||||
licenseinfo3 = 'You should have received a copy of the GNU General Public License along with gprMax. If not, see www.gnu.org/licenses.'
|
||||
|
||||
logo = """ www.gprmax.com __ __
|
||||
__ _ _ __ _ __| \/ | __ ___ __
|
||||
/ _` | '_ \| '__| |\/| |/ _` \ \/ /
|
||||
| (_| | |_) | | | | | | (_| |> <
|
||||
\__, | .__/|_| |_| |_|\__,_/_/\_\\
|
||||
|___/|_|
|
||||
v""" + version + '\n\n'
|
||||
|
||||
str = f"{description} {'=' * (get_terminal_width() - len(description) - 1)}\n\n"
|
||||
str += Fore.CYAN + f'{logo}'
|
||||
str += Style.RESET_ALL + textwrap.fill(copyright, width=get_terminal_width() - 1, initial_indent=' ') + '\n'
|
||||
str += textwrap.fill(authors, width=get_terminal_width() - 1, initial_indent=' ') + '\n\n'
|
||||
str += textwrap.fill(licenseinfo1, width=get_terminal_width() - 1, initial_indent=' ', subsequent_indent=' ') + '\n'
|
||||
str += textwrap.fill(licenseinfo2, width=get_terminal_width() - 1, initial_indent=' ', subsequent_indent=' ') + '\n'
|
||||
str += textwrap.fill(licenseinfo3, width=get_terminal_width() - 1, initial_indent=' ', subsequent_indent=' ')
|
||||
|
||||
return str
|
||||
|
||||
|
||||
def pretty_xml(roughxml):
|
||||
"""Nicely format XML string.
|
||||
|
||||
Args:
|
||||
roughxml (str): XML string to format
|
||||
|
||||
Returns:
|
||||
prettyxml (str): nicely formatted XML string
|
||||
"""
|
||||
|
||||
prettyxml = xml.dom.minidom.parseString(roughxml).toprettyxml()
|
||||
# Remove the weird newline issue
|
||||
prettyxml = os.linesep.join(
|
||||
[s for s in prettyxml.splitlines() if s.strip()])
|
||||
|
||||
return prettyxml
|
||||
|
||||
|
||||
def round_value(value, decimalplaces=0):
|
||||
"""Rounding function.
|
||||
|
||||
Args:
|
||||
value (float): Number to round.
|
||||
decimalplaces (int): Number of decimal places of float to represent
|
||||
rounded value.
|
||||
|
||||
Returns:
|
||||
rounded (int/float): Rounded value.
|
||||
"""
|
||||
|
||||
# Rounds to nearest integer (half values are rounded downwards)
|
||||
if decimalplaces == 0:
|
||||
rounded = int(d.Decimal(value).quantize(d.Decimal('1'), rounding=d.ROUND_HALF_DOWN))
|
||||
|
||||
# Rounds down to nearest float represented by number of decimal places
|
||||
else:
|
||||
precision = '1.{places}'.format(places='0' * decimalplaces)
|
||||
rounded = float(d.Decimal(value).quantize(d.Decimal(precision), rounding=d.ROUND_FLOOR))
|
||||
|
||||
return rounded
|
||||
|
||||
|
||||
def round32(value):
|
||||
"""Rounds up to nearest multiple of 32."""
|
||||
return int(32 * np.ceil(float(value) / 32))
|
||||
|
||||
|
||||
def fft_power(waveform, dt):
|
||||
"""Calculate a FFT of the given waveform of amplitude values;
|
||||
converted to decibels and shifted so that maximum power is 0dB
|
||||
|
||||
Args:
|
||||
waveform (ndarray): time domain waveform
|
||||
dt (float): time step
|
||||
|
||||
Returns:
|
||||
freqs (ndarray): frequency bins
|
||||
power (ndarray): power
|
||||
"""
|
||||
|
||||
# Calculate magnitude of frequency spectra of waveform (ignore warning from taking a log of any zero values)
|
||||
with np.errstate(divide='ignore'):
|
||||
power = 10 * np.log10(np.abs(np.fft.fft(waveform))**2)
|
||||
|
||||
# Replace any NaNs or Infs from zero division
|
||||
power[np.invert(np.isfinite(power))] = 0
|
||||
|
||||
# Frequency bins
|
||||
freqs = np.fft.fftfreq(power.size, d=dt)
|
||||
|
||||
# Shift powers so that frequency with maximum power is at zero decibels
|
||||
power -= np.amax(power)
|
||||
|
||||
return freqs, power
|
||||
|
||||
|
||||
def human_size(size, a_kilobyte_is_1024_bytes=False):
|
||||
"""Convert a file size to human-readable form.
|
||||
|
||||
Args:
|
||||
size (int): file size in bytes.
|
||||
a_kilobyte_is_1024_bytes (boolean) - true for multiples of 1024, false for multiples of 1000.
|
||||
|
||||
Returns:
|
||||
Human-readable (string).
|
||||
"""
|
||||
|
||||
suffixes = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], 1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']}
|
||||
|
||||
if size < 0:
|
||||
raise ValueError('Number must be non-negative.')
|
||||
|
||||
multiple = 1024 if a_kilobyte_is_1024_bytes else 1000
|
||||
for suffix in suffixes[multiple]:
|
||||
size /= multiple
|
||||
if size < multiple:
|
||||
return '{:.3g}{}'.format(size, suffix)
|
||||
|
||||
raise ValueError('Number is too large.')
|
||||
|
||||
|
||||
def atoi(text):
|
||||
"""Converts a string into an integer."""
|
||||
return int(text) if text.isdigit() else text
|
||||
|
||||
|
||||
def natural_keys(text):
|
||||
"""Human sorting of a string."""
|
||||
return [atoi(c) for c in re.split(r'(\d+)', text)]
|
||||
from .utilities import human_size
|
||||
|
||||
|
||||
def get_host_info():
|
||||
@@ -599,8 +356,3 @@ def detect_gpus():
|
||||
gpus.append(gpu)
|
||||
|
||||
return gpus
|
||||
|
||||
|
||||
def timer():
|
||||
"""Function to return time in fractional seconds."""
|
||||
return timer_fn()
|
102
gprMax/utilities/logging.py
普通文件
102
gprMax/utilities/logging.py
普通文件
@@ -0,0 +1,102 @@
|
||||
# Copyright (C) 2015-2021: The University of Edinburgh
|
||||
# Authors: Craig Warren, Antonis Giannopoulos, and John Hartley
|
||||
#
|
||||
# This file is part of gprMax.
|
||||
#
|
||||
# gprMax is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# gprMax is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
import sys
|
||||
from copy import copy
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Adds a custom log level to the root logger from which new loggers are derived
|
||||
BASIC_NUM = 25
|
||||
logging.addLevelName(BASIC_NUM, "BASIC")
|
||||
logging.BASIC = BASIC_NUM
|
||||
def basic(self, message, *args, **kws):
|
||||
if self.isEnabledFor(BASIC_NUM):
|
||||
self._log(BASIC_NUM, message, args, **kws)
|
||||
logging.Logger.basic = basic
|
||||
|
||||
|
||||
# Colour mapping for different log levels
|
||||
MAPPING = {
|
||||
'DEBUG' : 37, # white
|
||||
'BASIC' : 37, # white
|
||||
'INFO' : 37, # white
|
||||
'WARNING' : 33, # yellow
|
||||
'ERROR' : 31, # red
|
||||
'CRITICAL': 41, # white on red bg
|
||||
}
|
||||
PREFIX = '\033['
|
||||
SUFFIX = '\033[0m'
|
||||
|
||||
|
||||
class CustomFormatter(logging.Formatter):
|
||||
"""Logging Formatter to add colors and count warning / errors
|
||||
(https://stackoverflow.com/a/46482050)."""
|
||||
|
||||
def __init__(self, pattern):
|
||||
logging.Formatter.__init__(self, pattern)
|
||||
|
||||
def format(self, record):
|
||||
colored_record = copy(record)
|
||||
levelname = colored_record.levelname
|
||||
seq = MAPPING.get(levelname, 37) # default white
|
||||
colored_levelname = ('{0}{1}m{2}{3}').format(PREFIX, seq, levelname, SUFFIX)
|
||||
colored_record.levelname = colored_levelname
|
||||
return logging.Formatter.format(self, colored_record)
|
||||
|
||||
|
||||
def logging_config(name='gprMax', level=logging.INFO, format_style='std', log_file=False):
|
||||
"""Setup and configure logging.
|
||||
|
||||
Args:
|
||||
name (str): name of logger to create.
|
||||
level (logging level): set logging level to stdout.
|
||||
format_style (str): set formatting - 'std' or 'full'
|
||||
log_file (bool): additional logging to file.
|
||||
"""
|
||||
|
||||
# Set format style
|
||||
if format_style == 'std':
|
||||
format = "%(message)s"
|
||||
elif format_style == 'full':
|
||||
format = "%(asctime)s:%(levelname)s:%(name)s:%(lineno)d: %(message)s"
|
||||
|
||||
# Create main top-level logger
|
||||
logger = logging.getLogger(name)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
logger.propagate = False
|
||||
|
||||
# Config for logging to console
|
||||
handler = logging.StreamHandler(sys.stdout)
|
||||
handler.setLevel(level)
|
||||
handler.setFormatter(CustomFormatter(format))
|
||||
if (logger.hasHandlers()):
|
||||
logger.handlers.clear()
|
||||
logger.addHandler(handler)
|
||||
|
||||
# Config for logging to file if required
|
||||
if log_file:
|
||||
filename = name + '-log-' + datetime.datetime.now().strftime("%Y%m%d-%H%M%S") + '.txt'
|
||||
handler = logging.FileHandler(filename, mode='w')
|
||||
formatter = logging.Formatter('%(asctime)s:%(name)s:%(levelname)s: %(message)s (%(filename)s:%(lineno)d)')
|
||||
handler.setLevel(logging.DEBUG)
|
||||
handler.setFormatter(formatter)
|
||||
logger.addHandler(handler)
|
206
gprMax/utilities/utilities.py
普通文件
206
gprMax/utilities/utilities.py
普通文件
@@ -0,0 +1,206 @@
|
||||
# Copyright (C) 2015-2021: The University of Edinburgh
|
||||
# Authors: Craig Warren, Antonis Giannopoulos, and John Hartley
|
||||
#
|
||||
# This file is part of gprMax.
|
||||
#
|
||||
# gprMax is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# gprMax is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import datetime
|
||||
import decimal as d
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import textwrap
|
||||
import xml.dom.minidom
|
||||
from shutil import get_terminal_size
|
||||
|
||||
import numpy as np
|
||||
from colorama import Fore, Style, init
|
||||
init()
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
from time import thread_time as timer_fn
|
||||
except ImportError:
|
||||
from time import perf_counter as timer_fn
|
||||
logger.debug('"thread_time" not currently available in macOS and bug'\
|
||||
' (https://bugs.python.org/issue36205) with "process_time", so use "perf_counter".')
|
||||
|
||||
|
||||
def get_terminal_width():
|
||||
"""Get/set width of terminal being used.
|
||||
|
||||
Returns:
|
||||
terminalwidth (int): Terminal width
|
||||
"""
|
||||
|
||||
terminalwidth = get_terminal_size()[0]
|
||||
if terminalwidth == 0:
|
||||
terminalwidth = 100
|
||||
|
||||
return terminalwidth
|
||||
|
||||
|
||||
def logo(version):
|
||||
"""Print gprMax logo, version, and licencing/copyright information.
|
||||
|
||||
Args:
|
||||
version (str): Version number.
|
||||
|
||||
Returns:
|
||||
(str): Containing logo, version, and licencing/copyright information.
|
||||
"""
|
||||
|
||||
description = '\n=== Electromagnetic modelling software based on the Finite-Difference Time-Domain (FDTD) method'
|
||||
current_year = datetime.datetime.now().year
|
||||
copyright = f'Copyright (C) 2015-{current_year}: The University of Edinburgh'
|
||||
authors = 'Authors: Craig Warren, Antonis Giannopoulos, and John Hartley'
|
||||
licenseinfo1 = 'gprMax is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n'
|
||||
licenseinfo2 = 'gprMax is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.'
|
||||
licenseinfo3 = 'You should have received a copy of the GNU General Public License along with gprMax. If not, see www.gnu.org/licenses.'
|
||||
|
||||
logo = """ www.gprmax.com __ __
|
||||
__ _ _ __ _ __| \/ | __ ___ __
|
||||
/ _` | '_ \| '__| |\/| |/ _` \ \/ /
|
||||
| (_| | |_) | | | | | | (_| |> <
|
||||
\__, | .__/|_| |_| |_|\__,_/_/\_\\
|
||||
|___/|_|
|
||||
v""" + version + '\n\n'
|
||||
|
||||
str = f"{description} {'=' * (get_terminal_width() - len(description) - 1)}\n\n"
|
||||
str += Fore.CYAN + f'{logo}'
|
||||
str += Style.RESET_ALL + textwrap.fill(copyright, width=get_terminal_width() - 1, initial_indent=' ') + '\n'
|
||||
str += textwrap.fill(authors, width=get_terminal_width() - 1, initial_indent=' ') + '\n\n'
|
||||
str += textwrap.fill(licenseinfo1, width=get_terminal_width() - 1, initial_indent=' ', subsequent_indent=' ') + '\n'
|
||||
str += textwrap.fill(licenseinfo2, width=get_terminal_width() - 1, initial_indent=' ', subsequent_indent=' ') + '\n'
|
||||
str += textwrap.fill(licenseinfo3, width=get_terminal_width() - 1, initial_indent=' ', subsequent_indent=' ')
|
||||
|
||||
return str
|
||||
|
||||
|
||||
def pretty_xml(roughxml):
|
||||
"""Nicely format XML string.
|
||||
|
||||
Args:
|
||||
roughxml (str): XML string to format
|
||||
|
||||
Returns:
|
||||
prettyxml (str): nicely formatted XML string
|
||||
"""
|
||||
|
||||
prettyxml = xml.dom.minidom.parseString(roughxml).toprettyxml()
|
||||
# Remove the weird newline issue
|
||||
prettyxml = os.linesep.join(
|
||||
[s for s in prettyxml.splitlines() if s.strip()])
|
||||
|
||||
return prettyxml
|
||||
|
||||
|
||||
def round_value(value, decimalplaces=0):
|
||||
"""Rounding function.
|
||||
|
||||
Args:
|
||||
value (float): Number to round.
|
||||
decimalplaces (int): Number of decimal places of float to represent
|
||||
rounded value.
|
||||
|
||||
Returns:
|
||||
rounded (int/float): Rounded value.
|
||||
"""
|
||||
|
||||
# Rounds to nearest integer (half values are rounded downwards)
|
||||
if decimalplaces == 0:
|
||||
rounded = int(d.Decimal(value).quantize(d.Decimal('1'), rounding=d.ROUND_HALF_DOWN))
|
||||
|
||||
# Rounds down to nearest float represented by number of decimal places
|
||||
else:
|
||||
precision = '1.{places}'.format(places='0' * decimalplaces)
|
||||
rounded = float(d.Decimal(value).quantize(d.Decimal(precision), rounding=d.ROUND_FLOOR))
|
||||
|
||||
return rounded
|
||||
|
||||
|
||||
def round32(value):
|
||||
"""Rounds up to nearest multiple of 32."""
|
||||
return int(32 * np.ceil(float(value) / 32))
|
||||
|
||||
|
||||
def fft_power(waveform, dt):
|
||||
"""Calculate a FFT of the given waveform of amplitude values;
|
||||
converted to decibels and shifted so that maximum power is 0dB
|
||||
|
||||
Args:
|
||||
waveform (ndarray): time domain waveform
|
||||
dt (float): time step
|
||||
|
||||
Returns:
|
||||
freqs (ndarray): frequency bins
|
||||
power (ndarray): power
|
||||
"""
|
||||
|
||||
# Calculate magnitude of frequency spectra of waveform (ignore warning from taking a log of any zero values)
|
||||
with np.errstate(divide='ignore'):
|
||||
power = 10 * np.log10(np.abs(np.fft.fft(waveform))**2)
|
||||
|
||||
# Replace any NaNs or Infs from zero division
|
||||
power[np.invert(np.isfinite(power))] = 0
|
||||
|
||||
# Frequency bins
|
||||
freqs = np.fft.fftfreq(power.size, d=dt)
|
||||
|
||||
# Shift powers so that frequency with maximum power is at zero decibels
|
||||
power -= np.amax(power)
|
||||
|
||||
return freqs, power
|
||||
|
||||
|
||||
def human_size(size, a_kilobyte_is_1024_bytes=False):
|
||||
"""Convert a file size to human-readable form.
|
||||
|
||||
Args:
|
||||
size (int): file size in bytes.
|
||||
a_kilobyte_is_1024_bytes (boolean) - true for multiples of 1024, false for multiples of 1000.
|
||||
|
||||
Returns:
|
||||
Human-readable (string).
|
||||
"""
|
||||
|
||||
suffixes = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], 1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']}
|
||||
|
||||
if size < 0:
|
||||
raise ValueError('Number must be non-negative.')
|
||||
|
||||
multiple = 1024 if a_kilobyte_is_1024_bytes else 1000
|
||||
for suffix in suffixes[multiple]:
|
||||
size /= multiple
|
||||
if size < multiple:
|
||||
return '{:.3g}{}'.format(size, suffix)
|
||||
|
||||
raise ValueError('Number is too large.')
|
||||
|
||||
|
||||
def atoi(text):
|
||||
"""Converts a string into an integer."""
|
||||
return int(text) if text.isdigit() else text
|
||||
|
||||
|
||||
def natural_keys(text):
|
||||
"""Human sorting of a string."""
|
||||
return [atoi(c) for c in re.split(r'(\d+)', text)]
|
||||
|
||||
|
||||
def timer():
|
||||
"""Function to return time in fractional seconds."""
|
||||
return timer_fn()
|
在新工单中引用
屏蔽一个用户