istr-python 1.1.9__tar.gz → 1.1.11__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: istr-python
3
- Version: 1.1.9
3
+ Version: 1.1.11
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
@@ -648,7 +648,14 @@ Now, `test` will be `istr(396)` .
648
648
  Composing can also be done by prefixing a string with '=', like:
649
649
 
650
650
  ```
651
- test = istr("=xyz")
651
+ test = istr("=xyz") # test will be assigned istr(396)
652
+ ```
653
+
654
+ Composing and assignment can be done by prefixing a string with ':=', like:
655
+
656
+ ```
657
+ if istr("=xyz") > 300:
658
+ print(f"{xyx=}"") # ==> will print xyz=396
652
659
  ```
653
660
 
654
661
  Usually, composing and decomposing use the globals namespace, but this can be overridden with the namespace parameter. See the test suite for details.
@@ -838,6 +845,9 @@ divmod x divmod(istr(20), 3) ==> (istr('6'), istr('2'))
838
845
  ** x istr(2) ** 3 ==> istr('8')
839
846
  <=, <, >, >= x istr('100') > istr('2') ==> True
840
847
  abs x abs(istr(-20)) ==> istr('20')
848
+ int x int(istr("20")) ==> 20
849
+ float x float(istr("20")) ==> 20.0
850
+ complex x complex(istr("20")) ==> (20+0j)
841
851
  == x x istr(20) == 20 ==> True | istr(20) == '20' ==> True
842
852
  bool x x *) bool(istr(' 0 ')) ==> False | bool(istr('')) ==> False
843
853
  @ x istr(20) @ 3 ==> istr('202020')
@@ -635,7 +635,14 @@ Now, `test` will be `istr(396)` .
635
635
  Composing can also be done by prefixing a string with '=', like:
636
636
 
637
637
  ```
638
- test = istr("=xyz")
638
+ test = istr("=xyz") # test will be assigned istr(396)
639
+ ```
640
+
641
+ Composing and assignment can be done by prefixing a string with ':=', like:
642
+
643
+ ```
644
+ if istr("=xyz") > 300:
645
+ print(f"{xyx=}"") # ==> will print xyz=396
639
646
  ```
640
647
 
641
648
  Usually, composing and decomposing use the globals namespace, but this can be overridden with the namespace parameter. See the test suite for details.
@@ -825,6 +832,9 @@ divmod x divmod(istr(20), 3) ==> (istr('6'), istr('2'))
825
832
  ** x istr(2) ** 3 ==> istr('8')
826
833
  <=, <, >, >= x istr('100') > istr('2') ==> True
827
834
  abs x abs(istr(-20)) ==> istr('20')
835
+ int x int(istr("20")) ==> 20
836
+ float x float(istr("20")) ==> 20.0
837
+ complex x complex(istr("20")) ==> (20+0j)
828
838
  == x x istr(20) == 20 ==> True | istr(20) == '20' ==> True
829
839
  bool x x *) bool(istr(' 0 ')) ==> False | bool(istr('')) ==> False
830
840
  @ 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.9"
8
+ __version__ = "1.1.11"
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(self.start + self.step * start, self.start + self.step * stop, self.step * step)
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,74 @@ 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 cls._base != 10 and isinstance(value, str):
232
- return int(value, cls._base)
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
- return value
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
+ if isinstance(value, str) and value.startswith(":=") and value != ";=":
280
+ var_name=value[2:]
281
+ value = str(cls.compose(value[2:], namespace=namespace))
282
+ namespace[var_name]=cls(value)
283
+ as_int = cls._to_int(value, base)
263
284
  if isinstance(value, str):
264
285
  as_str = value
265
286
  else:
266
287
  if as_int is cls._nan:
267
288
  raise TypeError(f"incorrect value for {cls.__name__}: {repr(value)}")
268
- if cls._int_format == "" or cls._base != 10:
269
- if cls._base == 10:
289
+ if int_format == "" or base != 10:
290
+ if base == 10:
270
291
  as_str = str(as_int)
271
292
  else:
272
- as_str = istr._to_base(as_int, cls._base)
293
+ as_str = istr._to_base(as_int, base)
273
294
  else:
