Passed
Push — main ( 520e83...b06663 )
by Douglas
01:43
created

StringTools.join_kv_quoted()   A

Complexity

Conditions 1

Size

Total Lines 3
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nop 5
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
import re
0 ignored issues
show
introduced by
Missing module docstring
Loading history...
2
import warnings
3
from typing import Any, Callable, Iterable, Mapping, Optional, Sequence, Tuple, TypeVar, Union
4
5
import numpy as np
0 ignored issues
show
introduced by
Unable to import 'numpy'
Loading history...
6
import orjson
0 ignored issues
show
introduced by
Unable to import 'orjson'
Loading history...
7
import regex
0 ignored issues
show
introduced by
Unable to import 'regex'
Loading history...
8
9
from pocketutils.core.chars import *
0 ignored issues
show
Coding Style introduced by
The usage of wildcard imports like pocketutils.core.chars should generally be avoided.
Loading history...
Bug introduced by
The name core does not seem to exist in module pocketutils.
Loading history...
10
from pocketutils.core.exceptions import OutOfRangeError, XTypeError, XValueError
0 ignored issues
show
Bug introduced by
The name core does not seem to exist in module pocketutils.
Loading history...
11
from pocketutils.tools.base_tools import BaseTools
12
13
T = TypeVar("T")
0 ignored issues
show
Coding Style Naming introduced by
Class name "T" doesn't conform to PascalCase naming style ('[^\\W\\da-z][^\\W_]+$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
14
V = TypeVar("V")
0 ignored issues
show
Coding Style Naming introduced by
Class name "V" doesn't conform to PascalCase naming style ('[^\\W\\da-z][^\\W_]+$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
15
_control_chars = regex.compile(r"\p{C}", flags=regex.V1)
16
17
18
class StringTools(BaseTools):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
best-practice introduced by
Too many public methods (37/20)
Loading history...
19
    @classmethod
20
    def pretty_dict(cls, dct: Mapping[Any, Any]) -> str:
21
        """
22
        Returns a pretty-printed dict, complete with indentation. Will fail on non-JSON-serializable datatypes.
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (111/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
23
        """
24
        # return Pretty.condensed(dct)
25
        return orjson.dumps(dct, option=orjson.OPT_INDENT_2).decode(encoding="utf8")
26
27
    @classmethod
28
    def extract_group(
29
        cls,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
30
        pattern: Union[str, re.Pattern, regex.Pattern],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
31
        value: Optional[str],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
32
        *,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
33
        group: int = 0,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
34
        ignore_null: bool = False,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
35
    ) -> Optional[str]:
36
        """
37
        Extracts a capture group from a regex full-match.
38
        Returns None if there was no match.
39
        **Always** uses https://pypi.org/project/regex with ``flags=regex.V1``.
40
41
        Args:
42
            pattern: Regex pattern
43
            value: The target string
44
            group: The group number
45
            ignore_null: Return None if ``value`` is None instead of raising a ValueError
46
47
        Returns The capture group, or None
48
        """
49
        if isinstance(pattern, re.Pattern):
50
            pattern = regex.compile(pattern.pattern, flags=regex.V1)
51
        elif isinstance(pattern, str):
52
            pattern = regex.compile(pattern, flags=regex.V1)
53
        elif isinstance(pattern, regex.Pattern) and not pattern.flags & regex.V1:
54
            pattern = regex.compile(pattern.pattern, flags=regex.V1)
55
        if value is None and ignore_null:
56
            return None
57
        match = pattern.fullmatch(value)
58
        if match is None:
59
            return None
60
        return match.group(group)
61
62
    @classmethod
63
    def join_to_str(cls, *items: Any, last: str, sep: str = ", ") -> str:
64
        """
65
        Joins items to something like "cat, dog, and pigeon" or "cat, dog, or pigeon".
66
67
        Args:
68
            *items: Items to join; ``str(item) for item in items`` will be used
69
            last: Probably "and", "or", "and/or", or ""
70
                    Spaces are added/removed as needed if ``suffix`` is alphanumeric
71
                    or "and/or", after stripping whitespace off the ends.
72
            sep: Used to separate all words; include spaces as desired
73
74
        Examples:
75
            - ``join_to_str(["cat", "dog", "elephant"], last="and")  # cat, dog, and elephant``
76
            - ``join_to_str(["cat", "dog"], last="and")  # cat and dog``
77
            - ``join_to_str(["cat", "dog", "elephant"], last="", sep="/")  # cat/dog/elephant``
78
        """
