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.
Files changed (41) hide show
  1. {punit-1.2.23/src/pUnit.egg-info → punit-1.2.26}/PKG-INFO +1 -1
  2. {punit-1.2.23 → punit-1.2.26}/pyproject.toml +1 -1
  3. {punit-1.2.23 → punit-1.2.26/src/pUnit.egg-info}/PKG-INFO +1 -1
  4. {punit-1.2.23 → punit-1.2.26}/src/pUnit.egg-info/SOURCES.txt +3 -0
  5. {punit-1.2.23 → punit-1.2.26}/src/punit/__init__.py +3 -2
  6. punit-1.2.26/src/punit/facts/Fact.py +55 -0
  7. {punit-1.2.23 → punit-1.2.26}/src/punit/facts/FactManager.py +7 -19
  8. punit-1.2.26/src/punit/metadata/CallableMetadata.py +38 -0
  9. punit-1.2.26/src/punit/metadata/__init__.py +9 -0
  10. {punit-1.2.23 → punit-1.2.26}/src/punit/runner.py +6 -4
  11. {punit-1.2.23 → punit-1.2.26}/src/punit/theories/Theory.py +11 -38
  12. {punit-1.2.23 → punit-1.2.26}/src/punit/theories/TheoryManager.py +6 -18
  13. {punit-1.2.23 → punit-1.2.26}/src/punit/traits/Trait.py +2 -4
  14. punit-1.2.26/src/punit/traits/TraitManager.py +39 -0
  15. {punit-1.2.23 → punit-1.2.26}/src/punit/traits/__init__.py +2 -0
  16. punit-1.2.23/src/punit/facts/Fact.py +0 -82
  17. {punit-1.2.23 → punit-1.2.26}/.scripts/punit +0 -0
  18. {punit-1.2.23 → punit-1.2.26}/LICENSE +0 -0
  19. {punit-1.2.23 → punit-1.2.26}/README.md +0 -0
  20. {punit-1.2.23 → punit-1.2.26}/setup.cfg +0 -0
  21. {punit-1.2.23 → punit-1.2.26}/src/pUnit.egg-info/dependency_links.txt +0 -0
  22. {punit-1.2.23 → punit-1.2.26}/src/pUnit.egg-info/requires.txt +0 -0
  23. {punit-1.2.23 → punit-1.2.26}/src/pUnit.egg-info/top_level.txt +0 -0
  24. {punit-1.2.23 → punit-1.2.26}/src/punit/TestResult.py +0 -0
  25. {punit-1.2.23 → punit-1.2.26}/src/punit/__main__.py +0 -0
  26. {punit-1.2.23 → punit-1.2.26}/src/punit/assertions/__init__.py +0 -0
  27. {punit-1.2.23 → punit-1.2.26}/src/punit/assertions/collections.py +0 -0
  28. {punit-1.2.23 → punit-1.2.26}/src/punit/assertions/exceptions.py +0 -0
  29. {punit-1.2.23 → punit-1.2.26}/src/punit/assertions/strings.py +0 -0
  30. {punit-1.2.23 → punit-1.2.26}/src/punit/cli.py +0 -0
  31. {punit-1.2.23 → punit-1.2.26}/src/punit/discovery/TestModuleDiscovery.py +0 -0
  32. {punit-1.2.23 → punit-1.2.26}/src/punit/discovery/__init__.py +0 -0
  33. {punit-1.2.23 → punit-1.2.26}/src/punit/facts/__init__.py +0 -0
  34. {punit-1.2.23 → punit-1.2.26}/src/punit/filters/Filter.py +0 -0
  35. {punit-1.2.23 → punit-1.2.26}/src/punit/filters/FilterManager.py +0 -0
  36. {punit-1.2.23 → punit-1.2.26}/src/punit/filters/__init__.py +0 -0
  37. {punit-1.2.23 → punit-1.2.26}/src/punit/py.typed +0 -0
  38. {punit-1.2.23 → punit-1.2.26}/src/punit/reports/HtmlReportGenerator.py +0 -0
  39. {punit-1.2.23 → punit-1.2.26}/src/punit/reports/JUnitReportGenerator.py +0 -0
  40. {punit-1.2.23 → punit-1.2.26}/src/punit/reports/__init__.py +0 -0
  41. {punit-1.2.23 → punit-1.2.26}/src/punit/theories/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pUnit
3
- Version: 1.2.23
3
+ Version: 1.2.26
4
4
  Summary: A modernized unit-test framework for Python.
5
5
  Author-email: Shaun Wilson <mrshaunwilson@msn.com>
6
6
  License: MIT License
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pUnit"
3
- version = "1.2.23"
3
+ version = "1.2.26"
4
4
  description = "A modernized unit-test framework for Python."
5
5
  keywords = ["test", "unittest", "unit-test", "xUnit", "nUnit", "pytest"]
6
6
  authors = [
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pUnit
3
- Version: 1.2.23
3
+ Version: 1.2.26
4
4
  Summary: A modernized unit-test framework for Python.
5
5
  Author-email: Shaun Wilson <mrshaunwilson@msn.com>
6
6
  License: MIT License
@@ -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.23'
11
- __commit__ = '5327cc9'
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 re
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 fact.traits:
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 fact.traits:
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.moduleName)
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
@@ -0,0 +1,9 @@
1
+ # SPDX-FileCopyrightText: © 2026 Shaun Wilson
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ from .CallableMetadata import CallableMetadata
5
+
6
+
7
+ __all__ = [
8
+ 'CallableMetadata'
9
+ ]
@@ -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.testName
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.testName
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 ..traits.Trait import Trait
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
- __moduleName:str
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, moduleName:str, target:FunctionType|MethodType|Callable, className:Optional[str] = None, testName:Optional[str] = None):
22
- self.__className = className
17
+ def __init__(self, target:Union[FunctionType, MethodType, BuiltinFunctionType, BuiltinMethodType, Callable]):
23
18
  self.__datas = []
24
- self.__moduleName = moduleName
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 moduleName(self) -> str:
39
- return self.__moduleName
27
+ def metadata(self) -> CallableMetadata:
28
+ return self.__metadata
40
29
 
41
30
  @property
42
- def target(self) -> FunctionType|MethodType|Callable:
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.__module__, 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 theory.traits:
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 theory.traits:
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.moduleName)
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 ..theories.TheoryManager import TheoryManager
29
- from ..facts.FactManager import FactManager
28
+ from .TraitManager import TraitManager
30
29
  trait = Trait(name, value)
31
- TheoryManager.instance().withTrait(target, trait)
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,9 +1,11 @@
1
1
  # SPDX-FileCopyrightText: © 2024 Shaun Wilson
2
2
  # SPDX-License-Identifier: MIT
3
3
 
4
+ from .TraitManager import TraitManager
4
5
  from .Trait import Trait, trait
5
6
 
6
7
 
7
8
  __all__ = [
9
+ 'TraitManager',
8
10
  'Trait', 'trait'
9
11
  ]
@@ -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