Passed
Push — main ( 29adcf...6c3df6 )
by Douglas
01:40
created

StringTools.greek_to_name()   A

Complexity

Conditions 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nop 1
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
import json
0 ignored issues
show
introduced by
Missing module docstring
Loading history...
2
import warnings
3
from copy import copy
4
from typing import Any, Callable, Iterable, Mapping, Optional, Sequence, Tuple, TypeVar, Union
5
6
import numpy as np
0 ignored issues
show
introduced by
Unable to import 'numpy'
Loading history...
7
import regex
0 ignored issues
show
introduced by
Unable to import 'regex'
Loading history...
8
9
from pocketutils.core import JsonEncoder
10
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...
11
from pocketutils.core.exceptions import OutOfRangeError
12
from pocketutils.tools.base_tools import BaseTools
13
14
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...
15
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...
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 (38/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 cls.retab(
26
            json.dumps(
27
                dct,
28
                default=JsonEncoder().default,
29
                sort_keys=True,
30
                indent=1,
31
                ensure_ascii=False,
32
            ),
33
            1,
34
        )
35
36
    @classmethod
37
    def extract_group_1(
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
38
        cls, pattern: Union[str, regex.Pattern], value: Optional[str], ignore_null: bool = False
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
39
    ) -> Optional[str]:
40
        warnings.warn("extract_group_1 will be removed", DeprecationWarning)
41
        """
42
        Performs a ``fullmatch`` on a target string and returns capture group 1, or None if there was no match.
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...
43
44
        Args:
45
            pattern: Regex pattern
46
            value: The target string
47
            ignore_null: If True, returns None if ``value`` is None; otherwise raises a ValueError if ``value`` is None
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...
48
                         (Useful for *map*-like operations.)
49
50
        Returns The first capture group, or None
51
        """
0 ignored issues
show
Unused Code introduced by
This string statement has no effect and could be removed.
Loading history...
52
        pattern = (
53
            pattern
54
            if isinstance(pattern, regex.Pattern)
55
            else regex.compile(pattern, flags=regex.V1)
56
        )
57
        if pattern.groups != 1:
58
            raise ValueError(f"Pattern {pattern} has {pattern.groups} groups, not 1")
59
        if value is None and ignore_null:
60
            return None
61
        match = pattern.fullmatch(value)
62
        if match is None:
63
            return None
64
        return match.group(1)
65
66
    @classmethod
67
    def roman_to_arabic(
68
        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...
69
    ) -> int:
70
        """
71
        Converts roman numerals to an integer.
72
73
        Args:
74
            roman: A string like "MCIV"
75
            min_val: Raise a ValueError if the parsed value is less than this
76
            max_val: Raise a ValueError if the parsed value is more than this
77
78
        Returns:
79
            The arabic numeral as a Python int
80
        """
81
        # this order is IMPORTANT!
82
        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...
83
            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
84
        )
85
        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...
86
            roman = roman.replace(k, str(v))
87
        # it'll just error if it's empty
88
        try:
89
            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...
90
        except (ValueError, StopIteration):
91
            raise ValueError(f"Cannot parse roman numerals '{roman}'")
92
        if min_val is not None and value < min_val or min_val is not None and roman > max_val:
93
            raise ValueError(f"Value {roman} (int={value}) is out of range ({min_val}, {max_val})")
94
        return value
95
96
    @classmethod
97
    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...
98
        """
99
        Converts indentation with spaces to tab indentation.
100
101
        Args:
102
            s: The string to convert
103
            nspaces: A tab is this number of spaces
104
        """
105
106
        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...
107
            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...
108
            return "\t" * n + " " * (len(m.group(1)) % nspaces)
109
110
        return regex.sub("^( +)", fix, s, flags=regex.V1 | regex.MULTILINE)
111
112
    @classmethod
113
    def strip_empty_decimal(cls, num: Union[float, str]) -> str:
114
        """
115
        Replaces prefix . with 0. and strips trailing .0 and trailing .
116
        """
117
        try:
118
            float(num)
119
        except TypeError:
120
            if not isinstance(num, str):
121
                raise TypeError("Must be either str or float-like") from None
122
        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...
123
        if t.startswith("."):
124
            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...
125
        if "." in t:
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
126
            return t.rstrip("0").rstrip(".")