79
        if last.strip().isalpha() or last.strip() == "and/or":
80
            last = last.strip() + " "
81
        items = [str(s).strip("'" + '"' + " ") for s in items]
82
        if len(items) > 2:
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
83
            return sep.join(items[:-1]) + sep + last + items[-1]
84
        else:
85
            return (" " + last + " ").join(items)
86
87
    @classmethod
88
    def strip_control_chars(cls, s: str) -> str:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
89
        """
90
        Strips all characters under the Unicode 'Cc' category.
91
        """
92
        return _control_chars.sub("", s)
93
94
    @classmethod
95
    def roman_to_arabic(
96
        cls, roman: str, min_val: Optional[int] = None, max_val: Optional[int] = None
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
97
    ) -> int:
98
        """
99
        Converts roman numerals to an integer.
100
101
        Args:
102
            roman: A string like "MCIV"
103
            min_val: Raise a ValueError if the parsed value is less than this
104
            max_val: Raise a ValueError if the parsed value is more than this
105
106
        Returns:
107
            The arabic numeral as a Python int
108
        """
109
        # this order is IMPORTANT!
110
        mp = dict(
0 ignored issues
show
Coding Style Naming introduced by
Variable name "mp" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
111
            IV=4, IX=9, XL=40, XC=90, CD=400, CM=900, I=1, V=5, X=10, L=50, C=100, D=500, M=1000
112
        )
113
        for k, v in mp.items():
0 ignored issues
show
Coding Style Naming introduced by
Variable name "v" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
114
            roman = roman.replace(k, str(v))
115
        # it'll just error if it's empty
116
        try:
117
            value = sum((int(num) for num in roman))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable num does not seem to be defined.
Loading history...
118
        except (ValueError, StopIteration):
119
            raise XValueError(f"Cannot parse roman numerals '{roman}'", value=roman)
120
        if min_val is not None and value < min_val or max_val is not None and value > max_val:
121
            raise XValueError(
122
                f"Value {roman} (int={value}) is out of range ({min_val}, {max_val})", value=roman
123
            )
124
        return value
125
126
    @classmethod
127
    def retab(cls, s: str, nspaces: int) -> str:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
128
        """
129
        Converts indentation with spaces to tab indentation.
130
131
        Args:
132
            s: The string to convert
133
            nspaces: A tab is this number of spaces
134
        """
135
136
        def fix(m):
0 ignored issues
show
Coding Style Naming introduced by
Argument name "m" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
137
            n = len(m.group(1)) // nspaces
0 ignored issues
show
Coding Style Naming introduced by
Variable name "n" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
138
            return "\t" * n + " " * (len(m.group(1)) % nspaces)
139
140
        return regex.sub("^( +)", fix, s, flags=regex.V1 | regex.MULTILINE)
141
142
    @classmethod
143
    def strip_empty_decimal(cls, num: Union[float, str]) -> str:
144
        """
145
        Replaces prefix . with 0. and strips trailing .0 and trailing .
146
        """
147
        try:
148
            float(num)
149
        except TypeError:
150
            if not isinstance(num, str):
151
                raise TypeError("Must be either str or float-like") from None
152
        t = str(num)
0 ignored issues
show
Coding Style Naming introduced by
Variable name "t" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
153
        if t.startswith("."):
154
            t = "0" + t
0 ignored issues
show
Coding Style Naming introduced by
Variable name "t" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
155
        if "." in t:
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
156
            return t.rstrip("0").rstrip(".")
157
        else:
158
            return t
159
160
    @classmethod
161
    def tabs_to_list(cls, s: str) -> Sequence[str]:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
162
        """
163
        Splits by tabs, but preserving quoted tabs, stripping quotes.
164
        In other words, will not split within a quoted substring.
165
        Double and single quotes are handled.
166
        """
167
        pat = regex.compile(r"""((?:[^\t"']|"[^"]*"|'[^']*')+)""", flags=regex.V1)
