istr-python 1.1.9__tar.gz → 1.1.10__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {istr_python-1.1.9 → istr_python-1.1.10}/PKG-INFO +4 -1
- {istr_python-1.1.9 → istr_python-1.1.10}/README.md +3 -0
- {istr_python-1.1.9 → istr_python-1.1.10}/istr/istr.py +69 -32
- {istr_python-1.1.9 → istr_python-1.1.10}/istr_python.egg-info/PKG-INFO +4 -1
- {istr_python-1.1.9 → istr_python-1.1.10}/pyproject.toml +1 -1
- {istr_python-1.1.9 → istr_python-1.1.10}/tests/test_istr.py +31 -5
- {istr_python-1.1.9 → istr_python-1.1.10}/istr/LICENSE.txt +0 -0
- {istr_python-1.1.9 → istr_python-1.1.10}/istr/__init__.py +0 -0
- {istr_python-1.1.9 → istr_python-1.1.10}/istr_python.egg-info/SOURCES.txt +0 -0
- {istr_python-1.1.9 → istr_python-1.1.10}/istr_python.egg-info/dependency_links.txt +0 -0
- {istr_python-1.1.9 → istr_python-1.1.10}/istr_python.egg-info/top_level.txt +0 -0
- {istr_python-1.1.9 → istr_python-1.1.10}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: istr-python
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.10
|
|
4
4
|
Summary: istr - strings you can count on
|
|
5
5
|
Author-email: Ruud van der Ham <rt.van.der.ham@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/salabim/istr
|
|
@@ -838,6 +838,9 @@ divmod x divmod(istr(20), 3) ==> (istr('6'), istr('2'))
|
|
|
838
838
|
** x istr(2) ** 3 ==> istr('8')
|
|
839
839
|
<=, <, >, >= x istr('100') > istr('2') ==> True
|
|
840
840
|
abs x abs(istr(-20)) ==> istr('20')
|
|
841
|
+
int x int(istr("20")) ==> 20
|
|
842
|
+
float x float(istr("20")) ==> 20.0
|
|
843
|
+
complex x complex(istr("20")) ==> (20+0j)
|
|
841
844
|
== x x istr(20) == 20 ==> True | istr(20) == '20' ==> True
|
|
842
845
|
bool x x *) bool(istr(' 0 ')) ==> False | bool(istr('')) ==> False
|
|
843
846
|
@ x istr(20) @ 3 ==> istr('202020')
|
|
@@ -825,6 +825,9 @@ divmod x divmod(istr(20), 3) ==> (istr('6'), istr('2'))
|
|
|
825
825
|
** x istr(2) ** 3 ==> istr('8')
|
|
826
826
|
<=, <, >, >= x istr('100') > istr('2') ==> True
|
|
827
827
|
abs x abs(istr(-20)) ==> istr('20')
|
|
828
|
+
int x int(istr("20")) ==> 20
|
|
829
|
+
float x float(istr("20")) ==> 20.0
|
|
830
|
+
complex x complex(istr("20")) ==> (20+0j)
|
|
828
831
|
== x x istr(20) == 20 ==> True | istr(20) == '20' ==> True
|
|
829
832
|
bool x x *) bool(istr(' 0 ')) ==> False | bool(istr('')) ==> False
|
|
830
833
|
@ x istr(20) @ 3 ==> istr('202020')
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# |_||___/ \__||_|
|
|
6
6
|
# strings you can count on
|
|
7
7
|
|
|
8
|
-
__version__ = "1.1.
|
|
8
|
+
__version__ = "1.1.10"
|
|
9
9
|
import functools
|
|
10
10
|
import itertools
|
|
11
11
|
import types
|
|
@@ -13,6 +13,7 @@ import sys
|
|
|
13
13
|
import inspect
|
|
14
14
|
import math
|
|
15
15
|
import operator
|
|
16
|
+
import copy
|
|
16
17
|
|
|
17
18
|
"""
|
|
18
19
|
Note: the changelog is now in changelog.md
|
|
@@ -30,7 +31,7 @@ class _range:
|
|
|
30
31
|
based on https://codereview.stackexchange.com/questions/229073/pure-python-range-implementation
|
|
31
32
|
"""
|
|
32
33
|
|
|
33
|
-
def __init__(self, cls, start, stop=None, step=1):
|
|
34
|
+
def __init__(self, cls, start, stop=None, step=1, base=None, int_format=None, repr_mode=None):
|
|
34
35
|
if stop is None:
|
|
35
36
|
start, stop = 0, start
|
|
36
37
|
self.start, self.stop, self.step = (int(obj) for obj in (start, stop, step))
|
|
@@ -42,6 +43,9 @@ class _range:
|
|
|
42
43
|
step_sign = 1
|
|
43
44
|
self._len = max(1 + (self.stop - self.start - step_sign) // self.step, 0)
|
|
44
45
|
self.parent_cls = cls
|
|
46
|
+
self.base = cls._base if base is None else base
|
|
47
|
+
self.int_format = cls._int_format if int_format is None else int_format
|
|
48
|
+
self.repr_mode = cls._repr_mode if repr_mode is None else repr_mode
|
|
45
49
|
self.init_done = True
|
|
46
50
|
|
|
47
51
|
def __setattr__(self, name, value):
|
|
@@ -100,13 +104,20 @@ class _range:
|
|
|
100
104
|
|
|
101
105
|
if isinstance(index, slice):
|
|
102
106
|
start, stop, step = adjust_indices(self._len, index.start, index.stop, index.step)
|
|
103
|
-
return self.parent_cls.range(
|
|
107
|
+
return self.parent_cls.range(
|
|
108
|
+
self.start + self.step * start,
|
|
109
|
+
self.start + self.step * stop,
|
|
110
|
+
self.step * step,
|
|
111
|
+
base=self.base,
|
|
112
|
+
int_format=self.int_format,
|
|
113
|
+
repr_mode=self.repr_mode,
|
|
114
|
+
)
|
|
104
115
|
index = int(index)
|
|
105
116
|
if index < 0:
|
|
106
117
|
index += self._len
|
|
107
118
|
if not 0 <= index < self._len:
|
|
108
119
|
raise IndexError("range object index out of range")
|
|
109
|
-
return self.parent_cls(self.start + self.step * index)
|
|
120
|
+
return self.parent_cls(self.start + self.step * index, base=self.base, int_format=self.int_format, repr_mode=self.repr_mode)
|
|
110
121
|
|
|
111
122
|
def __hash__(self):
|
|
112
123
|
if self._len == 0:
|
|
@@ -117,11 +128,11 @@ class _range:
|
|
|
117
128
|
value = self.start
|
|
118
129
|
if self.step > 0:
|
|
119
130
|
while value < self.stop:
|
|
120
|
-
yield self.parent_cls(value)
|
|
131
|
+
yield self.parent_cls(value, base=self.base, int_format=self.int_format, repr_mode=self.repr_mode)
|
|
121
132
|
value += self.step
|
|
122
133
|
else:
|
|
123
134
|
while value > self.stop:
|
|
124
|
-
yield self.parent_cls(value)
|
|
135
|
+
yield self.parent_cls(value, base=self.base, int_format=self.int_format, repr_mode=self.repr_mode)
|
|
125
136
|
value += self.step
|
|
126
137
|
|
|
127
138
|
def __len__(self):
|
|
@@ -207,7 +218,7 @@ class istr(str):
|
|
|
207
218
|
a, b, c = istr(5, 6, 7) ==> a=istr('5') , b=istr('6'), c=istr('7')
|
|
208
219
|
"""
|
|
209
220
|
|
|
210
|
-
__slots__ = ("_as_int", "_as_repr")
|
|
221
|
+
__slots__ = ("_as_int", "_as_repr","_this_base", "_this_int_format", "_this_repr_mode")
|
|
211
222
|
|
|
212
223
|
_int_format = ""
|
|
213
224
|
_repr_mode = "istr"
|
|
@@ -226,61 +237,70 @@ class istr(str):
|
|
|
226
237
|
return result[::-1] or "0"
|
|
227
238
|
|
|
228
239
|
@classmethod
|
|
229
|
-
def _to_int(cls, value):
|
|
240
|
+
def _to_int(cls, value, base=10):
|
|
230
241
|
try:
|
|
231
|
-
if
|
|
232
|
-
return int(value,
|
|
242
|
+
if base != 10 and isinstance(value, str):
|
|
243
|
+
return int(value, base)
|
|
233
244
|
else:
|
|
234
245
|
return int(value)
|
|
235
246
|
except Exception:
|
|
236
247
|
return cls._nan
|
|
237
248
|
|
|
238
|
-
def __new__(cls, *value, namespace=None):
|
|
249
|
+
def __new__(cls, *value, namespace=None, base=None, int_format=None, repr_mode=None):
|
|
239
250
|
if namespace is None:
|
|
240
251
|
try:
|
|
241
252
|
namespace = inspect.currentframe().f_back.f_back.f_globals
|
|
242
253
|
except AttributeError:
|
|
243
254
|
namespace = inspect.currentframe().f_back.f_globals # only used when running istr itself
|
|
255
|
+
base = cls._base if base is None else base
|
|
256
|
+
int_format = cls._int_format if int_format is None else int_format
|
|
257
|
+
repr_mode = cls._repr_mode if repr_mode is None else repr_mode
|
|
244
258
|
if len(value) == 0:
|
|
245
259
|
raise TypeError("no parameter given")
|
|
246
260
|
if len(value) == 1:
|
|
247
261
|
value = value[0] # normal case of 1 parameter
|
|
248
262
|
if isinstance(value, range):
|
|
249
|
-
return cls.range(value.start, value.stop, value.step)
|
|
263
|
+
return cls.range(value.start, value.stop, value.step, base=base, int_format=int_format, repr_mode=repr_mode)
|
|
250
264
|
if isinstance(value, _range):
|
|
251
265
|
return value
|
|
252
266
|
if isinstance(value, cls):
|
|
253
|
-
|
|
267
|
+
if value.is_int():
|
|
268
|
+
return cls(value._as_int, base=base, int_format=int_format, repr_mode=repr_mode)
|
|
269
|
+
else:
|
|
270
|
+
return copy.copy(value)
|
|
254
271
|
if isinstance(value, dict):
|
|
255
|
-
return type(value)((k, cls(v, namespace=namespace)) for k, v in value.items())
|
|
272
|
+
return type(value)((k, cls(v, base=base, int_format=int_format, repr_mode=repr_mode, namespace=namespace)) for k, v in value.items())
|
|
256
273
|
if not isinstance(value, (str, type)) and hasattr(value, "__iter__"):
|
|
257
274
|
if hasattr(value, "__next__"):
|
|
258
|
-
return map(lambda v: cls(v, namespace=namespace), value)
|
|
259
|
-
return type(value)(map(lambda v: cls(v, namespace=namespace), value))
|
|
275
|
+
return map(lambda v: cls(v, base=base, int_format=int_format, repr_mode=repr_mode, namespace=namespace), value)
|
|
276
|
+
return type(value)(map(lambda v: cls(v, base=base, int_format=int_format, repr_mode=repr_mode, namespace=namespace), value))
|
|
260
277
|
if isinstance(value, str) and value.startswith("=") and value != "=":
|
|
261
278
|
value = str(cls.compose(value[1:], namespace=namespace))
|
|
262
|
-
as_int = cls._to_int(value)
|
|
279
|
+
as_int = cls._to_int(value, base)
|
|
263
280
|
if isinstance(value, str):
|
|
264
281
|
as_str = value
|
|
265
282
|
else:
|
|
266
283
|
if as_int is cls._nan:
|
|
267
284
|
raise TypeError(f"incorrect value for {cls.__name__}: {repr(value)}")
|
|
268
|
-
if
|
|
269
|
-
if
|
|
285
|
+
if int_format == "" or base != 10:
|
|
286
|
+
if base == 10:
|
|
270
287
|
as_str = str(as_int)
|
|
271
288
|
else:
|
|
272
|
-
as_str = istr._to_base(as_int,
|
|
289
|
+
as_str = istr._to_base(as_int, base)
|
|
273
290
|
else:
|
|
274
|
-
as_str = f"{as_int:{
|
|
291
|
+
as_str = f"{as_int:{int_format}}"
|
|
275
292
|
|
|
276
293
|
self = super().__new__(cls, as_str)
|
|
277
294
|
self._as_int = as_int
|
|
278
|
-
if
|
|
295
|
+
if repr_mode == "istr":
|
|
279
296
|
self._as_repr = f"{cls.__name__}({repr(as_str)})"
|
|
280
|
-
elif
|
|
297
|
+
elif repr_mode == "int":
|
|
281
298
|
self._as_repr = "?" if as_int is self._nan else repr(as_int)
|
|
282
299
|
else:
|
|
283
300
|
self._as_repr = repr(as_str)
|
|
301
|
+
self._this_base=base
|
|
302
|
+
self._this_int_format=int_format
|
|
303
|
+
self._this_repr_mode=repr_mode
|
|
284
304
|
return self
|
|
285
305
|
|
|
286
306
|
def __iter__(self):
|
|
@@ -353,9 +373,19 @@ class istr(str):
|
|
|
353
373
|
|
|
354
374
|
def __int__(self):
|
|
355
375
|
if not self.is_int():
|
|
356
|
-
raise ValueError(f"invalid literal for int()
|
|
376
|
+
raise ValueError(f"invalid literal for int(): {self._frepr(self)}")
|
|
357
377
|
return int(self._as_int)
|
|
358
378
|
|
|
379
|
+
def __float__(self):
|
|
380
|
+
if not self.is_int():
|
|
381
|
+
raise ValueError(f"invalid literal for float(): {self._frepr(self)}")
|
|
382
|
+
return float(self._as_int)
|
|
383
|
+
|
|
384
|
+
def __complex__(self):
|
|
385
|
+
if not self.is_int():
|
|
386
|
+
raise ValueError(f"invalid literal for complex(): {self._frepr(self)}")
|
|
387
|
+
return complex(self._as_int)
|
|
388
|
+
|
|
359
389
|
def is_even(self):
|
|
360
390
|
return istr.is_divisible_by(self, 2)
|
|
361
391
|
|
|
@@ -404,13 +434,12 @@ class istr(str):
|
|
|
404
434
|
namespace = inspect.currentframe().f_back.f_globals
|
|
405
435
|
|
|
406
436
|
lookup = {}
|
|
407
|
-
|
|
408
437
|
for letter, ch in zip(letters, self):
|
|
409
438
|
if letter in lookup and lookup[letter] != ch:
|
|
410
439
|
raise ValueError(f"multiple values found for variable {letter}")
|
|
411
440
|
if not letter.isidentifier():
|
|
412
441
|
raise ValueError(f"{letter} cannot be used as a variable")
|
|
413
|
-
lookup[letter] = ch
|
|
442
|
+
lookup[str(letter)] = ch
|
|
414
443
|
if len(letters) != len(self):
|
|
415
444
|
raise ValueError(f"incorrect number of variables {len(letters)}; should be {len(self)}")
|
|
416
445
|
namespace.update(lookup)
|
|
@@ -425,8 +454,8 @@ class istr(str):
|
|
|
425
454
|
for letter in letters:
|
|
426
455
|
if letter not in namespace:
|
|
427
456
|
raise ValueError(f"variable {letter} not defined")
|
|
428
|
-
|
|
429
|
-
return istr(
|
|
457
|
+
s = "".join(str(namespace[letter]) for letter in letters)
|
|
458
|
+
return istr(s)
|
|
430
459
|
|
|
431
460
|
def __or__(self, other):
|
|
432
461
|
try:
|
|
@@ -510,7 +539,16 @@ class istr(str):
|
|
|
510
539
|
def enumerate(cls, iterable, start=0):
|
|
511
540
|
for i, value in enumerate(iterable, start):
|
|
512
541
|
yield cls(i), value
|
|
542
|
+
|
|
543
|
+
def this_base(self):
|
|
544
|
+
return self._this_base
|
|
513
545
|
|
|
546
|
+
def this_int_format(self):
|
|
547
|
+
return self._this_int_format
|
|
548
|
+
|
|
549
|
+
def this_repr_mode(self):
|
|
550
|
+
return self._this_repr_mode
|
|
551
|
+
|
|
514
552
|
@classmethod
|
|
515
553
|
class int_format:
|
|
516
554
|
def __new__(cls, cls_int_format, int_format=None):
|
|
@@ -525,7 +563,6 @@ class istr(str):
|
|
|
525
563
|
raise ValueError(f"{repr(int_format)} is incorrect int_format")
|
|
526
564
|
|
|
527
565
|
cls._int_format = int_format
|
|
528
|
-
|
|
529
566
|
def __enter__(self):
|
|
530
567
|
...
|
|
531
568
|
|
|
@@ -539,7 +576,7 @@ class istr(str):
|
|
|
539
576
|
return cls_repr_mode._repr_mode
|
|
540
577
|
if mode is int:
|
|
541
578
|
mode = "int"
|
|
542
|
-
if mode in ("istr", "str", "int"): #
|
|
579
|
+
if mode in ("istr", "str", "int"): # istr is used only for TypeErrors
|
|
543
580
|
return super().__new__(cls)
|
|
544
581
|
raise TypeError(f"mode not 'istr', 'str' or 'int', but {repr(mode)}")
|
|
545
582
|
|
|
@@ -575,8 +612,8 @@ class istr(str):
|
|
|
575
612
|
self.saved_cls._base = self.saved_base
|
|
576
613
|
|
|
577
614
|
@classmethod
|
|
578
|
-
def range(cls, start, stop=None, step=1):
|
|
579
|
-
return _range(cls, start, stop, step)
|
|
615
|
+
def range(cls, start, stop=None, step=1, base=None, int_format=None, repr_mode=None):
|
|
616
|
+
return _range(cls, start, stop, step, base=base, int_format=int_format, repr_mode=repr_mode)
|
|
580
617
|
|
|
581
618
|
@classmethod
|
|
582
619
|
def digits(cls, *args):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: istr-python
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.10
|
|
4
4
|
Summary: istr - strings you can count on
|
|
5
5
|
Author-email: Ruud van der Ham <rt.van.der.ham@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/salabim/istr
|
|
@@ -838,6 +838,9 @@ divmod x divmod(istr(20), 3) ==> (istr('6'), istr('2'))
|
|
|
838
838
|
** x istr(2) ** 3 ==> istr('8')
|
|
839
839
|
<=, <, >, >= x istr('100') > istr('2') ==> True
|
|
840
840
|
abs x abs(istr(-20)) ==> istr('20')
|
|
841
|
+
int x int(istr("20")) ==> 20
|
|
842
|
+
float x float(istr("20")) ==> 20.0
|
|
843
|
+
complex x complex(istr("20")) ==> (20+0j)
|
|
841
844
|
== x x istr(20) == 20 ==> True | istr(20) == '20' ==> True
|
|
842
845
|
bool x x *) bool(istr(' 0 ')) ==> False | bool(istr('')) ==> False
|
|
843
846
|
@ x istr(20) @ 3 ==> istr('202020')
|
|
@@ -5,9 +5,10 @@ import sys
|
|
|
5
5
|
import re
|
|
6
6
|
|
|
7
7
|
if __name__ == "__main__": # to make the tests run without the pytest cli
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
import os, sys # three lines to use the local package and chdir
|
|
9
|
+
|
|
10
|
+
os.chdir(os.path.dirname(__file__))
|
|
11
|
+
sys.path.insert(0, os.path.dirname(__file__) + "/../" + os.path.dirname(__file__).split(os.sep)[-2])
|
|
11
12
|
|
|
12
13
|
import pytest
|
|
13
14
|
|
|
@@ -186,6 +187,8 @@ def test_range():
|
|
|
186
187
|
|
|
187
188
|
with pytest.raises(IndexError):
|
|
188
189
|
one_to_twelve[12]
|
|
190
|
+
|
|
191
|
+
assert str(list(istr.range(5, base=2, repr_mode='str'))) == "['0', '1', '10', '11', '100']"
|
|
189
192
|
|
|
190
193
|
|
|
191
194
|
def test_misc():
|
|
@@ -305,9 +308,12 @@ def test_range_int_format():
|
|
|
305
308
|
r = istr.range(11)
|
|
306
309
|
assert repr(r) == "istr.range(0, 11)"
|
|
307
310
|
assert " ".join(r) == "0 1 2 3 4 5 6 7 8 9 10"
|
|
308
|
-
|
|
311
|
+
|
|
309
312
|
with istr.int_format("02"):
|
|
313
|
+
r = istr.range(11)
|
|
310
314
|
assert " ".join(r) == "00 01 02 03 04 05 06 07 08 09 10"
|
|
315
|
+
r = istr.range(11, int_format="03")
|
|
316
|
+
assert " ".join(r) == "000 001 002 003 004 005 006 007 008 009 010"
|
|
311
317
|
|
|
312
318
|
|
|
313
319
|
def test_even_odd():
|
|
@@ -665,7 +671,27 @@ def test_base():
|
|
|
665
671
|
with istr.base(10):
|
|
666
672
|
assert a * a == 225
|
|
667
673
|
|
|
668
|
-
|
|
674
|
+
a = istr(255)
|
|
675
|
+
x = istr("?")
|
|
676
|
+
with istr.base(16):
|
|
677
|
+
assert istr(a) == "FF"
|
|
678
|
+
assert istr(a, base=36) == "73"
|
|
679
|
+
assert istr(x) == "?"
|
|
680
|
+
assert istr(x, base=36) == "?"
|
|
681
|
+
|
|
682
|
+
def test_this_queries():
|
|
683
|
+
a = istr(12, base=36, int_format="04", repr_mode="str")
|
|
684
|
+
assert repr(a)=="'C'"
|
|
685
|
+
assert a.this_base()==36
|
|
686
|
+
assert a.this_int_format()=='04'
|
|
687
|
+
assert a.this_repr_mode()=='str'
|
|
688
|
+
|
|
689
|
+
a = istr(12, int_format="04", repr_mode="str")
|
|
690
|
+
assert repr(a)=="'0012'"
|
|
691
|
+
assert a.this_base()==10
|
|
692
|
+
assert a.this_int_format()=='04'
|
|
693
|
+
assert a.this_repr_mode()=='str'
|
|
694
|
+
|
|
669
695
|
def test_digits():
|
|
670
696
|
assert istr.digits().equals(istr("0123456789"))
|
|
671
697
|
assert istr.digits("").equals(istr("0123456789"))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|