pUnit 1.2.23__tar.gz → 1.2.26__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.
- {punit-1.2.23/src/pUnit.egg-info → punit-1.2.26}/PKG-INFO +1 -1
- {punit-1.2.23 → punit-1.2.26}/pyproject.toml +1 -1
- {punit-1.2.23 → punit-1.2.26/src/pUnit.egg-info}/PKG-INFO +1 -1
- {punit-1.2.23 → punit-1.2.26}/src/pUnit.egg-info/SOURCES.txt +3 -0
- {punit-1.2.23 → punit-1.2.26}/src/punit/__init__.py +3 -2
- punit-1.2.26/src/punit/facts/Fact.py +55 -0
- {punit-1.2.23 → punit-1.2.26}/src/punit/facts/FactManager.py +7 -19
- punit-1.2.26/src/punit/metadata/CallableMetadata.py +38 -0
- punit-1.2.26/src/punit/metadata/__init__.py +9 -0
- {punit-1.2.23 → punit-1.2.26}/src/punit/runner.py +6 -4
- {punit-1.2.23 → punit-1.2.26}/src/punit/theories/Theory.py +11 -38
- {punit-1.2.23 → punit-1.2.26}/src/punit/theories/TheoryManager.py +6 -18
- {punit-1.2.23 → punit-1.2.26}/src/punit/traits/Trait.py +2 -4
- punit-1.2.26/src/punit/traits/TraitManager.py +39 -0
- {punit-1.2.23 → punit-1.2.26}/src/punit/traits/__init__.py +2 -0
- punit-1.2.23/src/punit/facts/Fact.py +0 -82
- {punit-1.2.23 → punit-1.2.26}/.scripts/punit +0 -0
- {punit-1.2.23 → punit-1.2.26}/LICENSE +0 -0
- {punit-1.2.23 → punit-1.2.26}/README.md +0 -0
- {punit-1.2.23 → punit-1.2.26}/setup.cfg +0 -0
- {punit-1.2.23 → punit-1.2.26}/src/pUnit.egg-info/dependency_links.txt +0 -0
- {punit-1.2.23 → punit-1.2.26}/src/pUnit.egg-info/requires.txt +0 -0
- {punit-1.2.23 → punit-1.2.26}/src/pUnit.egg-info/top_level.txt +0 -0
- {punit-1.2.23 → punit-1.2.26}/src/punit/TestResult.py +0 -0
- {punit-1.2.23 → punit-1.2.26}/src/punit/__main__.py +0 -0
- {punit-1.2.23 → punit-1.2.26}/src/punit/assertions/__init__.py +0 -0
- {punit-1.2.23 → punit-1.2.26}/src/punit/assertions/collections.py +0 -0
- {punit-1.2.23 → punit-1.2.26}/src/punit/assertions/exceptions.py +0 -0
- {punit-1.2.23 → punit-1.2.26}/src/punit/assertions/strings.py +0 -0
- {punit-1.2.23 → punit-1.2.26}/src/punit/cli.py +0 -0
- {punit-1.2.23 → punit-1.2.26}/src/punit/discovery/TestModuleDiscovery.py +0 -0
- {punit-1.2.23 → punit-1.2.26}/src/punit/discovery/__init__.py +0 -0
- {punit-1.2.23 → punit-1.2.26}/src/punit/facts/__init__.py +0 -0
- {punit-1.2.23 → punit-1.2.26}/src/punit/filters/Filter.py +0 -0
- {punit-1.2.23 → punit-1.2.26}/src/punit/filters/FilterManager.py +0 -0
- {punit-1.2.23 → punit-1.2.26}/src/punit/filters/__init__.py +0 -0
- {punit-1.2.23 → punit-1.2.26}/src/punit/py.typed +0 -0
- {punit-1.2.23 → punit-1.2.26}/src/punit/reports/HtmlReportGenerator.py +0 -0
- {punit-1.2.23 → punit-1.2.26}/src/punit/reports/JUnitReportGenerator.py +0 -0
- {punit-1.2.23 → punit-1.2.26}/src/punit/reports/__init__.py +0 -0
- {punit-1.2.23 → punit-1.2.26}/src/punit/theories/__init__.py +0 -0
|
@@ -25,6 +25,8 @@ src/punit/facts/__init__.py
|
|
|
25
25
|
src/punit/filters/Filter.py
|
|
26
26
|
src/punit/filters/FilterManager.py
|
|
27
27
|
src/punit/filters/__init__.py
|
|
28
|
+
src/punit/metadata/CallableMetadata.py
|
|
29
|
+
src/punit/metadata/__init__.py
|
|
28
30
|
src/punit/reports/HtmlReportGenerator.py
|
|
29
31
|
src/punit/reports/JUnitReportGenerator.py
|
|
30
32
|
src/punit/reports/__init__.py
|
|
@@ -32,4 +34,5 @@ src/punit/theories/Theory.py
|
|
|
32
34
|
src/punit/theories/TheoryManager.py
|
|
33
35
|
src/punit/theories/__init__.py
|
|
34
36
|
src/punit/traits/Trait.py
|
|
37
|
+
src/punit/traits/TraitManager.py
|
|
35
38
|
src/punit/traits/__init__.py
|
|
@@ -7,14 +7,15 @@ from .theories import *
|
|
|
7
7
|
from .traits import *
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
__version__ = '1.2.
|
|
11
|
-
__commit__ = '
|
|
10
|
+
__version__ = '1.2.26'
|
|
11
|
+
__commit__ = 'aeff39e'
|
|
12
12
|
__all__ = [
|
|
13
13
|
'__version__', '__commit__',
|
|
14
14
|
'assertions',
|
|
15
15
|
'collections', 'exceptions', 'strings',
|
|
16
16
|
'facts',
|
|
17
17
|
'fact',
|
|
18
|
+
'metadata',
|
|
18
19
|
'theories',
|
|
19
20
|
'theory', 'inlinedata',
|
|
20
21
|
'traits',
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: © 2024 Shaun Wilson
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
import inspect
|
|
5
|
+
from types import BuiltinFunctionType, BuiltinMethodType, FunctionType, MethodType, ModuleType
|
|
6
|
+
from typing import Callable, Coroutine, Union, cast
|
|
7
|
+
|
|
8
|
+
from ..metadata import CallableMetadata
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Fact:
|
|
12
|
+
|
|
13
|
+
__target:Union[FunctionType, MethodType, BuiltinFunctionType, BuiltinMethodType, Callable]
|
|
14
|
+
|
|
15
|
+
def __init__(self, target:Union[FunctionType, MethodType, BuiltinFunctionType, BuiltinMethodType, Callable]):
|
|
16
|
+
self.__metadata = CallableMetadata(target)
|
|
17
|
+
self.__target = target
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def metadata(self) -> CallableMetadata:
|
|
21
|
+
return self.__metadata
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def target(self) -> Union[FunctionType, MethodType, BuiltinFunctionType, BuiltinMethodType, Callable]:
|
|
25
|
+
return self.__target
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
async def execute(self, module:ModuleType) -> None:
|
|
29
|
+
coro:Coroutine|None = None
|
|
30
|
+
if hasattr(self.__target, '__qualname__') and self.__target.__qualname__.find('.') > -1:
|
|
31
|
+
if isinstance(self.__target, staticmethod):
|
|
32
|
+
coro = self.__target()
|
|
33
|
+
else:
|
|
34
|
+
qnparts = self.__target.__qualname__.split('.')
|
|
35
|
+
qntarget = module
|
|
36
|
+
for qnpart in qnparts[0:-1]:
|
|
37
|
+
qntarget = getattr(qntarget, qnpart)
|
|
38
|
+
if isinstance(self.__target, classmethod):
|
|
39
|
+
coro = self.__target.__func__(qntarget)
|
|
40
|
+
else:
|
|
41
|
+
# every test execution gets a new instance of class
|
|
42
|
+
coro = self.__target(cast(Callable,qntarget)())
|
|
43
|
+
else:
|
|
44
|
+
coro = self.__target()
|
|
45
|
+
if inspect.iscoroutine(coro):
|
|
46
|
+
await coro
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def fact(target:Callable) -> Callable:
|
|
50
|
+
from .FactManager import FactManager
|
|
51
|
+
if (not inspect.isfunction(target)) and (not isinstance(target, classmethod)) and (not isinstance(target, staticmethod)):
|
|
52
|
+
raise Exception('@fact can only be applied to functions and methods.')
|
|
53
|
+
fact:Fact = Fact(target)
|
|
54
|
+
FactManager.instance().put(fact)
|
|
55
|
+
return target
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
# SPDX-License-Identifier: MIT
|
|
3
3
|
##
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
from typing import Callable, Optional
|
|
5
|
+
from typing import Optional
|
|
7
6
|
|
|
8
7
|
from ..filters.FilterManager import FilterManager
|
|
9
8
|
from ..traits.Trait import Trait
|
|
9
|
+
from ..traits.TraitManager import TraitManager
|
|
10
10
|
from .Fact import Fact
|
|
11
11
|
|
|
12
12
|
|
|
@@ -16,13 +16,11 @@ class FactManager:
|
|
|
16
16
|
__instance:Optional['FactManager'] = None
|
|
17
17
|
__includeTraits:list[Trait]
|
|
18
18
|
__modules:dict[str, list[Fact]]
|
|
19
|
-
__traits:dict[Callable, list[Trait]]
|
|
20
19
|
|
|
21
20
|
def __init__(self) -> None:
|
|
22
21
|
if FactManager.__instance is not None:
|
|
23
22
|
raise Exception('Cannot create more than one instance of FactManager') # pragma: no cover
|
|
24
23
|
self.__modules = {}
|
|
25
|
-
self.__traits = {}
|
|
26
24
|
|
|
27
25
|
@staticmethod
|
|
28
26
|
def instance() -> 'FactManager':
|
|
@@ -47,14 +45,15 @@ class FactManager:
|
|
|
47
45
|
self.__includeTraits = value
|
|
48
46
|
|
|
49
47
|
def __excludeByTraits(self, fact:Fact) -> bool:
|
|
48
|
+
traits = TraitManager.instance().get(fact.target)
|
|
50
49
|
if self.excludeTraits is not None and len(self.excludeTraits) > 0:
|
|
51
50
|
for trait in self.excludeTraits:
|
|
52
|
-
for L_trait in
|
|
51
|
+
for L_trait in traits:
|
|
53
52
|
if trait.name == L_trait.name and (trait.value is None or (trait.value == L_trait.value)):
|
|
54
53
|
return True
|
|
55
54
|
if self.includeTraits is not None and len(self.includeTraits) > 0:
|
|
56
55
|
for trait in self.includeTraits:
|
|
57
|
-
for L_trait in
|
|
56
|
+
for L_trait in traits:
|
|
58
57
|
if trait.name == L_trait.name and (trait.value is None or (trait.value == L_trait.value)):
|
|
59
58
|
return False
|
|
60
59
|
return True
|
|
@@ -71,21 +70,10 @@ class FactManager:
|
|
|
71
70
|
filters = FilterManager.instance().filters
|
|
72
71
|
matches_filter:bool = False
|
|
73
72
|
for filter in filters:
|
|
74
|
-
if filter.re.fullmatch(fact.filterName) is not None:
|
|
73
|
+
if filter.re.fullmatch(fact.metadata.filterName) is not None:
|
|
75
74
|
matches_filter = not filter.isExclude
|
|
76
75
|
break
|
|
77
76
|
if matches_filter:
|
|
78
|
-
l = self.get(fact.
|
|
79
|
-
t = self.__traits.get(fact.target)
|
|
80
|
-
if t is not None:
|
|
81
|
-
for trait in t:
|
|
82
|
-
fact.traits.append(trait)
|
|
77
|
+
l = self.get(fact.target.__module__)
|
|
83
78
|
if not self.__excludeByTraits(fact):
|
|
84
79
|
l.append(fact)
|
|
85
|
-
|
|
86
|
-
def withTrait(self, target:Callable, trait:Trait) -> None:
|
|
87
|
-
t = self.__traits.get(target)
|
|
88
|
-
if t is None:
|
|
89
|
-
t = []
|
|
90
|
-
self.__traits[target] = t
|
|
91
|
-
t.append(trait)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: © 2026 Shaun Wilson
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
from types import BuiltinFunctionType, BuiltinMethodType, FunctionType, MethodType, ModuleType
|
|
5
|
+
from typing import Callable, Optional, Union, cast
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CallableMetadata:
|
|
9
|
+
|
|
10
|
+
__callable:Union[FunctionType, MethodType, BuiltinFunctionType, BuiltinMethodType, Callable]
|
|
11
|
+
__className:str
|
|
12
|
+
__moduleName:str
|
|
13
|
+
__name:str
|
|
14
|
+
|
|
15
|
+
def __init__(self, callable:Union[FunctionType, MethodType, BuiltinFunctionType, BuiltinMethodType, Callable]) -> None:
|
|
16
|
+
self.__callable = callable
|
|
17
|
+
self.__className = '.'.join(callable.__qualname__.replace(f'{callable.__module__}.', '').split('.')[0:-1])
|
|
18
|
+
self.__moduleName = callable.__module__
|
|
19
|
+
self.__name = callable.__name__
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def className(self) -> Optional[str]:
|
|
23
|
+
return self.__className
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def filterName(self) -> str:
|
|
27
|
+
"""
|
|
28
|
+
The name used for pattern matched "Test Filtering".
|
|
29
|
+
"""
|
|
30
|
+
return f'{".".join(self.__moduleName.split(".")[1:])}/{"" if self.__className is None else f"{self.__className}/"}{self.__name}'
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def moduleName(self) -> str:
|
|
34
|
+
return self.__moduleName
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def name(self) -> str:
|
|
38
|
+
return self.__name
|
|
@@ -6,8 +6,10 @@ import os
|
|
|
6
6
|
import socket
|
|
7
7
|
import time
|
|
8
8
|
import traceback
|
|
9
|
+
|
|
9
10
|
from .cli import CommandLineInterface
|
|
10
11
|
from .facts.FactManager import FactManager
|
|
12
|
+
from .metadata.CallableMetadata import CallableMetadata
|
|
11
13
|
from .theories.TheoryManager import TheoryManager
|
|
12
14
|
from .TestResult import TestResult
|
|
13
15
|
|
|
@@ -68,8 +70,8 @@ class TestRunner:
|
|
|
68
70
|
result.exception = ex
|
|
69
71
|
result.releaseOutput()
|
|
70
72
|
result.stopTime = time.time()
|
|
71
|
-
result.className = fact.className
|
|
72
|
-
result.testName = fact.
|
|
73
|
+
result.className = fact.metadata.className
|
|
74
|
+
result.testName = fact.metadata.name
|
|
73
75
|
results.append(result)
|
|
74
76
|
self.printTestResult(result)
|
|
75
77
|
if self.__cli.failfast and not result.isSuccess:
|
|
@@ -94,8 +96,8 @@ class TestRunner:
|
|
|
94
96
|
result.exception = ex
|
|
95
97
|
result.releaseOutput()
|
|
96
98
|
result.stopTime = time.time()
|
|
97
|
-
result.className = theory.className
|
|
98
|
-
result.testName = theory.
|
|
99
|
+
result.className = theory.metadata.className
|
|
100
|
+
result.testName = theory.metadata.name
|
|
99
101
|
results.append(result)
|
|
100
102
|
self.printTestResult(result)
|
|
101
103
|
if self.__cli.failfast and not result.isSuccess:
|
|
@@ -3,79 +3,52 @@
|
|
|
3
3
|
##
|
|
4
4
|
|
|
5
5
|
import inspect
|
|
6
|
-
from types import FunctionType, MethodType, ModuleType
|
|
7
|
-
from typing import Callable, Coroutine, Optional, cast
|
|
6
|
+
from types import BuiltinFunctionType, BuiltinMethodType, FunctionType, MethodType, ModuleType
|
|
7
|
+
from typing import Callable, Coroutine, Optional, Union, cast
|
|
8
8
|
|
|
9
|
-
from ..
|
|
9
|
+
from ..metadata import CallableMetadata
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class Theory:
|
|
13
13
|
|
|
14
|
-
__className:Optional[str]
|
|
15
14
|
__datas:list[tuple]
|
|
16
|
-
|
|
17
|
-
__target:FunctionType|MethodType|Callable
|
|
18
|
-
__testName:Optional[str]
|
|
19
|
-
__traits:list[Trait]
|
|
15
|
+
__target:Union[FunctionType, MethodType, BuiltinFunctionType, BuiltinMethodType, Callable]
|
|
20
16
|
|
|
21
|
-
def __init__(self,
|
|
22
|
-
self.__className = className
|
|
17
|
+
def __init__(self, target:Union[FunctionType, MethodType, BuiltinFunctionType, BuiltinMethodType, Callable]):
|
|
23
18
|
self.__datas = []
|
|
24
|
-
self.
|
|
19
|
+
self.__metadata = CallableMetadata(target)
|
|
25
20
|
self.__target = target
|
|
26
|
-
self.__testName = testName
|
|
27
|
-
self.__traits = []
|
|
28
|
-
|
|
29
|
-
@property
|
|
30
|
-
def className(self) -> Optional[str]:
|
|
31
|
-
return self.__className
|
|
32
21
|
|
|
33
22
|
@property
|
|
34
23
|
def datas(self) -> list[tuple]:
|
|
35
24
|
return self.__datas
|
|
36
25
|
|
|
37
26
|
@property
|
|
38
|
-
def
|
|
39
|
-
return self.
|
|
27
|
+
def metadata(self) -> CallableMetadata:
|
|
28
|
+
return self.__metadata
|
|
40
29
|
|
|
41
30
|
@property
|
|
42
|
-
def target(self) -> FunctionType
|
|
31
|
+
def target(self) -> Union[FunctionType, MethodType, BuiltinFunctionType, BuiltinMethodType, Callable]:
|
|
43
32
|
return self.__target
|
|
44
33
|
|
|
45
|
-
@property
|
|
46
|
-
def testName(self) -> str:
|
|
47
|
-
return self.__testName if self.__testName is not None else self.__target.__qualname__.split('.')[-1]
|
|
48
|
-
|
|
49
|
-
@property
|
|
50
|
-
def traits(self) -> list[Trait]:
|
|
51
|
-
return self.__traits
|
|
52
|
-
|
|
53
|
-
@property
|
|
54
|
-
def filterName(self) -> str:
|
|
55
|
-
return f'{self.moduleName}/{"" if self.className is None or len(self.className) == 0 else f"{self.className}/"}{self.testName}'
|
|
56
|
-
|
|
57
34
|
async def execute(self, module:ModuleType, data:tuple) -> None:
|
|
58
35
|
coro:Coroutine|None = None
|
|
59
36
|
if hasattr(self.__target, '__qualname__') and self.__target.__qualname__.find('.') > -1:
|
|
60
37
|
qnparts = self.__target.__qualname__.split('.')
|
|
61
38
|
if isinstance(self.__target, staticmethod):
|
|
62
39
|
coro = self.__target(*data)
|
|
63
|
-
self.__className = '.'.join(qnparts[0:-1])
|
|
64
|
-
self.__testName = qnparts[-1]
|
|
65
40
|
else:
|
|
66
41
|
qntarget = module
|
|
67
|
-
self.__testName = qnparts[-1]
|
|
68
|
-
self.__className = '.'.join(qnparts[0:-1])
|
|
69
42
|
for qnpart in qnparts[0:-1]:
|
|
70
43
|
qntarget = getattr(qntarget, qnpart)
|
|
71
44
|
if isinstance(self.__target, classmethod):
|
|
72
45
|
args = (qntarget,) + data
|
|
73
46
|
coro = self.__target.__func__(*args)
|
|
74
47
|
else:
|
|
48
|
+
# every test execution gets a new instance of class
|
|
75
49
|
args = (cast(Callable,qntarget)(),) + data
|
|
76
50
|
coro = self.__target(*args)
|
|
77
51
|
else:
|
|
78
|
-
self.__className = None
|
|
79
52
|
self.__testName = self.__target.__name__
|
|
80
53
|
coro = self.__target(*data)
|
|
81
54
|
if inspect.iscoroutine(coro):
|
|
@@ -86,7 +59,7 @@ def theory(target:Callable) -> Callable:
|
|
|
86
59
|
from .TheoryManager import TheoryManager
|
|
87
60
|
if (not inspect.isfunction(target)) and (not isinstance(target, classmethod)) and (not isinstance(target, staticmethod)):
|
|
88
61
|
raise Exception('@theory can only be applied to functions and methods.')
|
|
89
|
-
theory:Theory = Theory(target
|
|
62
|
+
theory:Theory = Theory(target)
|
|
90
63
|
TheoryManager.instance().put(theory)
|
|
91
64
|
return target
|
|
92
65
|
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
# SPDX-License-Identifier: MIT
|
|
3
3
|
##
|
|
4
4
|
|
|
5
|
-
import re
|
|
6
5
|
from typing import Callable, Optional
|
|
7
6
|
|
|
8
7
|
from ..filters.FilterManager import FilterManager
|
|
9
8
|
from ..traits.Trait import Trait
|
|
9
|
+
from ..traits.TraitManager import TraitManager
|
|
10
10
|
from .Theory import Theory
|
|
11
11
|
|
|
12
12
|
|
|
@@ -17,14 +17,12 @@ class TheoryManager:
|
|
|
17
17
|
__instance:Optional['TheoryManager'] = None
|
|
18
18
|
__modules:dict[str, list[Theory]]
|
|
19
19
|
__datas:dict[Callable, list[tuple]]
|
|
20
|
-
__traits:dict[Callable, list[Trait]]
|
|
21
20
|
|
|
22
21
|
def __init__(self) -> None:
|
|
23
22
|
if TheoryManager.__instance is not None:
|
|
24
23
|
raise Exception('Cannot create more than one instance of TheoryManager') # pragma: no cover
|
|
25
24
|
self.__modules = {}
|
|
26
25
|
self.__datas = {}
|
|
27
|
-
self.__traits = {}
|
|
28
26
|
|
|
29
27
|
@staticmethod
|
|
30
28
|
def instance() -> 'TheoryManager':
|
|
@@ -49,14 +47,15 @@ class TheoryManager:
|
|
|
49
47
|
self.__includeTraits = value
|
|
50
48
|
|
|
51
49
|
def __excludeByTraits(self, theory:Theory) -> bool:
|
|
50
|
+
traits = TraitManager.instance().get(theory.target)
|
|
52
51
|
if self.excludeTraits is not None and len(self.__excludeTraits) > 0:
|
|
53
52
|
for trait in self.excludeTraits:
|
|
54
|
-
for L_trait in
|
|
53
|
+
for L_trait in traits:
|
|
55
54
|
if trait.name == L_trait.name and (trait.value is None or (trait.value == L_trait.value)):
|
|
56
55
|
return True
|
|
57
56
|
if self.includeTraits is not None and len(self.includeTraits) > 0:
|
|
58
57
|
for trait in self.includeTraits:
|
|
59
|
-
for L_trait in
|
|
58
|
+
for L_trait in traits:
|
|
60
59
|
if trait.name == L_trait.name and (trait.value is None or (trait.value == L_trait.value)):
|
|
61
60
|
return False
|
|
62
61
|
return True
|
|
@@ -73,20 +72,16 @@ class TheoryManager:
|
|
|
73
72
|
filters = FilterManager.instance().filters
|
|
74
73
|
matches_filter:bool = False
|
|
75
74
|
for filter in filters:
|
|
76
|
-
if filter.re.fullmatch(theory.filterName) is not None:
|
|
75
|
+
if filter.re.fullmatch(theory.metadata.filterName) is not None:
|
|
77
76
|
matches_filter = not filter.isExclude
|
|
78
77
|
break
|
|
79
78
|
if matches_filter:
|
|
80
|
-
l = self.get(theory.
|
|
79
|
+
l = self.get(theory.target.__module__)
|
|
81
80
|
d = self.__datas.get(theory.target)
|
|
82
81
|
if d is not None:
|
|
83
82
|
d.reverse()
|
|
84
83
|
for data in d:
|
|
85
84
|
theory.datas.append(data)
|
|
86
|
-
t = self.__traits.get(theory.target)
|
|
87
|
-
if t is not None:
|
|
88
|
-
for trait in t:
|
|
89
|
-
theory.traits.append(trait)
|
|
90
85
|
if not self.__excludeByTraits(theory):
|
|
91
86
|
l.append(theory)
|
|
92
87
|
|
|
@@ -99,10 +94,3 @@ class TheoryManager:
|
|
|
99
94
|
d = []
|
|
100
95
|
self.__datas[target] = d
|
|
101
96
|
d.append(data)
|
|
102
|
-
|
|
103
|
-
def withTrait(self, target:Callable, trait:Trait) -> None:
|
|
104
|
-
t = self.__traits.get(target)
|
|
105
|
-
if t is None:
|
|
106
|
-
t = []
|
|
107
|
-
self.__traits[target] = t
|
|
108
|
-
t.append(trait)
|
|
@@ -25,10 +25,8 @@ class Trait:
|
|
|
25
25
|
|
|
26
26
|
def trait(name:str, value:Optional[str]=None) -> Callable:
|
|
27
27
|
def wrapper(target:Callable) -> Callable:
|
|
28
|
-
from
|
|
29
|
-
from ..facts.FactManager import FactManager
|
|
28
|
+
from .TraitManager import TraitManager
|
|
30
29
|
trait = Trait(name, value)
|
|
31
|
-
|
|
32
|
-
FactManager.instance().withTrait(target, trait)
|
|
30
|
+
TraitManager.instance().put(target, trait)
|
|
33
31
|
return target
|
|
34
32
|
return wrapper
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: © 2026 Shaun Wilson
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
##
|
|
4
|
+
|
|
5
|
+
from types import FunctionType, MethodType
|
|
6
|
+
from typing import Callable, Optional
|
|
7
|
+
|
|
8
|
+
from .Trait import Trait
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TraitManager:
|
|
12
|
+
|
|
13
|
+
__instance:Optional['TraitManager'] = None
|
|
14
|
+
__traits:dict[Callable|FunctionType|MethodType, dict[str, Trait]]
|
|
15
|
+
|
|
16
|
+
def __init__(self) -> None:
|
|
17
|
+
if TraitManager.__instance is not None:
|
|
18
|
+
raise Exception('Cannot create more than one instance of TraitManager') # pragma: no cover
|
|
19
|
+
self.__traits = dict[Callable|FunctionType|MethodType, dict[str, Trait]]()
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
def instance() -> 'TraitManager':
|
|
23
|
+
if TraitManager.__instance is None:
|
|
24
|
+
TraitManager.__instance = TraitManager()
|
|
25
|
+
return TraitManager.__instance
|
|
26
|
+
|
|
27
|
+
def get(self, callable:Callable|FunctionType|MethodType) -> list[Trait]:
|
|
28
|
+
d = self.__traits.get(callable)
|
|
29
|
+
if d is None:
|
|
30
|
+
d = dict[str, Trait]()
|
|
31
|
+
self.__traits[callable] = d
|
|
32
|
+
return [e for e in d.values()]
|
|
33
|
+
|
|
34
|
+
def put(self, callable:Callable, trait:Trait) -> None:
|
|
35
|
+
d = self.__traits.get(callable)
|
|
36
|
+
if d is None:
|
|
37
|
+
d = dict[str,Trait]()
|
|
38
|
+
self.__traits[callable] = d
|
|
39
|
+
d[trait.name] = trait
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: © 2024 Shaun Wilson
|
|
2
|
-
# SPDX-License-Identifier: MIT
|
|
3
|
-
|
|
4
|
-
import inspect
|
|
5
|
-
from types import FunctionType, MethodType, ModuleType
|
|
6
|
-
from typing import Callable, Coroutine, Optional, cast
|
|
7
|
-
|
|
8
|
-
from ..traits.Trait import Trait
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class Fact:
|
|
12
|
-
|
|
13
|
-
__className:Optional[str]
|
|
14
|
-
__moduleName:str
|
|
15
|
-
__target:FunctionType|MethodType|Callable
|
|
16
|
-
__testName:Optional[str]
|
|
17
|
-
__traits:list[Trait]
|
|
18
|
-
|
|
19
|
-
def __init__(self, moduleName:str, target:FunctionType|MethodType|Callable, className:Optional[str] = None, testName:Optional[str] = None):
|
|
20
|
-
self.__className = className
|
|
21
|
-
self.__moduleName = moduleName
|
|
22
|
-
self.__target = target
|
|
23
|
-
self.__testName = testName
|
|
24
|
-
self.__traits = []
|
|
25
|
-
|
|
26
|
-
@property
|
|
27
|
-
def className(self) -> Optional[str]:
|
|
28
|
-
return self.__className
|
|
29
|
-
|
|
30
|
-
@property
|
|
31
|
-
def moduleName(self) -> str:
|
|
32
|
-
return self.__moduleName
|
|
33
|
-
|
|
34
|
-
@property
|
|
35
|
-
def target(self) -> FunctionType|MethodType|Callable:
|
|
36
|
-
return self.__target
|
|
37
|
-
|
|
38
|
-
@property
|
|
39
|
-
def testName(self) -> str:
|
|
40
|
-
return self.__testName if self.__testName is not None else self.__target.__qualname__.split('.')[-1]
|
|
41
|
-
|
|
42
|
-
@property
|
|
43
|
-
def filterName(self) -> str:
|
|
44
|
-
return f'{self.moduleName}/{"" if self.className is None or len(self.className) == 0 else f"{self.className}/"}{self.testName}'
|
|
45
|
-
|
|
46
|
-
@property
|
|
47
|
-
def traits(self) -> list[Trait]:
|
|
48
|
-
return self.__traits
|
|
49
|
-
|
|
50
|
-
async def execute(self, module:ModuleType) -> None:
|
|
51
|
-
coro:Coroutine|None = None
|
|
52
|
-
if hasattr(self.__target, '__qualname__') and self.__target.__qualname__.find('.') > -1:
|
|
53
|
-
qnparts = self.__target.__qualname__.split('.')
|
|
54
|
-
if isinstance(self.__target, staticmethod):
|
|
55
|
-
coro = self.__target()
|
|
56
|
-
self.__className = '.'.join(qnparts[0:-1])
|
|
57
|
-
self.__testName = qnparts[-1]
|
|
58
|
-
else:
|
|
59
|
-
qntarget = module
|
|
60
|
-
self.__className = '.'.join(qnparts[0:-1])
|
|
61
|
-
self.__testName = qnparts[-1]
|
|
62
|
-
for qnpart in qnparts[0:-1]:
|
|
63
|
-
qntarget = getattr(qntarget, qnpart)
|
|
64
|
-
if isinstance(self.__target, classmethod):
|
|
65
|
-
coro = self.__target.__func__(qntarget)
|
|
66
|
-
else:
|
|
67
|
-
coro = self.__target(cast(Callable,qntarget)())
|
|
68
|
-
else:
|
|
69
|
-
self.__className = None
|
|
70
|
-
self.__testName = self.__target.__name__
|
|
71
|
-
coro = self.__target()
|
|
72
|
-
if inspect.iscoroutine(coro):
|
|
73
|
-
await coro
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def fact(target:Callable) -> Callable:
|
|
77
|
-
from .FactManager import FactManager
|
|
78
|
-
if (not inspect.isfunction(target)) and (not isinstance(target, classmethod)) and (not isinstance(target, staticmethod)):
|
|
79
|
-
raise Exception('@fact can only be applied to functions and methods.')
|
|
80
|
-
fact:Fact = Fact(target.__module__, target)
|
|
81
|
-
FactManager.instance().put(fact)
|
|
82
|
-
return target
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|