168
        # Don't strip double 2x quotes: ex ""55"" should be "55", not 55
169
        def strip(i: str) -> str:
170
            if i.endswith('"') or i.endswith("'"):
171
                i = i[:-1]
172
            if i.startswith('"') or i.startswith("'"):
173
                i = i[1:]
174
            return i.strip()
175
176
        return [strip(i) for i in pat.findall(s)]
177
178
    @classmethod
179
    def truncate(
0 ignored issues
show
Coding Style Naming introduced by
Argument name "n" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style Naming introduced by
Argument name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
180
        cls,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
181
        s: Optional[str],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
182
        n: int,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
183
        *,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
184
        null: Optional[str] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
185
    ) -> Optional[str]:
186
        """
187
        Returns a string if it has ``n`` or fewer characters;
188
        otherwise truncates to length ``n-1`` and appends ``…`` (UTF character).
189
        If ``s`` is None and ``always_dots`` is True, returns ``n`` copies of ``.`` (as a string).
190
        If ``s`` is None otherwise, returns None.
191
192
        Args:
193
            s: The string
194
            n: The maximum length, inclusive
195
            null: Replace ``None`` with this string
196
197
        Returns:
198
            A string or None
199
        """
200
        if s is None:
201
            return null
202
        if len(s) > n:
203
            nx = max(0, n - 1)
0 ignored issues
show
Coding Style Naming introduced by
Variable name "nx" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
204
            return s[:nx] + "…"
205
        return s
206
207
    # these are provided to avoid having to call with labdas or functools.partial
208
    @classmethod
209
    def truncating(
0 ignored issues
show
Coding Style Naming introduced by
Argument name "n" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
introduced by
Missing function or method docstring
Loading history...
210
        cls,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
211
        n: int = 40,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
212
        always_dots: bool = False,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
213
        *,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
214
        null: Optional[str] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
215
    ) -> Callable[[str], str]:
216
        # pretty much functools.partial
217
        def trunc(s: str) -> str:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
218
            return cls.truncate(s, n, null=null)
219
220
        trunc.__name__ = f"truncate({n},{'…' if always_dots else ''})"
221
        return trunc
222
223
    @classmethod
224
    def longest(cls, parts: Iterable[T]) -> T:
225
        """
226
        Returns an element with the highest ``len``.
227
        """
228
        mx = ""
0 ignored issues
show
Coding Style Naming introduced by
Variable name "mx" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
229
        for i, x in enumerate(parts):
0 ignored issues
show
Unused Code introduced by
The variable i seems to be unused.
Loading history...
Coding Style Naming introduced by
Variable name "x" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
230
            if len(x) > len(mx):
231
                mx = x
0 ignored issues
show
Coding Style Naming introduced by
Variable name "mx" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
232
        return mx
233
234
    @classmethod
235
    def longest_str(cls, parts: Iterable[str]) -> str:
236
        """
237
        Returns the argmax by length.
238
        """
239
        return cls.longest(parts)
240
241
    @classmethod
242
    def strip_off_start(cls, s: str, pre: str) -> str:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
243
        """
244
        Strips the full string ``pre`` from the start of ``str``.
245
        See ``Tools.strip_off`` for more info.
246
        """
247
        if not isinstance(pre, str):
248
            raise XTypeError(
249
                f"{pre} is not a string", actual=str(type(pre)), expected=str(type(str))
250
            )
251
        if s.startswith(pre):
252
            s = s[len(pre) :]
253
        return s
254
255
    @classmethod
256
    def strip_off_end(cls, s: str, suf: str) -> str:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
257
        """
258
        Strips the full string ``suf`` from the end of ``str``.
259
        See `Tools.strip_off` for more info.
260
        """
261
        if not isinstance(suf, str):
262
            raise TypeError(f"{suf} is not a string")
263
        if s.endswith(suf):
264
            s = s[: -len(suf)]
265
        return s
266
267
    @classmethod
268
    def strip_off(cls, s: str, prefix_or_suffix: str) -> str:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
269
        """
270
        Strip a substring from the beginning or end of a string (at most 1 occurrence).
271
        """
272
        return StringTools.strip_off_start(
273
            StringTools.strip_off_end(s, prefix_or_suffix), prefix_or_suffix
274
        )
275
276
    @classmethod