127
        else:
128
            return t
129
130
    @classmethod
131
    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...
132
        """
133
        Splits by tabs, but preserving quoted tabs, stripping quotes.
134
        """
135
        pat = regex.compile(r"""((?:[^\t"']|"[^"]*"|'[^']*')+)""", flags=regex.V1)
136
        # Don't strip double 2x quotes: ex ""55"" should be "55", not 55
137
        def strip(i: str) -> str:
138
            if i.endswith('"') or i.endswith("'"):
139
                i = i[:-1]
140
            if i.startswith('"') or i.startswith("'"):
141
                i = i[1:]
142
            return i.strip()
143
144
        return [strip(i) for i in pat.findall(s)]
145
146
    @classmethod
147
    def truncate(cls, s: Optional[str], n: int, always_dots: bool = False) -> Optional[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...
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...
148
        """
149
        Returns a string if it has ``n`` or fewer characters;
150
        otherwise truncates to length ``n-1`` and appends ``…`` (UTF character).
151
        If ``s`` is None and ``always_dots`` is True, returns ``n`` copies of ``.`` (as a string).
152
        If ``s`` is None otherwise, returns None.
153
154
        Args:
155
            s: The string
156
            n: The maximum length, inclusive
157
            always_dots: Use dots instead of returning None; see above
158
159
        Returns:
160
            A string or None
161
        """
162
        if always_dots is not False:
163
            warnings.warn("always_dots argument will be removed", DeprecationWarning)
164
        if s is None and always_dots:
165
            return "…" * n
166
        if s is None:
167
            return None
168
        if len(s) > n:
169
            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...
170
            return s[:nx] + "…"
171
        return s
172
173
    # these are provided to avoid having to call with labdas or functools.partial
174
    @classmethod
175
    def truncator(cls, n: int = 40, always_dots: bool = False) -> Callable[[str], str]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
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...
176
        # pretty much functools.partial
177
        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...
178
            return cls.truncate(s, n, always_dots)
179
180
        trunc.__name__ = f"truncate({n},{'…' if always_dots else ''})"
181
        return trunc
182
183
    @classmethod
184
    def truncate60(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...
introduced by
Missing function or method docstring
Loading history...
185
        return StringTools.truncate(s, 60)
186
187
    @classmethod
188
    def truncate40(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...
introduced by
Missing function or method docstring
Loading history...
189
        return StringTools.truncate(s, 64)
190
191
    @classmethod
192
    def truncate30(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...
introduced by
Missing function or method docstring
Loading history...
193
        return StringTools.truncate(s, 30)
194
195
    @classmethod
196
    def truncate20(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...
introduced by
Missing function or method docstring
Loading history...
197
        return StringTools.truncate(s, 20)
198
199
    @classmethod
200
    def truncate10(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...
introduced by
Missing function or method docstring
Loading history...
201
        return StringTools.truncate(s, 10)
202
203
    @classmethod
204
    def truncate10_nodots(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...
introduced by
Missing function or method docstring
Loading history...
205
        return StringTools.truncate(s, 10, False)
206
207
    @classmethod
208
    def longest(cls, parts: Iterable[T]) -> T:
209
        """
210
        Returns an element with the highest ``len``.
211
        """
212
        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...
213
        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...
214
            if len(x) > len(mx):
215
                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...
216
        return mx
217
218
    @classmethod
219
    def longest_str(cls, parts: Iterable[str]) -> str:
220
        """
221
        Returns the argmax by length.
222
        """
223
        return cls.longest(parts)
224
225
    @classmethod
226
    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...
227
        """
228
        Strips the full string ``pre`` from the start of ``str``.
229
        See ``Tools.strip_off`` for more info.
230
        """
231
        if not isinstance(pre, str):
232
            raise TypeError(f"{pre} is not a string")
233
        if s.startswith(pre):
234
            s = s[len(pre) :]
235
        return s
236
237
    @classmethod
238
    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...
239
        """
240
        Strips the full string ``suf`` from the end of ``str``.
241
        See `Tools.strip_off` for more info.
242
        """
243
        if not isinstance(suf, str):
244
            raise TypeError(f"{suf} is not a string")
245
        if s.endswith(suf):
246
            s = s[: -len(suf)]
247
        return s
248
249
    @classmethod
250
    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...
251
        """
252
        Strip a substring from the beginning or end of a string (at most 1 occurrence).
253
        """
254
        return StringTools.strip_off_start(
255
            StringTools.strip_off_end(s, prefix_or_suffix), prefix_or_suffix
256
        )
257
258
    @classmethod
259
    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...
260
        """
261
        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...
262
        """
263
        return StringTools.strip_off_start(StringTools.strip_off_end(s, suffix), prefix)
264
265
    @classmethod
266
    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...
267
        cls,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
268
        s: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
269
        prefixes: Union[str, Sequence[str]],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
270
        suffixes: Union[str, Sequence[str]],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
271
    ) -> str:
272
        """
273
        Flexible variant that strips any number of prefixes and any number of suffixes.
274
        Also less type-safe than more specific variants.
275
        Note that the order of the prefixes (or suffixes) DOES matter.
276
        """
277
        prefixes = (
278
            [str(z) for z in prefixes]
279
            if StringTools.is_true_iterable(prefixes)
280
            else [str(prefixes)]
281
        )
282
        suffixes = (
283
            [str(z) for z in suffixes]
284
            if StringTools.is_true_iterable(suffixes)
285
            else [str(suffixes)]
286
        )
287
        s = str(s)
288
        for pre in prefixes:
289
            if s.startswith(pre):
290
                s = s[len(pre) :]
291
        for suf in suffixes:
292
            if s.endswith(suf):
293
                s = s[: -len(suf)]
294
        return s
295
296
    @classmethod
297
    def strip_brackets(cls, text: str) -> str:
298
        """
299
        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...
300
        See Also:
301
             strip_paired
302
        """
303
        pieces = [
304
            ("(", ")"),
305
            ("[", "]"),
306
            ("[", "]"),
307
            ("{", "}"),
308
            ("<", ">"),
309
            (Chars.lshell, Chars.rshell),
310
            (Chars.langle, Chars.rangle),
311
            (Chars.ldparen, Chars.rdparen),
312
            (Chars.ldbracket, Chars.rdbracket),
313
            (Chars.ldangle, Chars.rdangle),
314
            (Chars.ldshell, Chars.rdshell),
315
        ]
316
        return StringTools.strip_paired(text, pieces)
317
318
    @classmethod
319
    def strip_quotes(cls, text: str) -> str:
320
        """
321
        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...
322
323
        See Also:
324
            strip_paired
325
        """
326
        pieces = [
327
            ("`", "`"),
328
            (Chars.lsq, Chars.rsq),
329
            (Chars.ldq, Chars.rdq),
330
            ("'", "'"),
331
            ('"', '"'),
332
        ]
333
        return StringTools.strip_paired(text, pieces)
334
335
    @classmethod
336
    def strip_brackets_and_quotes(cls, text: str) -> str:
337
        """
338
        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...
339
340
        See Also:
341
            strip_paired
342
        """
343
        pieces = [
344
            ("(", ")"),
345
            ("[", "]"),
346
            ("[", "]"),
347
            ("{", "}"),
348
            ("<", ">"),
349
            (Chars.lshell, Chars.rshell),
350
            (Chars.langle, Chars.rangle),
351
            ("`", "`"),
352
            (Chars.lsq, Chars.rsq),
353
            (Chars.ldq, Chars.rdq),
354
            ("'", "'"),
355
            ('"', '"'),
356
            (Chars.ldparen, Chars.rdparen),
357
            (Chars.ldbracket, Chars.rdbracket),
358
            (Chars.ldangle, Chars.rdangle),
359
            (Chars.ldshell, Chars.rdshell),
360
        ]
361
        return StringTools.strip_paired(text, pieces)
362
363
    @classmethod
364
    def strip_paired(cls, text: str, pieces: Iterable[Tuple[str, str]]) -> str:
365
        """
366
        Strips pairs of (start, end) from the ends of strings.
367
368
        Example:
369
            >>> StringTools.strip_paired("[(abc]", [("(", ")"), ("[", "]"))  # returns "(abc"
370
371
        Also see ``strip_brackets``
372
        """
373
        if any([a for a in pieces if len(a) != 2]):
374
            raise ValueError(f"Each item must be a string of length 2: (stard, end); got {pieces}")
375
        text = str(text)
376
        while len(text) > 0:
377
            yes = False
378
            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...
379
                while text.startswith(a) and text.endswith(b):
380
                    text = text[1:-1]
381
                    yes = True
382
            if not yes:
383
                break
384
        return text
385
386
    @classmethod
387
    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...
388
        """
389
        Simply replace multiple things in a string.
390
        """
391
        warnings.warn("replace_all will be removed", DeprecationWarning)
392
        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...
393
            s = s.replace(k, v)
394
        return s
395
396
    @classmethod
397
    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...
398
        """
399
        Replaces digits, +, =, (, and ) with equivalent Unicode superscript chars (ex ¹).
400
        """
401
        return "".join(dict(zip("0123456789-+=()", "⁰¹²³⁴⁵⁶⁷⁸⁹⁻⁺⁼⁽⁾")).get(c, c) for c in s)
402
403
    @classmethod
404
    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...
405
        """
406
        Replaces digits, +, =, (, and ) with equivalent Unicode subscript chars (ex ₁).
407
        """
408
        return "".join(dict(zip("0123456789+-=()", "₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎")).get(c, c) for c in s)
409
410
    @classmethod
411
    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...
412
        """
413
        Replaces Unicode superscript digits, +, =, (, and ) with normal chars.
414
        """
415
        return "".join(dict(zip("⁰¹²³⁴⁵⁶⁷⁸⁹⁻⁺⁼⁽⁾", "0123456789-+=()")).get(c, c) for c in s)
416
417
    @classmethod
418
    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...
419
        """
420
        Replaces Unicode superscript digits, +, =, (, and ) with normal chars.
421
        """
422
        return "".join(dict(zip("₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎", "0123456789+-=()")).get(c, c) for c in s)
423
424
    @classmethod
425
    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...
426
        """
427
        Replaces most Latin-alphabet dash-like and hyphen-like characters with a hyphen-minus.
428
        """
429
        smallem = "﹘"
430
        smallhm = "﹣"
431
        fullhm = "-"
432
        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...
433
            Chars.em,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
434
            Chars.en,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
435
            Chars.fig,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
436
            Chars.minus,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
437
            Chars.hyphen,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
438
            Chars.nbhyphen,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
439
            smallem,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
440
            smallhm,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
441
            fullhm,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
442
        ]:
443
            s = str(s).replace(c, "-")
444
        return s
445
446
    @classmethod
447
    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...
448
        """
449
        Represents a float as a string, with symbols for NaN and infinity.
450
        The returned string always has a minus or + prepended. Strip off the plus with .lstrip('+').
451
        If v is an integer (by isinstance), makes sure to display without a decimal point.
452
        If n_sigfigs < 2, will never have a
453
        For ex:
454
            - StringTools.pretty_float(.2222222)       # '+0.22222'
455
            - StringTools.pretty_float(-.2222222)      # '−0.22222' (Unicode minus)
456
            - StringTools.pretty_float(-float('inf'))  # '−∞'
457
            - StringTools.pretty_float(np.NaN)         # '⌀'
458
        """
459
        # 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...
460
        if n_sigfigs is None or n_sigfigs < 1:
461
            raise OutOfRangeError(
462
                f"Sigfigs of {n_sigfigs} is nonpositive",
463
                value=n_sigfigs,
464
                minimum=1,
465
            )
466
        # first, handle NaN and infinities
467
        if np.isneginf(v):
0 ignored issues
show
unused-code introduced by
Unnecessary "elif" after "return"
Loading history...
468
            return Chars.minus + Chars.inf
469
        elif np.isposinf(v):
470
            return "+" + Chars.inf
471
        elif np.isnan(v):
472
            return Chars.null
473
        # sweet. it's a regular float or int.
474
        if n_sigfigs is None:
475
            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...
476
        else:
477
            # yes, this is weird. we need to convert from str to float then back to str
478
            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...
479
        # remove the .0 if the precision doesn't support it
480
        # if v >= 1 and n_sigfigs<2, it couldn't have a decimal
481
        # and if n_sigfigs<1, it definitely can't
482
        # and ... %g does this.
483
        if isinstance(v, int) or n_sigfigs is not None and n_sigfigs < 2:
484
            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...
485
        # prepend + or - (unless 0)
486
        if float(s) == 0.0:
487
            return s
488
        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...
489
        if not s.startswith(Chars.minus):
490
            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...
491
        if len(s) > 1 and s[1] == ".":
492
            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...
493
        return s
494
495
    @classmethod
496
    def pretty_function(
0 ignored issues
show
best-practice introduced by
Too many return statements (8/6)
Loading history...
497
        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...
498
    ) -> str:
499
        """
500
        Get a better and shorter name for a function than str(function).
501
        Ex: pprint_function(lambda s: s)  == '<λ>'
502
        - Instead of '<bound method ...', you'll get '<name(nargs)>'
503
        - Instead of 'lambda ...', you'll get '<λ(nargs)>'
504
        - etc.
505
        NOTE 1: If function is None, returns '⌀'
506
        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...
507
        NOTE 3: If it's a primitive, returns str(function)
508
509
        Args:
510
            function: Can be anything, but especially useful for functions
511
            with_address: Include `@ hex-mem-addr` in the name
512
            prefix: Prefix to the whole string
513
            suffix: Suffix to the whole string
514
        """
515
        if function is None:
516
            return Chars.null
517
        n_args = str(function.__code__.co_argcount) if hasattr(function, "__code__") else "?"
518
        pat = regex.compile(r"^<bound method [^ .]+\.([^ ]+) of (.+)>$", flags=regex.V1)
519
        boundmatch = pat.fullmatch(str(function))
520
        pat = regex.compile(r"<([A-Za-z0-9_.<>]+)[ ']*object", flags=regex.V1)
521
        objmatch = pat.search(str(function))  # instance of global or local class
522
        addr = " @ " + hex(id(function)) if with_address else ""
523
        if cls.is_lambda(function):
0 ignored issues
show
unused-code introduced by
Unnecessary "elif" after "return"
Loading history...
524
            # simplify lambda functions!
525
            return prefix + "λ(" + n_args + ")" + addr + suffix
526
        elif boundmatch is not None:
527
            # it's a method (bound function)
528
            # don't show the address of the instance AND its method
529
            pat = regex.compile(r"@ ?0x[0-9a-hA-H]+\)?$", flags=regex.V1)
530
            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...
531
            return (
532
                prefix + "`" + s + "`." + boundmatch.group(1) + "(" + n_args + ")" + addr + suffix
533
            )
534
        elif isinstance(function, type):
535
            # it's a class
536
            return prefix + "type:" + function.__name__ + suffix
537
        elif callable(function):
538
            # it's an actual function
539
            return prefix + function.__name__ + addr + suffix
540
        elif hasattr(function, "__dict__") and len(function.__dict__) > 0:
541
            # it's a member with attributes
542
            # it's interesting enough that it may have a good __str__
543
            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...
544
                StringTools.strip_off_start(str(function), prefix), suffix
545
            )
546
            return prefix + s + addr + suffix
547
        elif objmatch is not None:
548
            # it's an instance without attributes
549
            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...
550
            if "." in s:
551
                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...
552
            return prefix + s + addr + suffix
553
        else:
554
            # it's a primitive, etc
555
            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...
556
                StringTools.strip_off_start(str(function), prefix), suffix
557
            )
558
            return s
559
560
    @classmethod
561
    def greek_to_name(cls) -> Mapping[str, str]:
562
        """
563
        Returns a dict from Greek lowercase+uppercase Unicode chars to their full names
564
        @return A defensive copy
565
        """
566
        return copy(StringTools._greek_alphabet)
567
568
    @classmethod
569
    def name_to_greek(cls) -> Mapping[str, str]:
570
        """
571
        Returns a dict from Greek lowercase+uppercase letter names to their Unicode chars
572
        @return A defensive copy
573
        """
574
        return {v: k for k, v in StringTools._greek_alphabet.items()}
575
576
    @classmethod
577
    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...
578
        """
579
        Replaces Greek letter names with their Unicode equivalents.
580
        Does this correctly by replacing superstrings before substrings.
581
        Ex: '1-beta' is '1-β' rather than '1-bη'
582
        If lowercase is True: Replaces Beta, BeTa, and BETA with β
583
        Else: Replaces Beta with a capital Greek Beta and ignores BETA and BeTa.
584
        """
585
        # Clever if I may say so:
586
        # If we just sort from longest to shortest, we can't replace substrings by accident
587
        # For example we'll replace 'beta' before 'eta', so '1-beta' won't become '1-bη'
588
        greek = sorted(
589
            [(v, k) for k, v in StringTools._greek_alphabet.items()],
590
            key=lambda t: -len(t[1]),
591
        )
592
        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...
593
            if k[0].isupper() and lowercase:
594
                continue
595
            if lowercase:
596
                s = regex.compile(k, flags=regex.V1 | regex.IGNORECASE).sub(v, s)
597
            else:
598
                s = s.replace(k, v)
599
        return s
600
601
    @classmethod
602
    def join(
603
        cls,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
604
        seq: Iterable[T],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
605
        *,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
606
        sep: str = "\t",
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
607
        attr: Optional[str] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
608
        prefix: str = "",
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
609
        suffix: str = "",
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
610
    ) -> str:
611
        """
612
        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...
613
        Won't break with ValueError if the elements aren't strs.
614
        Ex:
615
            - StringTools.join([1,2,3])  # "1    2    3"
616
            - 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...
617
618
            seq: Sequence of elements
619
            sep: Delimiter
620
            attr: Get this attribute from each element (in `seq`), or use the element itself if None
621
            prefix: Prefix before each item
622
            suffix: Suffix after each item
623
624
        Returns:
625
            A string
626
        """
627
        if attr is None:
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
628
            return sep.join([prefix + str(s) + suffix for s in seq])
629
        else:
630
            return sep.join([prefix + str(getattr(s, attr)) + suffix for s in seq])
631
632
    @classmethod
633
    def join_kv(
634
        cls,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
635
        seq: Mapping[T, V],
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
636
        *,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
637
        sep: str = "\t",
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
638
        eq: 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...
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
639
        prefix: str = "",
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
640
        suffix: str = "",
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
641
    ) -> str:
642
        """
643
        Joins dict elements into a str like 'a=1, b=2, c=3`.
644
        Won't break with ValueError if the keys or values aren't strs.
645
646
        Args:
647
            seq: Dict-like, with ``items()``
648
            sep: Delimiter
649
            eq: Separates a key with its value
650
            prefix: Prepend before every key
651
            suffix: Append after every value
652
653
        Returns:
654
            A string
655
        """
656
        return sep.join([prefix + str(k) + eq + str(v) + suffix for k, v in seq.items()])
657
658
    _greek_alphabet = {
659
        "\u0391": "Alpha",
660
        "\u0392": "Beta",
661
        "\u0393": "Gamma",
662
        "\u0394": "Delta",
663
        "\u0395": "Epsilon",
664
        "\u0396": "Zeta",
665
        "\u0397": "Eta",
666
        "\u0398": "Theta",
667
        "\u0399": "Iota",
668
        "\u039A": "Kappa",
669
        "\u039B": "Lambda",
670
        "\u039C": "Mu",
671
        "\u039D": "Nu",
672
        "\u039E": "Xi",
673
        "\u039F": "Omicron",
674
        "\u03A0": "Pi",
675
        "\u03A1": "Rho",
676
        "\u03A3": "Sigma",
677
        "\u03A4": "Tau",
678
        "\u03A5": "Upsilon",
679
        "\u03A6": "Phi",
680
        "\u03A7": "Chi",
681
        "\u03A8": "Psi",
682
        "\u03A9": "Omega",
683
        "\u03B1": "alpha",
684
        "\u03B2": "beta",
685
        "\u03B3": "gamma",
686
        "\u03B4": "delta",
687
        "\u03B5": "epsilon",
688
        "\u03B6": "zeta",
689
        "\u03B7": "eta",
690
        "\u03B8": "theta",
691
        "\u03B9": "iota",
692
        "\u03BA": "kappa",
693
        "\u03BB": "lambda",
694
        "\u03BC": "mu",
695
        "\u03BD": "nu",
696
        "\u03BE": "xi",
697
        "\u03BF": "omicron",
698
        "\u03C0": "pi",
699
        "\u03C1": "rho",
700
        "\u03C3": "sigma",
701
        "\u03C4": "tau",
702
        "\u03C5": "upsilon",
703
        "\u03C6": "phi",
704
        "\u03C7": "chi",
705
        "\u03C8": "psi",
706
        "\u03C9": "omega",
707
    }
708
709
710
__all__ = ["StringTools"]
711