punit 1.2.19__tar.gz → 1.2.23__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 (37) hide show
  1. {punit-1.2.19/src/pUnit.egg-info → punit-1.2.23}/PKG-INFO +5 -5
  2. {punit-1.2.19 → punit-1.2.23}/README.md +4 -4
  3. {punit-1.2.19 → punit-1.2.23}/pyproject.toml +1 -1
  4. {punit-1.2.19 → punit-1.2.23/src/pUnit.egg-info}/PKG-INFO +5 -5
  5. {punit-1.2.19 → punit-1.2.23}/src/pUnit.egg-info/SOURCES.txt +3 -0
  6. {punit-1.2.19 → punit-1.2.23}/src/punit/TestResult.py +4 -2
  7. {punit-1.2.19 → punit-1.2.23}/src/punit/__init__.py +2 -2
  8. {punit-1.2.19 → punit-1.2.23}/src/punit/__main__.py +1 -1
  9. {punit-1.2.19 → punit-1.2.23}/src/punit/cli.py +22 -24
  10. {punit-1.2.19 → punit-1.2.23}/src/punit/discovery/TestModuleDiscovery.py +2 -19
  11. {punit-1.2.19 → punit-1.2.23}/src/punit/discovery/__init__.py +0 -1
  12. {punit-1.2.19 → punit-1.2.23}/src/punit/facts/FactManager.py +10 -19
  13. punit-1.2.23/src/punit/filters/Filter.py +37 -0
  14. punit-1.2.23/src/punit/filters/FilterManager.py +70 -0
  15. punit-1.2.23/src/punit/filters/__init__.py +11 -0
  16. {punit-1.2.19 → punit-1.2.23}/src/punit/theories/TheoryManager.py +10 -19
  17. {punit-1.2.19 → punit-1.2.23}/.scripts/punit +0 -0
  18. {punit-1.2.19 → punit-1.2.23}/LICENSE +0 -0
  19. {punit-1.2.19 → punit-1.2.23}/setup.cfg +0 -0
  20. {punit-1.2.19 → punit-1.2.23}/src/pUnit.egg-info/dependency_links.txt +0 -0
  21. {punit-1.2.19 → punit-1.2.23}/src/pUnit.egg-info/requires.txt +0 -0
  22. {punit-1.2.19 → punit-1.2.23}/src/pUnit.egg-info/top_level.txt +0 -0
  23. {punit-1.2.19 → punit-1.2.23}/src/punit/assertions/__init__.py +0 -0
  24. {punit-1.2.19 → punit-1.2.23}/src/punit/assertions/collections.py +0 -0
  25. {punit-1.2.19 → punit-1.2.23}/src/punit/assertions/exceptions.py +0 -0
  26. {punit-1.2.19 → punit-1.2.23}/src/punit/assertions/strings.py +0 -0
  27. {punit-1.2.19 → punit-1.2.23}/src/punit/facts/Fact.py +0 -0
  28. {punit-1.2.19 → punit-1.2.23}/src/punit/facts/__init__.py +0 -0
  29. {punit-1.2.19 → punit-1.2.23}/src/punit/py.typed +0 -0
  30. {punit-1.2.19 → punit-1.2.23}/src/punit/reports/HtmlReportGenerator.py +0 -0
  31. {punit-1.2.19 → punit-1.2.23}/src/punit/reports/JUnitReportGenerator.py +0 -0
  32. {punit-1.2.19 → punit-1.2.23}/src/punit/reports/__init__.py +0 -0
  33. {punit-1.2.19 → punit-1.2.23}/src/punit/runner.py +0 -0
  34. {punit-1.2.19 → punit-1.2.23}/src/punit/theories/Theory.py +0 -0
  35. {punit-1.2.19 → punit-1.2.23}/src/punit/theories/__init__.py +0 -0
  36. {punit-1.2.19 → punit-1.2.23}/src/punit/traits/Trait.py +0 -0
  37. {punit-1.2.19 → punit-1.2.23}/src/punit/traits/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pUnit
3
- Version: 1.2.19
3
+ Version: 1.2.23
4
4
  Summary: A modernized unit-test framework for Python.
