jk_typing 0.2024.1.3__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.
- jk_typing-0.2024.1.3/PKG-INFO +95 -0
- jk_typing-0.2024.1.3/README.md +81 -0
- jk_typing-0.2024.1.3/jk_typing/__init__.py +11 -0
- jk_typing-0.2024.1.3/jk_typing/checkFunctionSignature.py +387 -0
- jk_typing-0.2024.1.3/jk_typing/checking/AbstractCTNode.py +55 -0
- jk_typing-0.2024.1.3/jk_typing/checking/CTAlwaysTrue.py +61 -0
- jk_typing-0.2024.1.3/jk_typing/checking/CTIsCallable.py +66 -0
- jk_typing-0.2024.1.3/jk_typing/checking/CTIsDict.py +81 -0
- jk_typing-0.2024.1.3/jk_typing/checking/CTIsNone.py +61 -0
- jk_typing-0.2024.1.3/jk_typing/checking/CTIsSequence.py +75 -0
- jk_typing-0.2024.1.3/jk_typing/checking/CTIsType.py +104 -0
- jk_typing-0.2024.1.3/jk_typing/checking/CTIsType.py.sav.OLD +118 -0
- jk_typing-0.2024.1.3/jk_typing/checking/CTIsType__CheckItems_AllTheSameType.py +80 -0
- jk_typing-0.2024.1.3/jk_typing/checking/CTIsType__CheckItems_ExactTypeSequence.py +98 -0
- jk_typing-0.2024.1.3/jk_typing/checking/CTIsType__Single_NewType.py +102 -0
- jk_typing-0.2024.1.3/jk_typing/checking/CTIsType__Single_None.py +79 -0
- jk_typing-0.2024.1.3/jk_typing/checking/CTIsType__Union.py +77 -0
- jk_typing-0.2024.1.3/jk_typing/checking/CheckTypeCompiler.py +389 -0
- jk_typing-0.2024.1.3/jk_typing/checking/__init__.py +11 -0
- jk_typing-0.2024.1.3/pyproject.toml +52 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: jk_typing
|
|
3
|
+
Version: 0.2024.1.3
|
|
4
|
+
Summary: This module provides capabilities for type checking function arguments. (NOTE: A full type check with this module is not possible, since this is fundamentally not possible due to limitiations of Python itself.)
|
|
5
|
+
Keywords: typing,type-checking
|
|
6
|
+
Author-email: Jürgen Knauth <pubsrc@binary-overflow.de>
|
|
7
|
+
Maintainer-email: Jürgen Knauth <pubsrc@binary-overflow.de>
|
|
8
|
+
Requires-Python: >=3.8
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
|
|
14
|
+
jk_typing
|
|
15
|
+
==========
|
|
16
|
+
|
|
17
|
+
Introduction
|
|
18
|
+
------------
|
|
19
|
+
|
|
20
|
+
This module provides capabilities for type checking function arguments.
|
|
21
|
+
|
|
22
|
+
Please note that a full type check with this module is not possible, since this is fundamentally not possible due to limitiations of Python itself.
|
|
23
|
+
|
|
24
|
+
Information about this module can be found here:
|
|
25
|
+
|
|
26
|
+
* [github.org](https://github.com/jkpubsrc/python-module-jk-typing)
|
|
27
|
+
* [Documentation](https://github.com/jkpubsrc/python-module-jk-typing/tree/master/documentation)
|
|
28
|
+
* [pypi.python.org](https://pypi.python.org/pypi/jk_typing)
|
|
29
|
+
|
|
30
|
+
Why this module?
|
|
31
|
+
----------------
|
|
32
|
+
|
|
33
|
+
Python nowadays is capable of managing type hints for function arguments and return values. Unfortunately Python does not perform type checking automatically. This module provides capabilities of performing type checking automatically.
|
|
34
|
+
|
|
35
|
+
For more details have a look at the [documentation](https://github.com/jkpubsrc/python-module-jk-typing/tree/master/documentation).
|
|
36
|
+
|
|
37
|
+
Requirements
|
|
38
|
+
------------
|
|
39
|
+
|
|
40
|
+
This module requires Python 3.7 or later. It can not be used on older releases of Python because of limitations of module `typing`.
|
|
41
|
+
|
|
42
|
+
Limitations of this module
|
|
43
|
+
--------------------------
|
|
44
|
+
|
|
45
|
+
Type checking is not implemented in pure Python. Therefore it is slower as if the Python interpreter would perform type checking itself. Additionally this module has not yet a complete implementation of all kinds of types. (This will change in the future, maybe with your help.)
|
|
46
|
+
|
|
47
|
+
For more details have a look at the [documentation](https://github.com/jkpubsrc/python-module-jk-typing/tree/master/documentation).
|
|
48
|
+
|
|
49
|
+
How to use this module
|
|
50
|
+
----------------------
|
|
51
|
+
|
|
52
|
+
### Import this module
|
|
53
|
+
|
|
54
|
+
Please include this module into your application using the following code:
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
import jk_typing
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Define a function
|
|
61
|
+
|
|
62
|
+
Example:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
@checkFunctionSignature(bDebug = True)
|
|
66
|
+
def someFunction(a:int, b:str) -> bool:
|
|
67
|
+
return str(a) == b
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Invoke the function (and perform type checking)
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
result = someFunction(123, "123")
|
|
74
|
+
print(result)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Contact Information
|
|
78
|
+
-------------------
|
|
79
|
+
|
|
80
|
+
This work is Open Source. This enables you to use this work for free.
|
|
81
|
+
|
|
82
|
+
Please have in mind this also enables you to contribute. We, the subspecies of software developers, can create great things. But the more collaborate, the more fantastic these things can become. Therefore Feel free to contact the author(s) listed below, either for giving feedback, providing comments, hints, indicate possible collaborations, ideas, improvements. Or maybe for "only" reporting some bugs:
|
|
83
|
+
|
|
84
|
+
* Jürgen Knauth: jknauth@uni-goettingen.de, pubsrc@binary-overflow.de
|
|
85
|
+
|
|
86
|
+
License
|
|
87
|
+
-------
|
|
88
|
+
|
|
89
|
+
This software is provided under the following license:
|
|
90
|
+
|
|
91
|
+
* Apache Software License 2.0
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
jk_typing
|
|
2
|
+
==========
|
|
3
|
+
|
|
4
|
+
Introduction
|
|
5
|
+
------------
|
|
6
|
+
|
|
7
|
+
This module provides capabilities for type checking function arguments.
|
|
8
|
+
|
|
9
|
+
Please note that a full type check with this module is not possible, since this is fundamentally not possible due to limitiations of Python itself.
|
|
10
|
+
|
|
11
|
+
Information about this module can be found here:
|
|
12
|
+
|
|
13
|
+
* [github.org](https://github.com/jkpubsrc/python-module-jk-typing)
|
|
14
|
+
* [Documentation](https://github.com/jkpubsrc/python-module-jk-typing/tree/master/documentation)
|
|
15
|
+
* [pypi.python.org](https://pypi.python.org/pypi/jk_typing)
|
|
16
|
+
|
|
17
|
+
Why this module?
|
|
18
|
+
----------------
|
|
19
|
+
|
|
20
|
+
Python nowadays is capable of managing type hints for function arguments and return values. Unfortunately Python does not perform type checking automatically. This module provides capabilities of performing type checking automatically.
|
|
21
|
+
|
|
22
|
+
For more details have a look at the [documentation](https://github.com/jkpubsrc/python-module-jk-typing/tree/master/documentation).
|
|
23
|
+
|
|
24
|
+
Requirements
|
|
25
|
+
------------
|
|
26
|
+
|
|
27
|
+
This module requires Python 3.7 or later. It can not be used on older releases of Python because of limitations of module `typing`.
|
|
28
|
+
|
|
29
|
+
Limitations of this module
|
|
30
|
+
--------------------------
|
|
31
|
+
|
|
32
|
+
Type checking is not implemented in pure Python. Therefore it is slower as if the Python interpreter would perform type checking itself. Additionally this module has not yet a complete implementation of all kinds of types. (This will change in the future, maybe with your help.)
|
|
33
|
+
|
|
34
|
+
For more details have a look at the [documentation](https://github.com/jkpubsrc/python-module-jk-typing/tree/master/documentation).
|
|
35
|
+
|
|
36
|
+
How to use this module
|
|
37
|
+
----------------------
|
|
38
|
+
|
|
39
|
+
### Import this module
|
|
40
|
+
|
|
41
|
+
Please include this module into your application using the following code:
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
import jk_typing
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Define a function
|
|
48
|
+
|
|
49
|
+
Example:
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
@checkFunctionSignature(bDebug = True)
|
|
53
|
+
def someFunction(a:int, b:str) -> bool:
|
|
54
|
+
return str(a) == b
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Invoke the function (and perform type checking)
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
result = someFunction(123, "123")
|
|
61
|
+
print(result)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Contact Information
|
|
65
|
+
-------------------
|
|
66
|
+
|
|
67
|
+
This work is Open Source. This enables you to use this work for free.
|
|
68
|
+
|
|
69
|
+
Please have in mind this also enables you to contribute. We, the subspecies of software developers, can create great things. But the more collaborate, the more fantastic these things can become. Therefore Feel free to contact the author(s) listed below, either for giving feedback, providing comments, hints, indicate possible collaborations, ideas, improvements. Or maybe for "only" reporting some bugs:
|
|
70
|
+
|
|
71
|
+
* Jürgen Knauth: jknauth@uni-goettingen.de, pubsrc@binary-overflow.de
|
|
72
|
+
|
|
73
|
+
License
|
|
74
|
+
-------
|
|
75
|
+
|
|
76
|
+
This software is provided under the following license:
|
|
77
|
+
|
|
78
|
+
* Apache Software License 2.0
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
import collections
|
|
4
|
+
import sys
|
|
5
|
+
import typing
|
|
6
|
+
import inspect
|
|
7
|
+
|
|
8
|
+
from .checking.AbstractCTNode import AbstractCTNode
|
|
9
|
+
from .checking.CheckTypeCompiler import CheckTypeCompiler
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
def __checkUnion(value, typeSpecs:list):
|
|
17
|
+
for typeSpec in typeSpecs:
|
|
18
|
+
if _checkType(value, typeSpec):
|
|
19
|
+
return True
|
|
20
|
+
return False
|
|
21
|
+
#
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
#_type_checking_enabled = True
|
|
27
|
+
|
|
28
|
+
if (sys.version_info.major < 3) or (
|
|
29
|
+
(sys.version_info.major == 3) and (sys.version_info.minor <= 6)):
|
|
30
|
+
# older python implementation are not yet mature enough.
|
|
31
|
+
raise Exception("Python versions below 3.6 are not supported!")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
"""
|
|
41
|
+
def deactiveTypeChecking():
|
|
42
|
+
global _type_checking_enabled
|
|
43
|
+
_type_checking_enabled = False
|
|
44
|
+
#
|
|
45
|
+
|
|
46
|
+
def isTypeCheckingEnabled() -> bool:
|
|
47
|
+
global _type_checking_enabled
|
|
48
|
+
return _type_checking_enabled
|
|
49
|
+
#
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
#
|
|
60
|
+
# Do the type check.
|
|
61
|
+
# This function is the heart of the type checking implementation.
|
|
62
|
+
#
|
|
63
|
+
# @param any value The value to check
|
|
64
|
+
# @param various typeSpec The type specification such as `None`, `int`, `typing.List[int]`, etc.
|
|
65
|
+
# @param int indent The current indentation level to use for debugging ouptut. A negative indent disables output.
|
|
66
|
+
#
|
|
67
|
+
def _checkType(value, typeSpec, indent:int):
|
|
68
|
+
if indent >= 0:
|
|
69
|
+
print("\t"*indent + ">> checking value " + repr(value) + " against: " + repr(typeSpec))
|
|
70
|
+
|
|
71
|
+
if typeSpec is None:
|
|
72
|
+
return value is None
|
|
73
|
+
|
|
74
|
+
elif isinstance(typeSpec, typing._GenericAlias):
|
|
75
|
+
if indent >= 0:
|
|
76
|
+
print("\t"*indent + ">> (the condition type is generc (_name={}, __origin__={}))".format(
|
|
77
|
+
repr(typeSpec._name),
|
|
78
|
+
repr(typeSpec.__origin__)
|
|
79
|
+
))
|
|
80
|
+
|
|
81
|
+
if typeSpec._name == "List":
|
|
82
|
+
if not isinstance(value, list):
|
|
83
|
+
return False
|
|
84
|
+
for v in value:
|
|
85
|
+
if not _checkType(v, typeSpec.__args__[0], indent+1):
|
|
86
|
+
return False
|
|
87
|
+
return True
|
|
88
|
+
|
|
89
|
+
elif typeSpec._name == "Tuple":
|
|
90
|
+
if not isinstance(value, tuple):
|
|
91
|
+
return False
|
|
92
|
+
for v in value:
|
|
93
|
+
if not _checkType(v, typeSpec.__args__[0], indent+1):
|
|
94
|
+
return False
|
|
95
|
+
return True
|
|
96
|
+
|
|
97
|
+
elif typeSpec._name == "Set":
|
|
98
|
+
if not isinstance(value, set):
|
|
99
|
+
return False
|
|
100
|
+
for v in value:
|
|
101
|
+
if not _checkType(v, typeSpec.__args__[0], indent+1):
|
|
102
|
+
return False
|
|
103
|
+
return True
|
|
104
|
+
|
|
105
|
+
elif typeSpec._name == "FrozenSet":
|
|
106
|
+
if not isinstance(value, frozenset):
|
|
107
|
+
return False
|
|
108
|
+
for v in value:
|
|
109
|
+
if not _checkType(v, typeSpec.__args__[0], indent+1):
|
|
110
|
+
return False
|
|
111
|
+
return True
|
|
112
|
+
|
|
113
|
+
elif typeSpec._name == "Deque":
|
|
114
|
+
if not isinstance(value, collections.deque):
|
|
115
|
+
return False
|
|
116
|
+
for v in value:
|
|
117
|
+
if not _checkType(v, typeSpec.__args__[0], indent+1):
|
|
118
|
+
return False
|
|
119
|
+
return True
|
|
120
|
+
|
|
121
|
+
elif typeSpec._name == "Dict":
|
|
122
|
+
if not isinstance(value, dict):
|
|
123
|
+
return False
|
|
124
|
+
for k, v in value.items():
|
|
125
|
+
if not _checkType(k, typeSpec.__args__[0], indent+1):
|
|
126
|
+
return False
|
|
127
|
+
if not _checkType(v, typeSpec.__args__[1], indent+1):
|
|
128
|
+
return False
|
|
129
|
+
return True
|
|
130
|
+
|
|
131
|
+
elif typeSpec.__origin__ == typing.Union:
|
|
132
|
+
for t in typeSpec.__args__:
|
|
133
|
+
if _checkType(value, t, indent+1):
|
|
134
|
+
return True
|
|
135
|
+
return False
|
|
136
|
+
|
|
137
|
+
else:
|
|
138
|
+
if indent >= 0:
|
|
139
|
+
print("\t"*indent + "WARN: Can't check this type: " + repr(typeSpec))
|
|
140
|
+
return True
|
|
141
|
+
|
|
142
|
+
else:
|
|
143
|
+
if indent >= 0:
|
|
144
|
+
print("\t"*indent + ">> (the condition type is primitive)")
|
|
145
|
+
return isinstance(value, typeSpec)
|
|
146
|
+
#
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _getTypeDescr(t) -> str:
|
|
151
|
+
if t is None:
|
|
152
|
+
return "void"
|
|
153
|
+
|
|
154
|
+
s = repr(t)
|
|
155
|
+
# s should be something like this:
|
|
156
|
+
# "<Parameter "abc">
|
|
157
|
+
# "<Parameter "abc:int">
|
|
158
|
+
# "<Parameter "*args">
|
|
159
|
+
if s.startswith("<Parameter \"*"):
|
|
160
|
+
s = s[13:-2]
|
|
161
|
+
elif s.startswith("<Parameter \""):
|
|
162
|
+
s = s[12:-2]
|
|
163
|
+
elif s.startswith("<class '"):
|
|
164
|
+
s = s[8:-2]
|
|
165
|
+
else:
|
|
166
|
+
return "(unknown:" + s + ")"
|
|
167
|
+
|
|
168
|
+
pos = s.find(":")
|
|
169
|
+
if pos > 0:
|
|
170
|
+
return s[pos+1:].strip()
|
|
171
|
+
else:
|
|
172
|
+
return "?"
|
|
173
|
+
#
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _debug_dumpObj(prefix:str, someObj, bSkipUnderscores:bool = True, names:list = None):
|
|
177
|
+
#print(prefix + "| " + repr(someObj))
|
|
178
|
+
if someObj is None:
|
|
179
|
+
return
|
|
180
|
+
if names is None:
|
|
181
|
+
names = dir(someObj)
|
|
182
|
+
for key in names:
|
|
183
|
+
if bSkipUnderscores and key.startswith("_"):
|
|
184
|
+
continue
|
|
185
|
+
print(prefix + "|\t" + key + ": " + str(getattr(someObj, key)))
|
|
186
|
+
#
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
#
|
|
190
|
+
# this is the annotation wrapper that receives arguments and returns the function that does the wrapping
|
|
191
|
+
#
|
|
192
|
+
# @param bool bDebug (optional) Enable debugging output at evaluation time.
|
|
193
|
+
# @param int|bool bDebugComp (optional) Enable debugging output at compile time.
|
|
194
|
+
# @param str logDescend (optional) Log descend message.
|
|
195
|
+
# @param jk_logging.EnumLoglevel logLevel (optional) Modify the standard descend log level.
|
|
196
|
+
# @param bool bLogWithhold (optional) Only log if a minimum log level of warning occurs.
|
|
197
|
+
#
|
|
198
|
+
def checkFunctionSignature(bDebug:bool = False, bDebugComp:typing.Union[bool,int] = False, logDescend:str = None, logLevel = None, bLogWithhold:bool = False):
|
|
199
|
+
assert isinstance(bDebug, bool)
|
|
200
|
+
assert isinstance(bDebugComp, (bool, int))
|
|
201
|
+
assert isinstance(bLogWithhold, bool)
|
|
202
|
+
|
|
203
|
+
# ----
|
|
204
|
+
|
|
205
|
+
# this function is executed for every function definition
|
|
206
|
+
def _wrap_the_function(fn):
|
|
207
|
+
annotations = typing.get_type_hints(fn) # receives a normal dictionary
|
|
208
|
+
_signature = inspect.signature(fn)
|
|
209
|
+
_signature_parameters = _signature.parameters # receives an ordered dictionary
|
|
210
|
+
_signature_return_annotation = _signature.return_annotation # receives a type annotation structure
|
|
211
|
+
|
|
212
|
+
if bDebugComp:
|
|
213
|
+
print("@@>> wrapping function " + fn.__qualname__ + "(..) with checkFunctionSignature() ...")
|
|
214
|
+
print("\t@@>> Annotations:")
|
|
215
|
+
for ak, av in annotations.items():
|
|
216
|
+
print("\t\t@@>> Annotation for " + repr(ak) + ": " + str(av))
|
|
217
|
+
print("\t@@>> Signature:")
|
|
218
|
+
for sk, sv in _signature_parameters.items():
|
|
219
|
+
print("\t\t@@>> Signature parameter for " + repr(sk) + ": " + repr(sv))
|
|
220
|
+
_debug_dumpObj("\t\t\t", sv, names = ["annotation", "default", "kind"])
|
|
221
|
+
# parameters are:
|
|
222
|
+
# str sv.name -- the name of the argument
|
|
223
|
+
# any sv.annotation -- the type annotation structure associated with this argument (or inspect._empty if not set)
|
|
224
|
+
# obj sv.default -- the defautl value (or inspect._empty if not set)
|
|
225
|
+
print("\t@@>> Return annotation: " + str(_signature_return_annotation))
|
|
226
|
+
|
|
227
|
+
# variables to fill during compile phase
|
|
228
|
+
_paramCheckers = {}
|
|
229
|
+
_returnChecker = None
|
|
230
|
+
|
|
231
|
+
# compile
|
|
232
|
+
|
|
233
|
+
outWarnList = [] # NOTE: we reuse this object for performance reasons
|
|
234
|
+
for k, t in _signature._parameters.items():
|
|
235
|
+
assert isinstance(t, inspect.Parameter)
|
|
236
|
+
c = CheckTypeCompiler.compile(k, _getTypeDescr(t), t.annotation, t.default, outWarnList, bDebugComp)
|
|
237
|
+
if bDebugComp:
|
|
238
|
+
if c is not None:
|
|
239
|
+
print("\t@@>> Signature parameter compilation for " + repr(k) + ":")
|
|
240
|
+
c.dump("\t\t|\t")
|
|
241
|
+
if bDebug:
|
|
242
|
+
if outWarnList:
|
|
243
|
+
for entry in outWarnList:
|
|
244
|
+
print("WARNING: " + fn.__qualname__ + "(), param " + repr(t.name) + " : " + entry)
|
|
245
|
+
outWarnList.clear()
|
|
246
|
+
if c is not None:
|
|
247
|
+
_paramCheckers[k] = c
|
|
248
|
+
#print("paramCheckers[" + k + "] =", c)
|
|
249
|
+
|
|
250
|
+
if _signature._return_annotation != inspect._empty:
|
|
251
|
+
outWarnList = []
|
|
252
|
+
_returnChecker = CheckTypeCompiler.compile(
|
|
253
|
+
None, # argName
|
|
254
|
+
_getTypeDescr(_signature._return_annotation), # sType
|
|
255
|
+
_signature._return_annotation, # typeSpec
|
|
256
|
+
inspect._empty, # defaultValue
|
|
257
|
+
outWarnList, # outWarnList
|
|
258
|
+
bDebugComp # nEnableDebugging
|
|
259
|
+
)
|
|
260
|
+
if bDebugComp:
|
|
261
|
+
if _returnChecker is not None:
|
|
262
|
+
print("\t@@>> Signature parameter compilation for returned values:")
|
|
263
|
+
_returnChecker.dump("\t\t|\t")
|
|
264
|
+
#print("returnChecker =", repr(returnChecker.sType))
|
|
265
|
+
_bIsMethod = "self" in _paramCheckers
|
|
266
|
+
|
|
267
|
+
# ----
|
|
268
|
+
|
|
269
|
+
_logDescend = None
|
|
270
|
+
if logDescend:
|
|
271
|
+
_pcLog = _paramCheckers["log"]
|
|
272
|
+
if _pcLog:
|
|
273
|
+
#_tmp = _pcLog.sType.split(".")
|
|
274
|
+
#if (_tmp[0] == "jk_logging") and (_tmp[-1].endswith("Logger")):
|
|
275
|
+
# _logDescend = logDescend
|
|
276
|
+
_logDescend = logDescend
|
|
277
|
+
if bDebug and (_logDescend is None):
|
|
278
|
+
print("WARNING: " + fn.__qualname__ + "() has no suitable parameter 'log' for log descent!")
|
|
279
|
+
if bDebug:
|
|
280
|
+
print("@@>> Using log descend.")
|
|
281
|
+
else:
|
|
282
|
+
if bDebug:
|
|
283
|
+
print("@@>> Not using log descend.")
|
|
284
|
+
|
|
285
|
+
# ----
|
|
286
|
+
|
|
287
|
+
def _wrapper(*args, **kwargs):
|
|
288
|
+
# bind arguments
|
|
289
|
+
|
|
290
|
+
boundedArgs = _signature.bind(*args, **kwargs)
|
|
291
|
+
#_pLog = _signature.parameters.get("log")
|
|
292
|
+
#print("@@", _pLog)
|
|
293
|
+
#print(dir(_pLog))
|
|
294
|
+
|
|
295
|
+
#print("\t>> " + pprint.pformat(boundedArgs))
|
|
296
|
+
|
|
297
|
+
# ----------------------------------------------------------------
|
|
298
|
+
# check arguments. delay raising of errors to print all error messages before raising an exception.
|
|
299
|
+
|
|
300
|
+
if bDebug:
|
|
301
|
+
print("@@>> Invoking function/method: " + fn.__qualname__ + "(..)")
|
|
302
|
+
|
|
303
|
+
err = None
|
|
304
|
+
for k, v in boundedArgs.arguments.items():
|
|
305
|
+
# k is the argument name
|
|
306
|
+
# v is the argument value
|
|
307
|
+
c = _paramCheckers.get(k)
|
|
308
|
+
if c:
|
|
309
|
+
if not c.__call__(v):
|
|
310
|
+
print("\targument " + repr(k) + ": " + c.sType + " => ✖")
|
|
311
|
+
if not err:
|
|
312
|
+
err = ValueError("Argument " + repr(k) + " for " + fn.__name__ + "() is of type '" + repr(type(v)) + "' which does not match '" + c.sType + "' as expected!")
|
|
313
|
+
else:
|
|
314
|
+
print("\targument " + repr(k) + ": " + c.sType + " => ✔")
|
|
315
|
+
else:
|
|
316
|
+
print("\targument " + repr(k) + ": no constraint specified")
|
|
317
|
+
if err:
|
|
318
|
+
raise err
|
|
319
|
+
|
|
320
|
+
else:
|
|
321
|
+
for k, v in boundedArgs.arguments.items():
|
|
322
|
+
# k is the argument name
|
|
323
|
+
# v is the argument value
|
|
324
|
+
c = _paramCheckers.get(k)
|
|
325
|
+
if c:
|
|
326
|
+
if not c.__call__(v):
|
|
327
|
+
raise ValueError("Argument " + repr(k) + " for " + fn.__name__ + "() is of type '" + repr(type(v)) + "' which does not match '" + c.sType + "' as expected!")
|
|
328
|
+
|
|
329
|
+
# ----------------------------------------------------------------
|
|
330
|
+
# invoke the function
|
|
331
|
+
|
|
332
|
+
if _logDescend:
|
|
333
|
+
_baLogger = boundedArgs.arguments.get("log")
|
|
334
|
+
if _baLogger is not None:
|
|
335
|
+
# a 'log' argument is a) expected and b) has been specified; this is the normal situation => descent
|
|
336
|
+
|
|
337
|
+
_baVerbose = boundedArgs.arguments.get("bVerbose")
|
|
338
|
+
bWithholdVerbose = _baVerbose if isinstance(_baVerbose, bool) else False
|
|
339
|
+
|
|
340
|
+
with _baLogger.descend(_logDescend.format(**boundedArgs.arguments), logLevel=logLevel, bWithhold=bLogWithhold, bWithholdVerbose=bWithholdVerbose) as log2:
|
|
341
|
+
boundedArgs.arguments["log"] = log2
|
|
342
|
+
ret = fn(*boundedArgs.args, **boundedArgs.kwargs)
|
|
343
|
+
|
|
344
|
+
else:
|
|
345
|
+
# a 'log' argument is a) expected and b) but has been specified; this will now result in an exception raise (= programming error by the caller)
|
|
346
|
+
ret = fn(*args, **kwargs)
|
|
347
|
+
else:
|
|
348
|
+
# no 'log' argument is expected => no descent
|
|
349
|
+
ret = fn(*args, **kwargs)
|
|
350
|
+
|
|
351
|
+
# ----------------------------------------------------------------
|
|
352
|
+
# check the return value.
|
|
353
|
+
|
|
354
|
+
if bDebug:
|
|
355
|
+
if _returnChecker:
|
|
356
|
+
if not _returnChecker.__call__(ret):
|
|
357
|
+
print("\treturn value: " + _returnChecker.sType + " => ✖")
|
|
358
|
+
raise ValueError("Return value for " + fn.__name__ + "() is of type '" + repr(type(ret)) + "' which does not match '" + _returnChecker.sType + "' as expected!")
|
|
359
|
+
else:
|
|
360
|
+
print("\treturn value: " + _returnChecker.sType + " => ✔")
|
|
361
|
+
else:
|
|
362
|
+
print("\treturn value: no constraint specified")
|
|
363
|
+
|
|
364
|
+
else:
|
|
365
|
+
if _returnChecker:
|
|
366
|
+
if not _returnChecker.__call__(ret):
|
|
367
|
+
raise ValueError("Return value for " + fn.__name__ + "() is of type '" + repr(type(ret)) + "' which does not match '" + _returnChecker.sType + "' as expected!")
|
|
368
|
+
|
|
369
|
+
# ----------------------------------------------------------------
|
|
370
|
+
|
|
371
|
+
return ret
|
|
372
|
+
#
|
|
373
|
+
|
|
374
|
+
_wrapper.orgName = fn.__name__
|
|
375
|
+
_wrapper.orgQualName = fn.__qualname__
|
|
376
|
+
_wrapper.orgSignature = _signature
|
|
377
|
+
return _wrapper
|
|
378
|
+
#return _FunctionWrapper(fn, signature, paramCheckers, returnChecker, bDebug)
|
|
379
|
+
#
|
|
380
|
+
|
|
381
|
+
return _wrap_the_function
|
|
382
|
+
#
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
import inspect
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AbstractCTNode(object):
|
|
11
|
+
|
|
12
|
+
################################################################################################################################
|
|
13
|
+
## Constructor
|
|
14
|
+
################################################################################################################################
|
|
15
|
+
|
|
16
|
+
################################################################################################################################
|
|
17
|
+
## Public Properties
|
|
18
|
+
################################################################################################################################
|
|
19
|
+
|
|
20
|
+
################################################################################################################################
|
|
21
|
+
## Helper Methods
|
|
22
|
+
################################################################################################################################
|
|
23
|
+
|
|
24
|
+
def _printCodeLocation(self, codeFileName:str, bValue:bool):
|
|
25
|
+
lineNo = inspect.currentframe().f_back.f_lineno
|
|
26
|
+
print(f">> Returns **{bValue}** here: {codeFileName}:{lineNo}")
|
|
27
|
+
#
|
|
28
|
+
|
|
29
|
+
################################################################################################################################
|
|
30
|
+
## Public Methods
|
|
31
|
+
################################################################################################################################
|
|
32
|
+
|
|
33
|
+
def __call__(self, value) -> bool:
|
|
34
|
+
raise NotImplementedError()
|
|
35
|
+
#
|
|
36
|
+
|
|
37
|
+
def dump(self, prefix:str = None, printFunc:typing.Callable = None):
|
|
38
|
+
if prefix is None:
|
|
39
|
+
prefix = ""
|
|
40
|
+
if printFunc is None:
|
|
41
|
+
printFunc = print
|
|
42
|
+
|
|
43
|
+
self._dump(prefix, printFunc)
|
|
44
|
+
#
|
|
45
|
+
|
|
46
|
+
def _dump(self, prefix:str, printFunc:typing.Callable):
|
|
47
|
+
raise NotImplementedError()
|
|
48
|
+
#
|
|
49
|
+
|
|
50
|
+
#
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
from .AbstractCTNode import AbstractCTNode
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CTAlwaysTrue(AbstractCTNode):
|
|
12
|
+
|
|
13
|
+
################################################################################################################################
|
|
14
|
+
## Constructor
|
|
15
|
+
################################################################################################################################
|
|
16
|
+
|
|
17
|
+
#
|
|
18
|
+
# Constructor method.
|
|
19
|
+
#
|
|
20
|
+
def __init__(self, argName:typing.Union[str,None], sType:str, nDebug:typing.Union[bool,int]):
|
|
21
|
+
if argName is not None:
|
|
22
|
+
assert isinstance(argName, str)
|
|
23
|
+
assert isinstance(sType, str)
|
|
24
|
+
assert isinstance(nDebug, (bool, int))
|
|
25
|
+
|
|
26
|
+
# ----
|
|
27
|
+
|
|
28
|
+
self.argName = argName
|
|
29
|
+
self.sType = sType
|
|
30
|
+
self.__nDebug = nDebug
|
|
31
|
+
#
|
|
32
|
+
|
|
33
|
+
################################################################################################################################
|
|
34
|
+
## Public Properties
|
|
35
|
+
################################################################################################################################
|
|
36
|
+
|
|
37
|
+
################################################################################################################################
|
|
38
|
+
## Helper Methods
|
|
39
|
+
################################################################################################################################
|
|
40
|
+
|
|
41
|
+
################################################################################################################################
|
|
42
|
+
## Public Methods
|
|
43
|
+
################################################################################################################################
|
|
44
|
+
|
|
45
|
+
def __call__(self, value) -> bool:
|
|
46
|
+
return True
|
|
47
|
+
#
|
|
48
|
+
|
|
49
|
+
def _dump(self, prefix:str, printFunc:typing.Callable):
|
|
50
|
+
printFunc(prefix + "CTAlwaysTrue<( argName=" + repr(self.argName) + ", sType=" + repr(self.sType))
|
|
51
|
+
printFunc(prefix + ")>")
|
|
52
|
+
#
|
|
53
|
+
|
|
54
|
+
#
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|