277
    def strip_ends(cls, s: str, prefix: str, suffix: str) -> str:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
278
        """
279
        Strips a substring from the start, and another substring from the end, of a string (at most 1 occurrence each).
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (119/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
280
        """
281
        return StringTools.strip_off_start(StringTools.strip_off_end(s, suffix), prefix)
282
283
    @classmethod
284
    def strip_any_ends(
0 ignored issues
show
Coding Style Naming introduced by
Argument name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
285
        cls,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
286
        s: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
287
        prefixes: Union[str, Sequence[str]],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
288
        suffixes: Union[str, Sequence[str]],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
289
    ) -> str:
290
        """
291
        Flexible variant that strips any number of prefixes and any number of suffixes.
292
        Also less type-safe than more specific variants.
293
        Note that the order of the prefixes (or suffixes) DOES matter.
294
        """
295
        prefixes = (
296
            [str(z) for z in prefixes]
297
            if StringTools.is_true_iterable(prefixes)
298
            else [str(prefixes)]
299
        )
300
        suffixes = (
301
            [str(z) for z in suffixes]
302
            if StringTools.is_true_iterable(suffixes)
303
            else [str(suffixes)]
304
        )
305
        s = str(s)
306
        for pre in prefixes:
307
            if s.startswith(pre):
308
                s = s[len(pre) :]
309
        for suf in suffixes:
310
            if s.endswith(suf):
311
                s = s[: -len(suf)]
312
        return s
313
314
    @classmethod
315
    def strip_brackets(cls, text: str) -> str:
316
        """
317
        Strips any and all pairs of brackets from start and end of a string, but only if they're paired.
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (104/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
318
        See Also:
319
             strip_paired
320
        """
321
        pieces = [
322
            ("(", ")"),
323
            ("[", "]"),
324
            ("[", "]"),
325
            ("{", "}"),
326
            ("<", ">"),
327
            (Chars.lshell, Chars.rshell),
328
            (Chars.langle, Chars.rangle),
329
            (Chars.ldparen, Chars.rdparen),
330
            (Chars.ldbracket, Chars.rdbracket),
331
            (Chars.ldangle, Chars.rdangle),
332
            (Chars.ldshell, Chars.rdshell),
333
        ]
334
        return StringTools.strip_paired(text, pieces)
335
336
    @classmethod
337
    def strip_quotes(cls, text: str) -> str:
338
        """
339
        Strips any and all pairs of quotes from start and end of a string, but only if they're paired.
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (102/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
340
341
        See Also:
342
            strip_paired
343
        """
344
        pieces = [
345
            ("`", "`"),
346
            (Chars.lsq, Chars.rsq),
347
            (Chars.ldq, Chars.rdq),
348
            ("'", "'"),
349
            ('"', '"'),
350
        ]
351
        return StringTools.strip_paired(text, pieces)
352
353
    @classmethod
354
    def strip_brackets_and_quotes(cls, text: str) -> str:
355
        """
356
        Strips any and all pairs of brackets and quotes from start and end of a string, but only if they're paired.
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (115/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
357
358
        See Also:
359
            strip_paired
360
        """
361
        pieces = [
362
            ("(", ")"),
363
            ("[", "]"),
364
            ("[", "]"),
365
            ("{", "}"),
366
            ("<", ">"),
367
            (Chars.lshell, Chars.rshell),
368
            (Chars.langle, Chars.rangle),
369
            ("`", "`"),
370
            (Chars.lsq, Chars.rsq),
371
            (Chars.ldq, Chars.rdq),
372
            ("'", "'"),
373
            ('"', '"'),
374
            (Chars.ldparen, Chars.rdparen),
375
            (Chars.ldbracket, Chars.rdbracket),
376
            (Chars.ldangle, Chars.rdangle),
377
            (Chars.ldshell, Chars.rdshell),
378
        ]
379
        return StringTools.strip_paired(text, pieces)
380
381
    @classmethod
382
    def strip_paired(cls, text: str, pieces: Iterable[Tuple[str, str]]) -> str:
383
        """
384
        Strips pairs of (start, end) from the ends of strings.
385
386
        Example:
387
            .. code-block::
388
                StringTools.strip_paired("[(abc]", [("(", ")"), ("[", "]"))  # returns "(abc"
389
390
        Also see ``strip_brackets``
391
        """