5
5
  Author-email: Shaun Wilson <mrshaunwilson@msn.com>
6
6
  License: MIT License
@@ -110,10 +110,10 @@ Options:
110
110
  be locatable as modules in the named package.
111
111
  Default: 'tests'
112
112
  -i, --include PATTERN
113
- Include any tests matching PATTERN
113
+ Include test files matching PATTERN
114
114
  Default: '*.py'
115
115
  -e, --exclude PATTERN
116
- Exclude any tests matching PATTERN, overriding --include
116
+ Exclude test files matching PATTERN, overriding --include
117
117
  Default: '__*__' (dunder files), '/.*/' (dot-directories)
118
118
  -f, --filter PATTERN|@FILEPATH
119
119
  Only execute tests matching PATTERN
@@ -178,9 +178,9 @@ class MyTestFixture:
178
178
  def verifyCalcErrorCondition(self) -> None:
179
179
  from punit.exceptions import raises
180
180
  # assert errors are raised, or not
181
- def calc_None():
181
+ def calc_None() -> None:
182
182
  self.calc(None)
183
- def calc_1()
183
+ def calc_1() -> None:
184
184
  self.calc(1)
185
185
  assert raises[Exception](calc_None)
186
186
  assert not raises[Exception](calc_1)
@@ -64,10 +64,10 @@ Options:
64
64
  be locatable as modules in the named package.
65
65
  Default: 'tests'
66
66
  -i, --include PATTERN
67
- Include any tests matching PATTERN
67
+ Include test files matching PATTERN
68
68
  Default: '*.py'
69
69
  -e, --exclude PATTERN
70
- Exclude any tests matching PATTERN, overriding --include
70
+ Exclude test files matching PATTERN, overriding --include
71
71
  Default: '__*__' (dunder files), '/.*/' (dot-directories)
72
72
  -f, --filter PATTERN|@FILEPATH
73
73
  Only execute tests matching PATTERN
@@ -132,9 +132,9 @@ class MyTestFixture:
132
132
  def verifyCalcErrorCondition(self) -> None:
133
133
  from punit.exceptions import raises
134
134
  # assert errors are raised, or not
135
- def calc_None():
135
+ def calc_None() -> None:
136
136
  self.calc(None)
137
- def calc_1()
137
+ def calc_1() -> None:
138
138
  self.calc(1)
139
139
  assert raises[Exception](calc_None)
140
140
  assert not raises[Exception](calc_1)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pUnit"
3
- version = "1.2.19"
3
+ version = "1.2.23"
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.19
3
+ Version: 1.2.23
4
4
  Summary: A modernized unit-test framework for Python.
5
5
  Author-email: Shaun Wilson <mrshaunwilson@msn.com>
6
6
  License: MIT License
@@ -110,10 +110,10 @@ Options:
110
110
  be locatable as modules in the named package.
111
111
  Default: 'tests'
112
112
  -i, --include PATTERN
113
- Include any tests matching PATTERN
113
+ Include test files matching PATTERN
114
114
  Default: '*.py'
115
115
  -e, --exclude PATTERN
116
- Exclude any tests matching PATTERN, overriding --include
116
+ Exclude test files matching PATTERN, overriding --include
117
117
  Default: '__*__' (dunder files), '/.*/' (dot-directories)
118
118
  -f, --filter PATTERN|@FILEPATH
119
119
  Only execute tests matching PATTERN
@@ -178,9 +178,9 @@ class MyTestFixture:
178
178
  def verifyCalcErrorCondition(self) -> None:
179
179
  from punit.exceptions import raises
180
180
  # assert errors are raised, or not
181
- def calc_None():
181
+ def calc_None() -> None:
182
182
  self.calc(None)
183
- def calc_1()
183
+ def calc_1() -> None:
184
184
  self.calc(1)
185
185
  assert raises[Exception](calc_None)
186
186
  assert not raises[Exception](calc_1)
@@ -22,6 +22,9 @@ src/punit/discovery/__init__.py
22
22
  src/punit/facts/Fact.py
