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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: istr-python
3
- Version: 1.1.9
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.9"
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(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,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 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
+ 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 cls._int_format == "" or cls._base != 10:
269
- if cls._base == 10:
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, cls._base)
289
+ as_str = istr._to_base(as_int, base)
273
290
  else:
274
- as_str = f"{as_int:{cls._int_format}}"
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 self._repr_mode == "istr":
295
+ if repr_mode == "istr":
279
296
  self._as_repr = f"{cls.__name__}({repr(as_str)})"
280
- elif self._repr_mode == "int":
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() with base 10: {self._frepr(self)}")
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("", namespace=None).join(str(namespace[letter]) for letter in letters)
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"): # _istr is used only for TypeErrors
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.9
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')
@@ -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.10"
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"))
File without changes