392
        if any([a for a in pieces if len(a) != 2]):
393
            raise XValueError(
394
                f"Each item must be a string of length 2: (stard, end); got {pieces}",
395
                value=str(pieces),
396
            )
397
        text = str(text)
398
        while len(text) > 0:
399
            yes = False
400
            for a, b in pieces:
0 ignored issues
show
Coding Style Naming introduced by
Variable name "a" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style Naming introduced by
Variable name "b" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
401
                while text.startswith(a) and text.endswith(b):
402
                    text = text[1:-1]
403
                    yes = True
404
            if not yes:
405
                break
406
        return text
407
408
    @classmethod
409
    def replace_all(cls, s: str, rep: Mapping[str, str]) -> str:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
410
        """
411
        Simply replace multiple things in a string.
412
        """
413
        warnings.warn("replace_all will be removed", DeprecationWarning)
414
        for k, v in rep.items():
0 ignored issues
show
Coding Style Naming introduced by
Variable name "v" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
415
            s = s.replace(k, v)
416
        return s
417
418
    @classmethod
419
    def superscript(cls, s: Union[str, float]) -> str:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
420
        """
421
        Replaces digits, +, =, (, and ) with equivalent Unicode superscript chars (ex ¹).
422
        """
423
        return "".join(dict(zip("0123456789-+=()", "⁰¹²³⁴⁵⁶⁷⁸⁹⁻⁺⁼⁽⁾")).get(c, c) for c in s)
424
425
    @classmethod
426
    def subscript(cls, s: Union[str, float]) -> str:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
427
        """
428
        Replaces digits, +, =, (, and ) with equivalent Unicode subscript chars (ex ₁).
429
        """
430
        return "".join(dict(zip("0123456789+-=()", "₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎")).get(c, c) for c in s)
431
432
    @classmethod
433
    def unsuperscript(cls, s: Union[str, float]) -> str:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
434
        """
435
        Replaces Unicode superscript digits, +, =, (, and ) with normal chars.
436
        """
437
        return "".join(dict(zip("⁰¹²³⁴⁵⁶⁷⁸⁹⁻⁺⁼⁽⁾", "0123456789-+=()")).get(c, c) for c in s)
438
439
    @classmethod
440
    def unsubscript(cls, s: Union[str, float]) -> str:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
441
        """
442
        Replaces Unicode superscript digits, +, =, (, and ) with normal chars.
443
        """
444
        return "".join(dict(zip("₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎", "0123456789+-=()")).get(c, c) for c in s)
445
446
    @classmethod
447
    def dashes_to_hm(cls, s: str) -> str:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
448
        """
449
        Replaces most Latin-alphabet dash-like and hyphen-like characters with a hyphen-minus.
450
        """
451
        smallem = "﹘"
452
        smallhm = "﹣"
453
        fullhm = "-"
454
        for c in [
0 ignored issues
show
Coding Style Naming introduced by
Variable name "c" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
455
            Chars.em,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
456
            Chars.en,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
457
            Chars.fig,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
458
            Chars.minus,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
459
            Chars.hyphen,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
460
            Chars.nbhyphen,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
461
            smallem,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
462
            smallhm,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
463
            fullhm,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
464
        ]:
465
            s = str(s).replace(c, "-")
466
        return s
467
468
    @classmethod
469
    def pretty_float(cls, v: Union[float, int], n_sigfigs: Optional[int] = 5) -> str:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "v" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
470
        """
471
        Represents a float as a string, with symbols for NaN and infinity.
472
        The returned string always has a minus or + prepended. Strip off the plus with .lstrip('+').
473
        If v is an integer (by isinstance), makes sure to display without a decimal point.
474
        If n_sigfigs < 2, will never have a
475
        For ex:
476
            - StringTools.pretty_float(.2222222)       # '+0.22222'
477
            - StringTools.pretty_float(-.2222222)      # '−0.22222' (Unicode minus)
478
            - StringTools.pretty_float(-float('inf'))  # '−∞'
479
            - StringTools.pretty_float(np.NaN)         # '⌀'
480
        """
481
        # TODO this seems absurdly long for what it does
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
482
        if n_sigfigs is None or n_sigfigs < 1:
483
            raise OutOfRangeError(
484
                f"Sigfigs of {n_sigfigs} is nonpositive",
485
                value=n_sigfigs,
486
                minimum=1,
487
            )
488
        # first, handle NaN and infinities
489
        if np.isneginf(v):
0 ignored issues
show
unused-code introduced by
Unnecessary "elif" after "return"
Loading history...
490
            return Chars.minus + Chars.inf
491
        elif np.isposinf(v):
492
            return "+" + Chars.inf
493
        elif np.isnan(v):
494
            return Chars.null
495
        # sweet. it's a regular float or int.
496
        if n_sigfigs is None:
497
            s = StringTools.strip_empty_decimal(str(v))
0 ignored issues
show
Coding Style Naming introduced by
Variable name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
498
        else:
499
            # yes, this is weird. we need to convert from str to float then back to str
500
            s = str(float(str(("%." + str(n_sigfigs) + "g") % v)))
0 ignored issues
show
Coding Style Naming introduced by
Variable name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
501
        # remove the .0 if the precision doesn't support it
502
        # if v >= 1 and n_sigfigs<2, it couldn't have a decimal
503
        # and if n_sigfigs<1, it definitely can't
504
        # and ... %g does this.
505
        if isinstance(v, int) or n_sigfigs is not None and n_sigfigs < 2:
506
            s = StringTools.strip_empty_decimal(s)
0 ignored issues
show
Coding Style Naming introduced by
Variable name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
507
        # prepend + or - (unless 0)
508
        if float(s) == 0.0:
509
            return s
510
        s = s.replace("-", Chars.minus)
0 ignored issues
show
Coding Style Naming introduced by
Variable name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
511
        if not s.startswith(Chars.minus):
512
            s = "+" + s[1:]
0 ignored issues
show
Coding Style Naming introduced by
Variable name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
513
        if len(s) > 1 and s[1] == ".":
514
            s = s[0] + "0." + s[2:]
0 ignored issues
show
Coding Style Naming introduced by
Variable name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
515
        return s
516
517
    @classmethod
518
    def pretty_function(
0 ignored issues
show
best-practice introduced by
Too many return statements (8/6)
Loading history...
519
        cls, function, *, with_address: bool = False, prefix: str = "⟨", suffix: str = "⟩"
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
520
    ) -> str:
521
        """
522
        Get a better and shorter name for a function than str(function).
523
        Ex: pprint_function(lambda s: s)  == '<λ>'
524
        - Instead of '<bound method ...', you'll get '<name(nargs)>'
525
        - Instead of 'lambda ...', you'll get '<λ(nargs)>'
526
        - etc.
527
        NOTE 1: If function is None, returns '⌀'
528
        NOTE 2: If function does not have __name__, returns prefix + type(function) + <address> + suffix
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (104/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
529
        NOTE 3: If it's a primitive, returns str(function)
530
531
        Args:
532
            function: Can be anything, but especially useful for functions
533
            with_address: Include ``@ hex-mem-addr`` in the name
534
            prefix: Prefix to the whole string
535
            suffix: Suffix to the whole string
536
        """
537
        if function is None:
538
            return Chars.null
539
        n_args = str(function.__code__.co_argcount) if hasattr(function, "__code__") else "?"
540
        pat = regex.compile(r"^<bound method [^ .]+\.([^ ]+) of (.+)>$", flags=regex.V1)
541
        boundmatch = pat.fullmatch(str(function))
542
        pat = regex.compile(r"<([A-Za-z0-9_.<>]+)[ ']*object", flags=regex.V1)
543
        objmatch = pat.search(str(function))  # instance of global or local class
544
        addr = " @ " + hex(id(function)) if with_address else ""
545
        if cls.is_lambda(function):
0 ignored issues
show
unused-code introduced by
Unnecessary "elif" after "return"
Loading history...
546
            # simplify lambda functions!
547
            return prefix + "λ(" + n_args + ")" + addr + suffix
548
        elif boundmatch is not None:
549
            # it's a method (bound function)
550
            # don't show the address of the instance AND its method
551
            pat = regex.compile(r"@ ?0x[0-9a-hA-H]+\)?$", flags=regex.V1)