23
23
  src/punit/facts/FactManager.py
24
24
  src/punit/facts/__init__.py
25
+ src/punit/filters/Filter.py
26
+ src/punit/filters/FilterManager.py
27
+ src/punit/filters/__init__.py
25
28
  src/punit/reports/HtmlReportGenerator.py
26
29
  src/punit/reports/JUnitReportGenerator.py
27
30
  src/punit/reports/__init__.py
@@ -16,13 +16,15 @@ class TextIOCapture:
16
16
  self.output = None
17
17
  self.target = target
18
18
 
19
- def write(self, text:str):
19
+ def write(self, text:str) -> int:
20
20
  if self.output is None:
21
21
  self.output = text
22
22
  else:
23
23
  self.output += text
24
24
  if not self.__quiet:
25
25
  self.target.write(text)
26
+ return len(text)
27
+
26
28
 
27
29
 
28
30
  class TestResult:
@@ -41,7 +43,7 @@ class TestResult:
41
43
  __stopTime:float|None
42
44
  __testName:str|None
43
45
 
44
- def __init__(self):
46
+ def __init__(self) -> None:
45
47
  self.__className = None
46
48
  self.__exception = None
47
49
  self.__fileName = None
@@ -7,8 +7,8 @@ from .theories import *
7
7
  from .traits import *
8
8
 
9
9
 
