istr-python 1.1.43__tar.gz → 1.1.44__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.43 → istr_python-1.1.44}/PKG-INFO +36 -1
- {istr_python-1.1.43 → istr_python-1.1.44}/README.md +35 -0
- {istr_python-1.1.43 → istr_python-1.1.44}/istr/istr.py +77 -11
- {istr_python-1.1.43 → istr_python-1.1.44}/istr_python.egg-info/PKG-INFO +36 -1
- {istr_python-1.1.43 → istr_python-1.1.44}/pyproject.toml +1 -1
- {istr_python-1.1.43 → istr_python-1.1.44}/istr/LICENSE.txt +0 -0
- {istr_python-1.1.43 → istr_python-1.1.44}/istr/__init__.py +0 -0
- {istr_python-1.1.43 → istr_python-1.1.44}/istr_python.egg-info/SOURCES.txt +0 -0
- {istr_python-1.1.43 → istr_python-1.1.44}/istr_python.egg-info/dependency_links.txt +0 -0
- {istr_python-1.1.43 → istr_python-1.1.44}/istr_python.egg-info/top_level.txt +0 -0
- {istr_python-1.1.43 → istr_python-1.1.44}/setup.cfg +0 -0
- {istr_python-1.1.43 → istr_python-1.1.44}/tests/test_istr.py +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.44
|
|
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
|
|
@@ -1041,6 +1041,41 @@ Note that `str(istr(":="))` is ":=" and does not assign any value.
|
|
|
1041
1041
|
|
|
1042
1042
|
Usually, composing and decomposing uses the globals namespace, but this can be overridden with the namespace parameter. See the test suite for details.
|
|
1043
1043
|
|
|
1044
|
+
#### Evaluating expressions with one letter variables
|
|
1045
|
+
The `istr.eval` method is essentially the same as `istr.compose`, but (safely) evaluates the result.
|
|
1046
|
+
E.g.
|
|
1047
|
+
|
|
1048
|
+
```
|
|
1049
|
+
a = 5
|
|
1050
|
+
b = 2
|
|
1051
|
+
print(istr.eval('aab + ba')
|
|
1052
|
+
```
|
|
1053
|
+
will print `577`
|
|
1054
|
+
|
|
1055
|
+
The operators +, -, * and / are supported.
|
|
1056
|
+
Comparisons (==, <=, <, >, >= are also supported. In that case, the result is also istr-ed, so istr('0') for False and istr('1') for True. E.g.
|
|
1057
|
+
|
|
1058
|
+
```
|
|
1059
|
+
a = 5
|
|
1060
|
+
b = 2
|
|
1061
|
+
print(istr.eval('a > b')
|
|
1062
|
+
```
|
|
1063
|
+
will print 1 (meaning True)
|
|
1064
|
+
|
|
1065
|
+
Note that leading zeroes are accepted. So, `istr.eval('03 + 7')` is `10`.
|
|
1066
|
+
Instead of ==, = can be used. So, `istr.eval('12 = 5 + 7')` is `1` (meaning True)
|
|
1067
|
+
|
|
1068
|
+
##### Alternative method
|
|
1069
|
+
|
|
1070
|
+
If istr is called with a string starting with a `?`, the string without the leading `?` will be evaluated using the new `istr.eval` method. So, the above examples read:
|
|
1071
|
+
```
|
|
1072
|
+
a = 5
|
|
1073
|
+
b = 2
|
|
1074
|
+
print(istr("?aab + ba':")
|
|
1075
|
+
print(istr("?a > b")
|
|
1076
|
+
```
|
|
1077
|
+
with output `577` and `1` (meaning True)
|
|
1078
|
+
|
|
1044
1079
|
#### Subclassing istr
|
|
1045
1080
|
|
|
1046
1081
|
When a class is derived from istr, all methods will return that newly derived class.
|
|
@@ -1028,6 +1028,41 @@ Note that `str(istr(":="))` is ":=" and does not assign any value.
|
|
|
1028
1028
|
|
|
1029
1029
|
Usually, composing and decomposing uses the globals namespace, but this can be overridden with the namespace parameter. See the test suite for details.
|
|
1030
1030
|
|
|
1031
|
+
#### Evaluating expressions with one letter variables
|
|
1032
|
+
The `istr.eval` method is essentially the same as `istr.compose`, but (safely) evaluates the result.
|
|
1033
|
+
E.g.
|
|
1034
|
+
|
|
1035
|
+
```
|
|
1036
|
+
a = 5
|
|
1037
|
+
b = 2
|
|
1038
|
+
print(istr.eval('aab + ba')
|
|
1039
|
+
```
|
|
1040
|
+
will print `577`
|
|
1041
|
+
|
|
1042
|
+
The operators +, -, * and / are supported.
|
|
1043
|
+
Comparisons (==, <=, <, >, >= are also supported. In that case, the result is also istr-ed, so istr('0') for False and istr('1') for True. E.g.
|
|
1044
|
+
|
|
1045
|
+
```
|
|
1046
|
+
a = 5
|
|
1047
|
+
b = 2
|
|
1048
|
+
print(istr.eval('a > b')
|
|
1049
|
+
```
|
|
1050
|
+
will print 1 (meaning True)
|
|
1051
|
+
|
|
1052
|
+
Note that leading zeroes are accepted. So, `istr.eval('03 + 7')` is `10`.
|
|
1053
|
+
Instead of ==, = can be used. So, `istr.eval('12 = 5 + 7')` is `1` (meaning True)
|
|
1054
|
+
|
|
1055
|
+
##### Alternative method
|
|
1056
|
+
|
|
1057
|
+
If istr is called with a string starting with a `?`, the string without the leading `?` will be evaluated using the new `istr.eval` method. So, the above examples read:
|
|
1058
|
+
```
|
|
1059
|
+
a = 5
|
|
1060
|
+
b = 2
|
|
1061
|
+
print(istr("?aab + ba':")
|
|
1062
|
+
print(istr("?a > b")
|
|
1063
|
+
```
|
|
1064
|
+
with output `577` and `1` (meaning True)
|
|
1065
|
+
|
|
1031
1066
|
#### Subclassing istr
|
|
1032
1067
|
|
|
1033
1068
|
When a class is derived from istr, all methods will return that newly derived class.
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# |_||___/ \__||_|
|
|
6
6
|
# strings you can count on
|
|
7
7
|
|
|
8
|
-
__version__ = "1.1.
|
|
8
|
+
__version__ = "1.1.44"
|
|
9
9
|
import functools
|
|
10
10
|
import itertools
|
|
11
11
|
import types
|
|
@@ -17,6 +17,8 @@ import copy
|
|
|
17
17
|
import bisect
|
|
18
18
|
import collections
|
|
19
19
|
import numbers
|
|
20
|
+
import re
|
|
21
|
+
import ast
|
|
20
22
|
|
|
21
23
|
"""
|
|
22
24
|
Note: the changelog is in changelog.md
|
|
@@ -282,15 +284,18 @@ class istr(str):
|
|
|
282
284
|
return map(lambda v: cls(v, base=base, int_format=int_format, repr_mode=repr_mode, namespace=get_namespace(namespace)), value)
|
|
283
285
|
return type(value)(map(lambda v: cls(v, base=base, int_format=int_format, repr_mode=repr_mode, namespace=get_namespace(namespace)), value))
|
|
284
286
|
|
|
285
|
-
if isinstance(value, str) and (any(value.startswith(s) and value != s for s in ("=", ":="))):
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
287
|
+
if isinstance(value, str) and (any(value.startswith(s) and value != s for s in ("=", ":=",'?'))):
|
|
288
|
+
match value[0]:
|
|
289
|
+
case '?':
|
|
290
|
+
value = str(cls.eval(value[1:], namespace=get_namespace(namespace)))
|
|
291
|
+
case '=':
|
|
292
|
+
value = str(cls.compose(value[1:], namespace=get_namespace(namespace)))
|
|
293
|
+
case ':':
|
|
294
|
+
var_name = value[2:]
|
|
295
|
+
if not var_name.isidentifier():
|
|
296
|
+
raise ValueError(f"{var_name!r} is not a valid identifier")
|
|
297
|
+
value = str(cls.compose(var_name, namespace=get_namespace(namespace)))
|
|
298
|
+
get_namespace(namespace)[var_name] = cls(value)
|
|
294
299
|
as_int = cls._to_int(value, base)
|
|
295
300
|
if as_int is cls._nan or isinstance(value, str):
|
|
296
301
|
as_str = value
|
|
@@ -674,7 +679,20 @@ class istr(str):
|
|
|
674
679
|
result.append(str(namespace[letter]))
|
|
675
680
|
else:
|
|
676
681
|
result.append(letter)
|
|
677
|
-
|
|
682
|
+
|
|
683
|
+
result_str = "".join(result)
|
|
684
|
+
return cls(result_str)
|
|
685
|
+
|
|
686
|
+
@classmethod
|
|
687
|
+
def eval(cls, letters, namespace=None):
|
|
688
|
+
"""
|
|
689
|
+
evaluates an istr from individual letter variables
|
|
690
|
+
"""
|
|
691
|
+
expr = cls.compose(letters, namespace=namespace)
|
|
692
|
+
expr = re.sub(r"\b0+(\d+)\b", r"\1", expr) # delete leading zeroes
|
|
693
|
+
expr=re.sub(r'(?<![<>=!])=(?!=)', '==', expr) # support =
|
|
694
|
+
result = safe_eval(expr)
|
|
695
|
+
return cls(result)
|
|
678
696
|
|
|
679
697
|
def __or__(self, other):
|
|
680
698
|
if isinstance(other, str):
|
|
@@ -1330,6 +1348,54 @@ def real_caller_frame():
|
|
|
1330
1348
|
return frame
|
|
1331
1349
|
|
|
1332
1350
|
|
|
1351
|
+
OPS = {ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul, ast.Div: operator.truediv, ast.Pow: operator.pow}
|
|
1352
|
+
|
|
1353
|
+
COMPARE_OPS = {
|
|
1354
|
+
ast.Eq: operator.eq, # ==
|
|
1355
|
+
ast.NotEq: operator.ne, # !=
|
|
1356
|
+
ast.Lt: operator.lt, # <
|
|
1357
|
+
ast.LtE: operator.le, # <=
|
|
1358
|
+
ast.Gt: operator.gt, # >
|
|
1359
|
+
ast.GtE: operator.ge, # >=
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
|
|
1363
|
+
def safe_eval(expr):
|
|
1364
|
+
def evaluate(node):
|
|
1365
|
+
if isinstance(node, ast.Constant) and isinstance(node.value, (int, float, str, bool)):
|
|
1366
|
+
return node.value
|
|
1367
|
+
|
|
1368
|
+
if isinstance(node, ast.BinOp) and type(node.op) in OPS:
|
|
1369
|
+
return OPS[type(node.op)](evaluate(node.left), evaluate(node.right))
|
|
1370
|
+
|
|
1371
|
+
if isinstance(node, ast.UnaryOp) and isinstance(node.op, (ast.UAdd, ast.USub)):
|
|
1372
|
+
value = evaluate(node.operand)
|
|
1373
|
+
return value if isinstance(node.op, ast.UAdd) else -value
|
|
1374
|
+
|
|
1375
|
+
if isinstance(node, ast.Compare):
|
|
1376
|
+
left = evaluate(node.left)
|
|
1377
|
+
|
|
1378
|
+
# Supports chained comparisons, e.g. 1 < 2 < 3
|
|
1379
|
+
for op, comparator in zip(node.ops, node.comparators):
|
|
1380
|
+
op_func = COMPARE_OPS.get(type(op))
|
|
1381
|
+
if op_func is None:
|
|
1382
|
+
raise ValueError("Unsupported comparison")
|
|
1383
|
+
|
|
1384
|
+
right = evaluate(comparator)
|
|
1385
|
+
|
|
1386
|
+
if not op_func(left, right):
|
|
1387
|
+
return False
|
|
1388
|
+
|
|
1389
|
+
left = right
|
|
1390
|
+
|
|
1391
|
+
return True
|
|
1392
|
+
|
|
1393
|
+
raise ValueError("Unsupported expression")
|
|
1394
|
+
|
|
1395
|
+
tree = ast.parse(expr, mode="eval")
|
|
1396
|
+
return evaluate(tree.body)
|
|
1397
|
+
|
|
1398
|
+
|
|
1333
1399
|
istr.type = type(istr(0))
|
|
1334
1400
|
|
|
1335
1401
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: istr-python
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.44
|
|
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
|
|
@@ -1041,6 +1041,41 @@ Note that `str(istr(":="))` is ":=" and does not assign any value.
|
|
|
1041
1041
|
|
|
1042
1042
|
Usually, composing and decomposing uses the globals namespace, but this can be overridden with the namespace parameter. See the test suite for details.
|
|
1043
1043
|
|
|
1044
|
+
#### Evaluating expressions with one letter variables
|
|
1045
|
+
The `istr.eval` method is essentially the same as `istr.compose`, but (safely) evaluates the result.
|
|
1046
|
+
E.g.
|
|
1047
|
+
|
|
1048
|
+
```
|
|
1049
|
+
a = 5
|
|
1050
|
+
b = 2
|
|
1051
|
+
print(istr.eval('aab + ba')
|
|
1052
|
+
```
|
|
1053
|
+
will print `577`
|
|
1054
|
+
|
|
1055
|
+
The operators +, -, * and / are supported.
|
|
1056
|
+
Comparisons (==, <=, <, >, >= are also supported. In that case, the result is also istr-ed, so istr('0') for False and istr('1') for True. E.g.
|
|
1057
|
+
|
|
1058
|
+
```
|
|
1059
|
+
a = 5
|
|
1060
|
+
b = 2
|
|
1061
|
+
print(istr.eval('a > b')
|
|
1062
|
+
```
|
|
1063
|
+
will print 1 (meaning True)
|
|
1064
|
+
|
|
1065
|
+
Note that leading zeroes are accepted. So, `istr.eval('03 + 7')` is `10`.
|
|
1066
|
+
Instead of ==, = can be used. So, `istr.eval('12 = 5 + 7')` is `1` (meaning True)
|
|
1067
|
+
|
|
1068
|
+
##### Alternative method
|
|
1069
|
+
|
|
1070
|
+
If istr is called with a string starting with a `?`, the string without the leading `?` will be evaluated using the new `istr.eval` method. So, the above examples read:
|
|
1071
|
+
```
|
|
1072
|
+
a = 5
|
|
1073
|
+
b = 2
|
|
1074
|
+
print(istr("?aab + ba':")
|
|
1075
|
+
print(istr("?a > b")
|
|
1076
|
+
```
|
|
1077
|
+
with output `577` and `1` (meaning True)
|
|
1078
|
+
|
|
1044
1079
|
#### Subclassing istr
|
|
1045
1080
|
|
|
1046
1081
|
When a class is derived from istr, all methods will return that newly derived class.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|