552
            s = pat.sub("", boundmatch.group(2)).strip()
0 ignored issues
show
Coding Style Naming introduced by
Variable name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
553
            return (
554
                prefix + "`" + s + "`." + boundmatch.group(1) + "(" + n_args + ")" + addr + suffix
555
            )
556
        elif isinstance(function, type):
557
            # it's a class
558
            return prefix + "type:" + function.__name__ + suffix
559
        elif callable(function):
560
            # it's an actual function
561
            return prefix + function.__name__ + addr + suffix
562
        elif hasattr(function, "__dict__") and len(function.__dict__) > 0:
563
            # it's a member with attributes
564
            # it's interesting enough that it may have a good __str__
565
            s = StringTools.strip_off_end(
0 ignored issues
show
Coding Style Naming introduced by
Variable name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
566
                StringTools.strip_off_start(str(function), prefix), suffix
567
            )
568
            return prefix + s + addr + suffix
569
        elif objmatch is not None:
570
            # it's an instance without attributes
571
            s = objmatch.group(1)
0 ignored issues
show
Coding Style Naming introduced by
Variable name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
572
            if "." in s:
573
                s = s[s.rindex(".") + 1 :]
0 ignored issues
show
Coding Style Naming introduced by
Variable name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
574
            return prefix + s + addr + suffix
575
        else:
576
            # it's a primitive, etc
577
            s = StringTools.strip_off_end(
0 ignored issues
show
Coding Style Naming introduced by
Variable name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
578
                StringTools.strip_off_start(str(function), prefix), suffix
579
            )
580
            return s
581
582
    @classmethod
583
    def greek_to_name(cls) -> Mapping[str, str]:
584
        """
585
        Returns a dict from Greek lowercase+uppercase Unicode chars to their full names.
586
        """
587
        return dict(StringTools._greek_alphabet)
588
589
    @classmethod
590
    def name_to_greek(cls) -> Mapping[str, str]:
591
        """
592
        Returns a dict from Greek lowercase+uppercase letter names to their Unicode chars.
593
        """
594
        return {v: k for k, v in StringTools._greek_alphabet.items()}
595
596
    @classmethod
597
    def fix_greek(cls, s: str, lowercase: bool = False) -> str:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
598
        """
599
        Replaces Greek letter names with their Unicode equivalents.
600
        Does this correctly by replacing superstrings before substrings.
601
        Ex: '1-beta' is '1-β' rather than '1-bη'
602
        If lowercase is True: Replaces Beta, BeTa, and BETA with β
603
        Else: Replaces Beta with a capital Greek Beta and ignores BETA and BeTa.
604
        """
605
        # Clever if I may say so:
606
        # If we just sort from longest to shortest, we can't replace substrings by accident
607
        # For example we'll replace 'beta' before 'eta', so '1-beta' won't become '1-bη'
608
        greek = sorted(
609
            [(v, k) for k, v in StringTools._greek_alphabet.items()],
610
            key=lambda t: -len(t[1]),
611
        )
612
        for k, v in greek:
0 ignored issues
show
Coding Style Naming introduced by
Variable name "v" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
613
            if k[0].isupper() and lowercase:
614
                continue
615
            if lowercase:
616
                s = regex.compile(k, flags=regex.V1 | regex.IGNORECASE).sub(v, s)
617
            else:
618
                s = s.replace(k, v)
619
        return s
620
621
    @classmethod
622
    def join(
623
        cls,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
624
        seq: Iterable[T],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
625
        *,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
626
        sep: str = "\t",
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
627
        attr: Optional[str] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
628
        prefix: str = "",
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
629
        suffix: str = "",
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
630
    ) -> str:
631
        """
632
        Join elements into a str more easily than ''.join. Just simplifies potentially long expressions.
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (104/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
633
        Won't break with ValueError if the elements aren't strs.
634
        Ex:
635
            - StringTools.join([1,2,3])  # "1    2    3"
636
            - StringTools.join(cars, sep=',', attr='make', prefix="(", suffix=")")`  # "(Ford),(Ford),(BMW)"
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (108/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
637
638
            seq: Sequence of elements
639
            sep: Delimiter
640
            attr: Get this attribute from each element (in `seq`), or use the element itself if None
641
            prefix: Prefix before each item
642
            suffix: Suffix after each item
643
644
        Returns:
645
            A string
646
        """