10
- __version__ = '1.2.19'
11
- __commit__ = 'af80eb6'
10
+ __version__ = '1.2.23'
11
+ __commit__ = '5327cc9'
12
12
  __all__ = [
13
13
  '__version__', '__commit__',
14
14
  'assertions',
@@ -52,7 +52,7 @@ async def async_main() -> None:
52
52
  if failureCount > 0:
53
53
  exit(119) # pragma: no cover
54
54
 
55
- def main():
55
+ def main() -> None:
56
56
  asyncio.run(async_main())
57
57
 
58
58
  if (__name__ == '__main__'):
@@ -5,6 +5,7 @@ import os
5
5
  import sys
6
6
  from typing import Optional
7
7
  from . import __version__, __commit__
8
+ from .filters.FilterManager import FilterManager
8
9
  from .traits import Trait
9
10
 
10
11
 
@@ -14,7 +15,6 @@ class CommandLineInterface:
14
15
  __excludePatterns:list[str]
15
16
  __excludeTraits:list[Trait]
16
17
  __failfast:bool
17
- __filterPattern:str
18
18
  __help:bool
19
19
  __includePatterns:list[str]
20
20
  __includeTraits:list[Trait]
@@ -26,12 +26,11 @@ class CommandLineInterface:
26
26
  __verbose:bool
27
27
  __workdir:str|None
28
28
 
29
- def __init__(self):
29
+ def __init__(self) -> None:
30
30
  self.__aliases = dict[str,str]()
31
31
  self.__excludePatterns = []
32
32
  self.__excludeTraits = []
33
33
  self.__failfast = False
34
- self.__filterPattern = '*'
35
34
  self.__help = False
36
35
  self.__includePatterns = []
37
36
  self.__includeTraits = []
@@ -45,7 +44,7 @@ class CommandLineInterface:
45
44
 
46
45
  def __parse(self, argv:list[str]) -> 'CommandLineInterface':
47
46
  aliasName:str|None = None
48
- extractFilterPattern:bool = False
47
+ extractFilter:bool = False
49
48
  extractExcludePattern:bool = False
50
49
  extractTrait:bool = False
51
50
  extractIncludePattern:bool = False
@@ -54,9 +53,9 @@ class CommandLineInterface:
54
53
  extractReportFormat:bool = False
55
54
  extractOutputFilename:bool = False
56
55
  for arg in argv:
57
- if extractFilterPattern:
58
- self.__filterPattern = arg
59
- extractFilterPattern = False
56
+ if extractFilter:
57
+ self.__processFilterArg(arg)
58
+ extractFilter = False
60
59
  continue
61
60
  if extractIncludePattern:
62
61
  self.__includePatterns.append(arg)
@@ -113,7 +112,7 @@ class CommandLineInterface:
113
112
  case '-a':
114
113
  extractAliasName = True
115
114
  case '-f' | '--filter':
116
- extractFilterPattern = True
115
+ extractFilter = True
117
116
  case '-e' | '--exclude':
118
117
  extractExcludePattern = True
119
118
  case '-z' | '--failfast':
@@ -138,8 +137,18 @@ class CommandLineInterface:
138
137
  extractTrait = True
139
138
  case _:
140
139
  continue
140
+
141
+ if len(FilterManager.instance().filters) == 0:
142
+ FilterManager.instance().add('*')
143
+
141
144
  return self
142
145
 
146
+ def __processFilterArg(self, arg:str) -> None:
147
+ if arg.startswith('@'):
148
+ FilterManager.instance().load(arg[1:])
149
+ else:
150
+ FilterManager.instance().add(arg)
151
+
143
152
  @property
144
153
  def aliases(self) -> dict:
145
154
  return self.__aliases
@@ -148,10 +157,6 @@ class CommandLineInterface:
148
157
  def failfast(self) -> bool:
149
158
  return self.__failfast
150
159
 
151
- @property
152
- def filterPattern(self) -> str:
153
- return self.__filterPattern
154
-
155
160
  @property
156
161
  def excludePatterns(self) -> list[str]:
157
162
  return self.__excludePatterns
@@ -225,10 +230,10 @@ Options:
225
230
  be locatable as modules in the named package.
226
231
  Default: 'tests'
227
232
  -i, --include PATTERN
228
- Include any tests matching PATTERN
233
+ Include test files matching PATTERN
229
234
  Default: '*.py'
230
235
  -e, --exclude PATTERN
231
- Exclude any tests matching PATTERN, overriding --include
236
+ Exclude test files matching PATTERN, overriding --include
232
237
  Default: '__*__' (dunder files), '/.*/' (dot-directories)
233
238
  -f, --filter PATTERN|@FILEPATH
234
239
  Only execute tests matching PATTERN
@@ -262,21 +267,14 @@ Options:
262
267
  print(f'Working Directory:\n\t{self.__workdir}')
263
268
  print(f'Fail Fast: \n\t{"Yes" if self.__failfast else "No"}')
264
269
  if len(self.__includePatterns) > 0:
265
- print('Include Patterns:')
270
+ print('Include Files:')
266
271
  for pattern in self.__includePatterns:
267
272
  print(f'\t{pattern}')
268
273
  if len(self.__excludePatterns) > 0:
269
- print('Exclude Patterns:')
274
+ print('Exclude Files:')
270
275
  for pattern in self.__excludePatterns:
271
276
  print(f'\t{pattern}')
272
- if self.__filterPattern.startswith('@'):
273
- print(f'Filter Patterns: (from {self.__filterPattern[1:]})')
274
- if not os.path.exists(os.path.realpath(self.__filterPattern[1:])):
275
- print(f'\tFILTERS FILE DOES NOT EXIST, ABORTING!')
276
- exit(5)
277
- else:
278
- print('Filter Pattern:')
279
- print(f'\t{self.__filterPattern}')
277
+ FilterManager.instance().print()
280
278
 
281
279
  def printVersion(self) -> None:
282
280
  print(f'pUnit {__version__} ({__commit__})')
@@ -9,13 +9,13 @@ from ..theories import TheoryManager
9
9
  from ..cli import CommandLineInterface
10
10
  from ..traits import Trait
11
11
 
12
+
12
13
  class TestModuleDiscovery:
13
14
 
14
15
  __cli:CommandLineInterface
15
16
  __excludePatterns:list[re.Pattern]
16
17
  __excludeTraits:list[Trait]
17
18
  __filenames:list[str]
18
- __filterPatterns:list[re.Pattern]
19
19
  __includePatterns:list[re.Pattern]
20
20
  __includeTraits:list[Trait]
21
21
  __workdir:str
@@ -30,7 +30,6 @@ class TestModuleDiscovery:
30
30
  self.__convertPatternToRegex(pattern),
31
31
  re.IGNORECASE))
32
32
  self.__filenames = []