274
- as_str = f"{as_int:{cls._int_format}}"
295
+ as_str = f"{as_int:{int_format}}"
275
296
 
276
297
  self = super().__new__(cls, as_str)
277
298
  self._as_int = as_int
278
- if self._repr_mode == "istr":
299
+ if repr_mode == "istr":
279
300
  self._as_repr = f"{cls.__name__}({repr(as_str)})"
280
- elif self._repr_mode == "int":
301
+ elif repr_mode == "int":
281
302
  self._as_repr = "?" if as_int is self._nan else repr(as_int)
282
303
  else:
283
304
  self._as_repr = repr(as_str)
305
+ self._this_base=base
306
+ self._this_int_format=int_format
307
+ self._this_repr_mode=repr_mode
284
308
  return self
285
309
 
286
310
  def __iter__(self):
@@ -353,9 +377,19 @@ class istr(str):
353
377
 
354
378
  def __int__(self):
355
379
  if not self.is_int():
356
- raise ValueError(f"invalid literal for int() with base 10: {self._frepr(self)}")
380
+ raise ValueError(f"invalid literal for int(): {self._frepr(self)}")
357
381
  return int(self._as_int)
358
382
 
383
+ def __float__(self):
384
+ if not self.is_int():
385
+ raise ValueError(f"invalid literal for float(): {self._frepr(self)}")
386
+ return float(self._as_int)
387
+
388
+ def __complex__(self):
389
+ if not self.is_int():
390
+ raise ValueError(f"invalid literal for complex(): {self._frepr(self)}")
391
+ return complex(self._as_int)
392
+
359
393
  def is_even(self):
360
394
  return istr.is_divisible_by(self, 2)
361
395
 
@@ -404,13 +438,12 @@ class istr(str):
404
438
  namespace = inspect.currentframe().f_back.f_globals
405
439
 
406
440
  lookup = {}
407
-
408
441
  for letter, ch in zip(letters, self):
409
442
  if letter in lookup and lookup[letter] != ch:
410
443
  raise ValueError(f"multiple values found for variable {letter}")
411
444
  if not letter.isidentifier():
412
445
  raise ValueError(f"{letter} cannot be used as a variable")
413
- lookup[letter] = ch
446
+ lookup[str(letter)] = ch
414
447
  if len(letters) != len(self):
415
448
  raise ValueError(f"incorrect number of variables {len(letters)}; should be {len(self)}")
416
449
  namespace.update(lookup)
@@ -425,8 +458,8 @@ class istr(str):
425
458
  for letter in letters:
426
459
  if letter not in namespace:
427
460
  raise ValueError(f"variable {letter} not defined")
428
-
429
- return istr("", namespace=None).join(str(namespace[letter]) for letter in letters)
461
+ s = "".join(str(namespace[letter]) for letter in letters)
462
+ return istr(s)
430
463
 
431
464
  def __or__(self, other):
432
465
  try:
@@ -510,7 +543,16 @@ class istr(str):
510
543
  def enumerate(cls, iterable, start=0):
511
544
  for i, value in enumerate(iterable, start):
512
545
  yield cls(i), value
546
+
547
+ def this_base(self):
548
+ return self._this_base
513
549
 
550
+ def this_int_format(self):
551
+ return self._this_int_format
552
+
553
+ def this_repr_mode(self):
554
+ return self._this_repr_mode
555
+
514
556
  @classmethod
515
557
  class int_format:
516
558
  def __new__(cls, cls_int_format, int_format=None):
@@ -525,7 +567,6 @@ class istr(str):
525
567
  raise ValueError(f"{repr(int_format)} is incorrect int_format")
526
568
 
527
569
  cls._int_format = int_format
528
-
529
570
  def __enter__(self):
530
571
  ...
531
572
 
@@ -539,7 +580,7 @@ class istr(str):
539
580
  return cls_repr_mode._repr_mode
540
581
  if mode is int:
541
582
  mode = "int"
542
- if mode in ("istr", "str", "int"): # _istr is used only for TypeErrors
583
+ if mode in ("istr", "str", "int"): # istr is used only for TypeErrors
543
584
  return super().__new__(cls)
