#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Simple utilities working with strings
-------------------------------------
:mod:`sloth.utils.strings`
.. note:: each function has its own imports and checks.
"""
#: module logger
from sloth.utils.logging import getLogger
_logger = getLogger("sloth.utils.strings", level="WARNING")
####################
# COLORIZED OUTPUT #
####################
[docs]
def colorstr(instr, color="green", on_color=None, attrs=["bold"]):
"""colorized string
Parameters
----------
color : str, 'green'
Available text colors:
'red', 'green', 'yellow', 'blue', 'magenta', 'cyan',
'white'
on_color : str, None
Available text highlights:
'on_red', 'on_green', 'on_yellow', 'on_blue', 'on_magenta',
'on_cyan', 'on_white'
attrs : list of str, ['bold']
Available attributes:
'bold', 'dark', 'underline', 'blink', 'reverse', 'concealed'
"""
try:
from termcolor import colored
return colored(instr, color=color, on_color=None, attrs=attrs)
except ImportError:
return instr
###################
# STRING TO RANGE #
###################
[docs]
def str2rng(rngstr, keeporder=True, rebin=None):
"""Convert a string to a range of intergers
Description
-----------
This utility converts a string representing list or range of intergers (=scans)
to a sorted list of integers
Parameters
----------
rngstr : str
Strig following a given syntax (see Example below)
keeporder : boolean
Control the order output [True -> keep the original order]
keeporder=False -> turn into a sorted list
rebin : int
if set, force rebinning of the final range [None]
.. note: useful for selecting scans every a given bunch.
Example
-------
> _str2rng('100, 7:9, 130:140:5, 14, 16:18:1')
> [7, 8, 9, 14, 16, 17, 18, 100, 130, 135, 140]
"""
if rngstr is None:
raise NameError("Provide a string or list of scans to load")
if type(rngstr) is list:
assert all([type(elem) is int for elem in rngstr]), "'rngstr' not list of ints"
_logger.info("[str2rng] 'rngstr' given as list of integers")
return rngstr
else:
assert type(rngstr) is str, "'rngstr' should be a string"
_rng = []
for _r in rngstr.split(", "): # the space is important!
if len(_r.split(",")) > 1:
raise NameError("Space after comma(s) is missing in '{0}'".format(_r))
_rsplit2 = _r.split(":")
if len(_rsplit2) == 1:
_rng.append(_r)
elif len(_rsplit2) == 2 or len(_rsplit2) == 3:
if len(_rsplit2) == 2:
_rsplit2.append("1")
if _rsplit2[0] == _rsplit2[1]:
raise NameError("Wrong range '{0}' in string '{1}'".format(_r, rngstr))
if int(_rsplit2[0]) > int(_rsplit2[1]):
raise NameError("Wrong range '{0}' in string '{1}'".format(_r, rngstr))
_rng.extend(range(int(_rsplit2[0]), int(_rsplit2[1]) + 1, int(_rsplit2[2])))
else:
raise NameError("Too many colon in {0}".format(_r))
# create the list and return it (removing the duplicates)
_rngout = [int(x) for x in _rng]
if rebin is not None:
try:
_rngout = _rngout[:: int(rebin)]
except:
raise NameError("Wrong rebin={0}".format(int(rebin)))
def uniquify(seq):
# Order preserving uniquifier by Dave Kirby
seen = set()
return [x for x in seq if x not in seen and not seen.add(x)]
if keeporder:
return uniquify(_rngout)
else:
return list(set(_rngout))
################
# TIME STRINGS #
################
[docs]
def get_timestamp() -> str:
"""return a custom time stamp string: YYY-MM-DD_HHMM"""
import time
return "{0:04d}-{1:02d}-{2:02d}_{3:02d}{4:02d}".format(*time.localtime())
###################
# Natural Sorting #
###################
def _atoi(text):
return int(text) if text.isdigit() else text
[docs]
def natural_keys(text):
"""
FROM: https://stackoverflow.com/questions/5967500/how-to-correctly-sort-a-string-with-a-number-inside
alist.sort(key=natural_keys) sorts in human order
http://nedbatchelder.com/blog/200712/human_sorting.html
(See Toothy's implementation in the comments)
Usage
-----
alist=[
"something1",
"something12",
"something17",
"something2",
"something25",
"something29"]
alist.sort(key=natural_keys)
print(alist)
"""
import re
return [_atoi(c) for c in re.split(r"(\d+)", text)]
##################
# Slice function #
##################
[docs]
def slice_func(list_in, filter_str=None):
"""Utility to slice a list with a function of string parameter
Parameters
----------
list_in : list
input string to slice
filter_str : None or str
filter to apply represented by a string [None -> no filter, input list]
Returns
-------
Sliced list
"""
if filter_str is None:
return list_in
assert isinstance(list_in, list), "`list_in` should be a list"
list_out = list_in[
slice(
*map(lambda x: int(x.strip()) if x.strip() else None, filter_str.split(":"))
)
]
return list_out
if __name__ == "__main__":
pass