33
- self.__filterPatterns = self.__buildFilterPatterns(cli.filterPattern)
34
33
  self.__includePatterns = []
35
34
  self.__excludeTraits = cli.excludeTraits
36
35
  self.__includeTraits = cli.includeTraits
@@ -42,25 +41,11 @@ class TestModuleDiscovery:
42
41
  re.IGNORECASE))
43
42
  self.__workdir = workdir
44
43
 
45
- def __buildFilterPatterns(self, input:str) -> list[re.Pattern]:
46
- if input.startswith('@'):
47
- # treat as a filepath containing one or more filter patterns
48
- filepath = os.path.realpath(input[1:])
49
- with open(filepath, 'rb') as f:
50
- result = list[re.Pattern]()
51
- for line in f.read().decode().splitlines():
52
- if (self.__cli.verbose):
53
- print(f'\t{line}')
54
- result.append(re.compile(self.__convertPatternToRegex(line)))
55
- return result
56
- else:
57
- # treat as a single filter pattern
58
- return [re.compile(self.__convertPatternToRegex(input))]
59
44
 
60
45
  def __convertPatternToRegex(self, pattern:str) -> str:
61
46
  result = re.escape(pattern)\
62
47
  .replace('\\\\', '/')\
63
- .replace('\\*', r'.+')\
48
+ .replace('\\*', r'.*')\
64
49
  .replace('?', '.')
65
50
  return result
66
51
 
@@ -115,10 +100,8 @@ class TestModuleDiscovery:
115
100
 
116
101
  def discover(self) -> list[str]:
117
102
  FactManager.instance().excludeTraits = self.__excludeTraits
118
- FactManager.instance().filterPatterns = self.__filterPatterns
119
103
  FactManager.instance().includeTraits = self.__includeTraits
120
104
  TheoryManager.instance().excludeTraits = self.__excludeTraits
121
- TheoryManager.instance().filterPatterns = self.__filterPatterns
122
105
  TheoryManager.instance().includeTraits = self.__includeTraits
123
106
  if self.__cli.verbose:
124
107
  print(f'.. starting test discovery')
@@ -3,7 +3,6 @@
3
3
 
4
4
  from .TestModuleDiscovery import TestModuleDiscovery
5
5
 
6
-
7
6
  __all__ = [
8
7
  'TestModuleDiscovery'
9
8
  ]
@@ -5,23 +5,22 @@
5
5
  import re
6
6
  from typing import Callable, Optional
7
7
 
8
- from .Fact import Fact
8
+ from ..filters.FilterManager import FilterManager
9
9
  from ..traits.Trait import Trait
10
+ from .Fact import Fact
10
11
 
11
12
 
12
13
  class FactManager:
13
14
 
14
15
  __excludeTraits:list[Trait]
15
- __filterPatterns:Optional[list[re.Pattern]]
16
16
  __instance:Optional['FactManager'] = None
17
17
  __includeTraits:list[Trait]
18
18
  __modules:dict[str, list[Fact]]
19
19
  __traits:dict[Callable, list[Trait]]
20
20
 
21
- def __init__(self):
21
+ def __init__(self) -> None:
22
22
  if FactManager.__instance is not None:
23
23
  raise Exception('Cannot create more than one instance of FactManager') # pragma: no cover
24
- self.__filterPatterns = None
25
24
  self.__modules = {}
26
25
  self.__traits = {}
27
26
 
@@ -39,14 +38,6 @@ class FactManager:
39
38
  def excludeTraits(self, value:list[Trait]) -> None:
40
39
  self.__excludeTraits = value
41
40
 
42
- @property
43
- def filterPatterns(self) -> Optional[list[re.Pattern]]:
44
- return self.__filterPatterns
45
-
46
- @filterPatterns.setter
47
- def filterPatterns(self, value:list[re.Pattern]) -> None:
48
- self.__filterPatterns = value
49
-
50
41
  @property
51
42
  def includeTraits(self) -> list[Trait]:
52
43
  return [] if self.__includeTraits is None else self.__includeTraits
@@ -77,13 +68,13 @@ class FactManager:
77
68
  return l