544
585
  raise TypeError(f"mode not 'istr', 'str' or 'int', but {repr(mode)}")
545
586
 
@@ -575,8 +616,8 @@ class istr(str):
575
616
  self.saved_cls._base = self.saved_base
576
617
 
577
618
  @classmethod
578
- def range(cls, start, stop=None, step=1):
579
- return _range(cls, start, stop, step)
619
+ def range(cls, start, stop=None, step=1, base=None, int_format=None, repr_mode=None):
620
+ return _range(cls, start, stop, step, base=base, int_format=int_format, repr_mode=repr_mode)
580
621
 
581
622
  @classmethod
582
623
  def digits(cls, *args):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: istr-python
3
- Version: 1.1.9
3
+ Version: 1.1.11
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
@@ -648,7 +648,14 @@ Now, `test` will be `istr(396)` .
648
648
  Composing can also be done by prefixing a string with '=', like:
649
649
 
650
650
  ```
651
- test = istr("=xyz")
651
+ test = istr("=xyz") # test will be assigned istr(396)
652
+ ```
653
+
654
+ Composing and assignment can be done by prefixing a string with ':=', like:
655
+
656
+ ```
657
+ if istr("=xyz") > 300:
658
+ print(f"{xyx=}"") # ==> will print xyz=396
652
659
  ```
653
660
 
654
661
  Usually, composing and decomposing use the globals namespace, but this can be overridden with the namespace parameter. See the test suite for details.
@@ -838,6 +845,9 @@ divmod x divmod(istr(20), 3) ==> (istr('6'), istr('2'))
838
845
  ** x istr(2) ** 3 ==> istr('8')
839
846
  <=, <, >, >= x istr('100') > istr('2') ==> True
840
847
  abs x abs(istr(-20)) ==> istr('20')
848
+ int x int(istr("20")) ==> 20
849
+ float x float(istr("20")) ==> 20.0
850
+ complex x complex(istr("20")) ==> (20+0j)
841
851
  == x x istr(20) == 20 ==> True | istr(20) == '20' ==> True
842
852
  bool x x *) bool(istr(' 0 ')) ==> False | bool(istr('')) ==> False
843
853
  @ x istr(20) @ 3 ==> istr('202020')
@@ -10,7 +10,7 @@ authors = [
10
10
  { name = "Ruud van der Ham", email = "rt.van.der.ham@gmail.com" },
11
11
  ]
12
12
  description = "istr - strings you can count on"
13
- version = "1.1.9"
13
+ version = "1.1.11"
14
14
  readme = "README.md"
15
15
  requires-python = ">=3.8"
16
16
  dependencies = []
@@ -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
- file_folder = os.path.dirname(__file__)
9
- os.chdir(file_folder)
10
- sys.path.insert(0, file_folder + "/../istr")
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
- r = istr.range(11)
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"))
@@ -790,7 +816,6 @@ def test_decompose():
790
816
 
791
817
 
792
818
  def test_compose():
793
- global x, y, z
794
819
  x = 1
795
820
  y = "2"
796
821
  z = istr(3)
@@ -803,14 +828,15 @@ def test_compose():
803
828
  assert istr("=") == "="
804
829
 
805
830
  assert istr(["=xyz", "=y"]) == [istr(123), istr(2)]
806
- assert istr(("=xyz", "=y")) == (istr(123), istr(2))
807
- assert istr({"=xyz", "=y"}) == {istr(123), istr(2)}
808
-
831
+
809
832
  assert istr(dict(xyz="=xyz", y="=y")) == {"xyz": istr(123), "y": istr(2)}
810
833
  assert istr(dict(xyz="=xyz", y="=y"), namespace=dict(x=3, y=4, z="z")) == {"xyz": istr("34z"), "y": istr(4)}
811
834
 
812
835
  assert istr(istr("=xyz")) == istr(123)
813
836
 
837
+ assert istr(":=xyz").equals(istr(123))
838
+ assert xyz.equals(istr(123))
839
+
814
840
 
815
841
  if __name__ == "__main__":
816
842
  pytest.main(["-vv", "-s", "-x", __file__])
File without changes