647
        if attr is None:
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
648
            return sep.join([prefix + str(s) + suffix for s in seq])
649
        else:
650
            return sep.join([prefix + str(getattr(s, attr)) + suffix for s in seq])
651
652
    @classmethod
653
    def join_kv_neat(cls, seq: Mapping[T, V], *, eq: str = "=", sep: str = ", ") -> str:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
Coding Style Naming introduced by
Variable name "eq" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
654
        return cls.join_kv(seq, sep=sep, eq=eq)
655
656
    @classmethod
657
    def join_kv_spaced(cls, seq: Mapping[T, V], *, eq: str = ": ", sep: str = "; ") -> str:
0 ignored issues
show
Coding Style Naming introduced by
Variable name "eq" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
introduced by
Missing function or method docstring
Loading history...
658
        return cls.join_kv(seq, sep=sep, eq=eq)
659
660
    @classmethod
661
    def join_kv_quoted(cls, seq: Mapping[T, V], *, eq: str = ": ", sep: str = "; ") -> str:
0 ignored issues
show
Coding Style Naming introduced by
Variable name "eq" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
introduced by
Missing function or method docstring
Loading history...
662
        return cls.join_kv(seq, sep=sep, eq=eq, prefix="'", suffix="'")
663
664
    @classmethod
665
    def join_kv(
666
        cls,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
667
        seq: Mapping[T, V],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
668
        *,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
669
        sep: str = "\t",
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
670
        eq: str = "=",
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
Coding Style Naming introduced by
Variable name "eq" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
671
        prefix: str = "",
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
672
        suffix: str = "",
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
673
    ) -> str:
674
        """
675
        Joins dict elements into a str like 'a=1, b=2, c=3`.
676
        Won't break with ValueError if the keys or values aren't strs.
677
678
        Args:
679
            seq: Dict-like, with ``items()``
680
            sep: Delimiter
681
            eq: Separates a key with its value
682
            prefix: Prepend before every key
683
            suffix: Append after every value
684
685
        Returns:
686
            A string
687
        """
688
        return sep.join([prefix + str(k) + eq + str(v) + suffix for k, v in seq.items()])
689
690
    _greek_alphabet = {
691
        "\u0391": "Alpha",
692
        "\u0392": "Beta",
693
        "\u0393": "Gamma",
694
        "\u0394": "Delta",
695
        "\u0395": "Epsilon",
696
        "\u0396": "Zeta",
697
        "\u0397": "Eta",
698
        "\u0398": "Theta",
699
        "\u0399": "Iota",
700
        "\u039A": "Kappa",
701
        "\u039B": "Lambda",
702
        "\u039C": "Mu",
703
        "\u039D": "Nu",
704
        "\u039E": "Xi",
705
        "\u039F": "Omicron",
706
        "\u03A0": "Pi",
707
        "\u03A1": "Rho",
708
        "\u03A3": "Sigma",
709
        "\u03A4": "Tau",
710
        "\u03A5": "Upsilon",
711
        "\u03A6": "Phi",
712
        "\u03A7": "Chi",
713
        "\u03A8": "Psi",
714
        "\u03A9": "Omega",
715
        "\u03B1": "alpha",
716
        "\u03B2": "beta",
717
        "\u03B3": "gamma",
718
        "\u03B4": "delta",
719
        "\u03B5": "epsilon",
720
        "\u03B6": "zeta",
721
        "\u03B7": "eta",
722
        "\u03B8": "theta",
723
        "\u03B9": "iota",
724
        "\u03BA": "kappa",
725
        "\u03BB": "lambda",
726
        "\u03BC": "mu",
727
        "\u03BD": "nu",
728
        "\u03BE": "xi",
729
        "\u03BF": "omicron",
730
        "\u03C0": "pi",
731
        "\u03C1": "rho",
732
        "\u03C3": "sigma",
733
        "\u03C4": "tau",
734
        "\u03C5": "upsilon",
735
        "\u03C6": "phi",
736
        "\u03C7": "chi",
737
        "\u03C8": "psi",
738
        "\u03C9": "omega",
739
    }
740
741
742
__all__ = ["StringTools"]
743