78
69
 
79
70
  def put(self, fact:Fact) -> None:
80
- matchesFilterPattern:bool = self.filterPatterns is None
81
- if self.filterPatterns is not None and len(self.filterPatterns) > 0:
82
- for filterPattern in self.filterPatterns:
83
- if len(filterPattern.findall(fact.filterName)) > 0:
84
- matchesFilterPattern = True
85
- break
86
- if matchesFilterPattern:
71
+ filters = FilterManager.instance().filters
72
+ matches_filter:bool = False
73
+ for filter in filters:
74
+ if filter.re.fullmatch(fact.filterName) is not None:
75
+ matches_filter = not filter.isExclude
76
+ break
77
+ if matches_filter:
87
78
  l = self.get(fact.moduleName)
88
79
  t = self.__traits.get(fact.target)
89
80
  if t is not None:
@@ -0,0 +1,37 @@
1
+ # SPDX-FileCopyrightText: © 2026 Shaun Wilson
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ import re
5
+
6
+
7
+ class Filter:
8
+
9
+ __isExclude:bool
10
+ __pattern:str
11
+ __re:re.Pattern
12
+
13
+ def __init__(self, pattern:str) -> None:
14
+ self.__isExclude = pattern.startswith('!')
15
+ self.__pattern = pattern
16
+ self.__re = self.__toRegex(pattern)
17
+
18
+ def __toRegex(self, pattern:str) -> re.Pattern:
19
+ if self.__isExclude:
20
+ pattern = pattern[1:]
21
+ pattern = re.escape(pattern)\
22
+ .replace('\\\\', '/')\
23
+ .replace('\\*', r'.*')\
24
+ .replace('?', '.')
25
+ return re.compile(pattern, re.IGNORECASE)
26
+
27
+ @property
28
+ def isExclude(self) -> bool:
29
+ return self.__isExclude
30
+
31
+ @property
32
+ def pattern(self) -> str:
33
+ return self.__pattern
34
+
35
+ @property
36
+ def re(self) -> re.Pattern:
37
+ return self.__re
@@ -0,0 +1,70 @@
1
+ # SPDX-FileCopyrightText: © 2026 Shaun Wilson
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ import os
5
+ import select
6
+ import sys
7
+ from typing import Optional
8
+
9
+ from .Filter import Filter
10
+
11
+
12
+ class FilterManager:
13
+
14
+ __instance:Optional['FilterManager']|None = None
15
+ __filters:list[Filter]
16
+
17
+ def __init__(self) -> None:
18
+ self.__filters = list[Filter]()
19
+
20
+ @property
21
+ def filters(self) -> list[Filter]:
22
+ return self.__filters
23
+
24
+ @staticmethod
25
+ def instance() -> 'FilterManager':
26
+ instance = FilterManager.__instance
27
+ if instance is None:
28
+ instance = FilterManager()
29
+ FilterManager.__instance = instance
30
+ return instance
31
+
32
+ def add(self, pattern:str) -> None:
33
+ self.__filters.append(Filter(pattern))
34
+
35
+ def remove(self, pattern:str) -> None:
36
+ for filter in [e for e in self.__filters]:
37
+ if filter.pattern == pattern:
38
+ self.__filters.remove(filter)
39
+ break
40
+
41
+ def load(self, filepath:str) -> None:
42
+ # treat as a filepath containing one or more filter patterns
43
+ lines:list[str] = []
44
+ if filepath == 'stdin':
45
+ ready, _, _ = select.select([sys.stdin], [], [], 5)
46
+ if not ready:
47
+ print('No data from stdin, aborting.')
48
+ sys.exit(7)
49
+ lines = sys.stdin.read().splitlines()
50
+ if len(lines) == 0:
51
+ print('No filters from stdin, aborting.')
52
+ sys.exit(6)
53
+ else:
54
+ if not os.path.exists(filepath):
55
+ print(f'file missing or not accessible: {filepath}')
56
+ sys.exit(5)
57
+ with open(filepath, 'rb') as f:
58
+ lines = f.read().decode().splitlines()
59
+ for line in lines:
60
+ line = line.split('#')[0].strip() # strip comments and prefix/postfix whitespace
61
+ if len(line) == 0:
62
+ # comments and empty lines
63
+ continue
64
+ self.__filters.append(Filter(line))
65
+
66
+ def print(self) -> None:
67
+ if len(FilterManager.instance().filters) > 0:
68
+ print('Filters:')
69
+ for filter in self.filters:
70
+ print(f'\t{filter.pattern}')
@@ -0,0 +1,11 @@
1
+ # SPDX-FileCopyrightText: © 2026 Shaun Wilson
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ from .Filter import Filter
5
+ from .FilterManager import FilterManager
6
+
7
+
8
+ __all__ = [
9
+ 'Filter',
10
+ 'FilterManager'
11
+ ]
@@ -5,6 +5,7 @@
5
5
  import re
