Merge branch 'ciherrera20-issue3345' into develop

This commit is contained in:
nicolargo 2025-11-16 10:52:11 +01:00
commit 1171d0b4d3
3 changed files with 122 additions and 2 deletions

View File

@ -631,3 +631,84 @@ def exit_after(seconds, default=None):
return wraps
return decorator
def split_esc(input_string, sep=None, maxsplit=-1, esc='\\'):
"""
Return a list of the substrings in the input_string, using sep as the separator char
and esc as the escape character.
sep
The separator used to split the input_string.
When set to None (the default value), will split on any whitespace
character (including \n \r \t \f and spaces) unless the character is escaped
and will discard empty strings from the result.
maxsplit
Maximum number of splits.
-1 (the default value) means no limit.
esc
The character used to escape the separator.
When set to None, this behaves equivalently to `str.split`.
Defaults to '\\\\' i.e. backslash.
Splitting starts at the front of the input_string and works to the end.
Note: escape characters in the substrings returned are removed. However, if
maxsplit is reached, escape characters in the remaining, unprocessed substring
are not removed, which allows split_esc to be called on it again.
"""
# Input validation
if not isinstance(input_string, str):
raise TypeError(f'must be str, not {input_string.__class__.__name__}')
str.split('', sep=sep, maxsplit=maxsplit) # Use str.split to validate sep and maxsplit
if esc is None:
return input_string.split(
sep=sep, maxsplit=maxsplit
) # Short circuit to default implementation if the escape character is None
if not isinstance(esc, str):
raise TypeError(f'must be str or None, not {esc.__class__.__name__}')
if len(esc) == 0:
raise ValueError('empty escape character')
if len(esc) > 1:
raise ValueError('escape must be a single character')
# Set up a simple state machine keeping track of whether we have seen an escape character
ret, esc_seen, i = [''], False, 0
while i < len(input_string) and len(ret) - 1 != maxsplit:
if not esc_seen:
if input_string[i] == esc:
# Consume the escape character and transition state
esc_seen = True
i += 1
elif sep is None and input_string[i].isspace():
# Consume as much whitespace as possible
n = 1
while i + n + 1 < len(input_string) and input_string[i + n : i + n + 1].isspace():
n += 1
ret.append('')
i += n
elif sep is not None and input_string[i : i + len(sep)] == sep:
# Consume the separator
ret.append('')
i += len(sep)
else:
# Otherwise just add the current char
ret[-1] += input_string[i]
i += 1
else:
# Add the current char and transition state back
ret[-1] += input_string[i]
esc_seen = False
i += 1
# Append any remaining string if we broke early because of maxsplit
if i < len(input_string):
ret[-1] += input_string[i:]
# If splitting on whitespace, discard empty strings from result
if sep is None:
ret = [sub for sub in ret if len(sub) > 0]
return ret

View File

@ -27,6 +27,7 @@ from glances.globals import (
listkeys,
mean,
nativestr,
split_esc,
)
from glances.history import GlancesHistory
from glances.logger import logger
@ -933,7 +934,10 @@ class GlancesPluginModel:
def read_alias(self):
if self.plugin_name + '_' + 'alias' in self._limits:
return {i.split(':')[0].lower(): i.split(':')[1] for i in self._limits[self.plugin_name + '_' + 'alias']}
return {
split_esc(i, ':')[0].lower(): split_esc(i, ':')[1]
for i in self._limits[self.plugin_name + '_' + 'alias']
}
return {}
def has_alias(self, header):

View File

@ -28,7 +28,18 @@ except ImportError:
from glances import __version__
from glances.events_list import GlancesEventsList
from glances.filter import GlancesFilter, GlancesFilterList
from glances.globals import BSD, LINUX, MACOS, SUNOS, WINDOWS, auto_unit, pretty_date, string_value_to_float, subsample
from glances.globals import (
BSD,
LINUX,
MACOS,
SUNOS,
WINDOWS,
auto_unit,
pretty_date,
string_value_to_float,
subsample,
split_esc,
)
from glances.main import GlancesMain
from glances.outputs.glances_bars import Bar
from glances.plugins.fs.zfs import zfs_enable, zfs_stats
@ -552,6 +563,30 @@ class TestGlances(unittest.TestCase):
self.assertEqual(stats.get_plugin('cpu').get_alert(75, minimum=0, maximum=100, header='total'), 'WARNING_LOG')
self.assertEqual(stats.get_plugin('cpu').get_alert(85, minimum=0, maximum=100, header='total'), 'CRITICAL_LOG')
def test_024_split_esc(self):
"""Test split_esc function"""
print('INFO: [TEST_024] split_esc')
self.assertEqual(split_esc(r''), [])
self.assertEqual(split_esc('\\'), [])
self.assertEqual(split_esc(r'abcd'), [r'abcd'])
self.assertEqual(split_esc(r'abcd efg'), [r'abcd', r'efg'])
self.assertEqual(split_esc('abcd \n\t\f efg'), [r'abcd', r'efg'])
self.assertEqual(split_esc(r'abcd\ efg'), [r'abcd efg'])
self.assertEqual(split_esc(r'', ':'), [''])
self.assertEqual(split_esc(r'abcd', ':'), [r'abcd'])
self.assertEqual(split_esc(r'abcd:efg', ':'), [r'abcd', r'efg'])
self.assertEqual(split_esc(r'abcd\:efg', ':'), [r'abcd:efg'])
self.assertEqual(split_esc(r'abcd:efg:hijk', ':'), [r'abcd', r'efg', r'hijk'])
self.assertEqual(split_esc(r'abcd\:efg:hijk', ':'), [r'abcd:efg', r'hijk'])
self.assertEqual(split_esc(r'abcd\:efg:hijk\:lmnop:qrs', ':', maxsplit=0), [r'abcd\:efg:hijk\:lmnop:qrs'])
self.assertEqual(split_esc(r'abcd\:efg:hijk\:lmnop:qrs', ':', maxsplit=1), [r'abcd:efg', r'hijk\:lmnop:qrs'])
self.assertEqual(
split_esc(r'abcd\:efg:hijk\:lmnop:qrs', ':', maxsplit=10), [r'abcd:efg', r'hijk:lmnop', r'qrs']
)
self.assertEqual(split_esc(r'ahellobhelloc', r'hello'), [r'a', r'b', r'c'])
self.assertEqual(split_esc(r'a\hellobhelloc', r'hello'), [r'ahellob', r'c'])
self.assertEqual(split_esc(r'ahe\llobhelloc', r'hello'), [r'ahellob', r'c'])
def test_093_auto_unit(self):
"""Test auto_unit classe"""
print('INFO: [TEST_093] Auto unit')