6
6
  from typing import Callable, Optional
7
7
 
8
+ from ..filters.FilterManager import FilterManager
8
9
  from ..traits.Trait import Trait
9
10
  from .Theory import Theory
10
11
 
@@ -12,17 +13,15 @@ from .Theory import Theory
12
13
  class TheoryManager:
13
14
 
14
15
  __excludeTraits:list[Trait]
15
- __filterPatterns:Optional[list[re.Pattern]]
16
16
  __includeTraits:list[Trait]
17
17
  __instance:Optional['TheoryManager'] = None
18
18
  __modules:dict[str, list[Theory]]
19
19
  __datas:dict[Callable, list[tuple]]
20
20
  __traits:dict[Callable, list[Trait]]
21
21
 
22
- def __init__(self):
22
+ def __init__(self) -> None:
23
23
  if TheoryManager.__instance is not None:
24
24
  raise Exception('Cannot create more than one instance of TheoryManager') # pragma: no cover
25
- self.__filterPatterns = None
26
25
  self.__modules = {}
27
26
  self.__datas = {}
28
27
  self.__traits = {}
@@ -41,14 +40,6 @@ class TheoryManager:
41
40
  def excludeTraits(self, value:list[Trait]) -> None:
42
41
  self.__excludeTraits = value
43
42
 
44
- @property
45
- def filterPatterns(self) -> Optional[list[re.Pattern]]:
46
- return self.__filterPatterns
47
-
48
- @filterPatterns.setter
49
- def filterPatterns(self, value:list[re.Pattern]) -> None:
50
- self.__filterPatterns = value
51
-
52
43
  @property
53
44
  def includeTraits(self) -> list[Trait]:
54
45
  return [] if self.__includeTraits is None else self.__includeTraits
@@ -79,13 +70,13 @@ class TheoryManager:
79
70
  return l
80
71
 
81
72
  def put(self, theory:Theory) -> None:
82
- matchesFilterPattern:bool = self.filterPatterns is None
83
- if self.filterPatterns is not None and len(self.filterPatterns) > 0:
84
- for filterPattern in self.filterPatterns:
85
- if len(filterPattern.findall(theory.filterName)) > 0:
86
- matchesFilterPattern = True
87
- break
88
- if matchesFilterPattern:
73
+ filters = FilterManager.instance().filters
74
+ matches_filter:bool = False
75
+ for filter in filters:
76
+ if filter.re.fullmatch(theory.filterName) is not None:
77
+ matches_filter = not filter.isExclude
78
+ break
79
+ if matches_filter:
89
80
  l = self.get(theory.moduleName)
90
81
  d = self.__datas.get(theory.target)
91
82
  if d is not None:
@@ -100,7 +91,7 @@ class TheoryManager:
100
91
  l.append(theory)
101
92
 
102
93
  def withData(self, target:Callable, data:tuple) -> None:
103
- # TODO: data acquisition should be deferred until put() since that is where filterPattern
94
+ # TODO: data acquisition should be deferred until put() since that is where `Filter` logic
104
95
  # is applied, but for current implementation `@inlinedata()` is not affected. more advanced
105
96
  # data decorators may benefit from deferral (for example, data coming from an API or DB.)
106
97
  d = self.__datas.get(target)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes