punit 1.4.0__tar.gz → 1.4.4__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 (43) hide show
  1. punit-1.4.4/.scripts/punit +9 -0
  2. {punit-1.4.0/src/pUnit.egg-info → punit-1.4.4}/PKG-INFO +2 -5
  3. {punit-1.4.0 → punit-1.4.4}/pyproject.toml +2 -5
  4. {punit-1.4.0 → punit-1.4.4/src/pUnit.egg-info}/PKG-INFO +2 -5
  5. {punit-1.4.0 → punit-1.4.4}/src/punit/__init__.py +2 -2
  6. {punit-1.4.0 → punit-1.4.4}/src/punit/__main__.py +18 -7
  7. punit-1.4.4/src/punit/assertions/collections.py +76 -0
  8. {punit-1.4.0 → punit-1.4.4}/src/punit/cli.py +4 -4
  9. {punit-1.4.0 → punit-1.4.4}/src/punit/filters/FilterManager.py +3 -3
  10. punit-1.4.0/.scripts/punit +0 -8
  11. punit-1.4.0/src/punit/assertions/collections.py +0 -76
  12. {punit-1.4.0 → punit-1.4.4}/LICENSE +0 -0
  13. {punit-1.4.0 → punit-1.4.4}/README.md +0 -0
  14. {punit-1.4.0 → punit-1.4.4}/setup.cfg +0 -0
  15. {punit-1.4.0 → punit-1.4.4}/src/pUnit.egg-info/SOURCES.txt +0 -0
  16. {punit-1.4.0 → punit-1.4.4}/src/pUnit.egg-info/dependency_links.txt +0 -0
  17. {punit-1.4.0 → punit-1.4.4}/src/pUnit.egg-info/requires.txt +0 -0
  18. {punit-1.4.0 → punit-1.4.4}/src/pUnit.egg-info/top_level.txt +0 -0
  19. {punit-1.4.0 → punit-1.4.4}/src/punit/TestResult.py +0 -0
  20. {punit-1.4.0 → punit-1.4.4}/src/punit/assertions/__init__.py +0 -0
  21. {punit-1.4.0 → punit-1.4.4}/src/punit/assertions/exceptions.py +0 -0
  22. {punit-1.4.0 → punit-1.4.4}/src/punit/assertions/strings.py +0 -0
  23. {punit-1.4.0 → punit-1.4.4}/src/punit/discovery/TestModuleDiscovery.py +0 -0
  24. {punit-1.4.0 → punit-1.4.4}/src/punit/discovery/__init__.py +0 -0
  25. {punit-1.4.0 → punit-1.4.4}/src/punit/facts/Fact.py +0 -0
  26. {punit-1.4.0 → punit-1.4.4}/src/punit/facts/FactManager.py +0 -0
  27. {punit-1.4.0 → punit-1.4.4}/src/punit/facts/__init__.py +0 -0
  28. {punit-1.4.0 → punit-1.4.4}/src/punit/filters/Filter.py +0 -0
  29. {punit-1.4.0 → punit-1.4.4}/src/punit/filters/__init__.py +0 -0
  30. {punit-1.4.0 → punit-1.4.4}/src/punit/metadata/CallableMetadata.py +0 -0
  31. {punit-1.4.0 → punit-1.4.4}/src/punit/metadata/__init__.py +0 -0
  32. {punit-1.4.0 → punit-1.4.4}/src/punit/py.typed +0 -0
  33. {punit-1.4.0 → punit-1.4.4}/src/punit/reports/HtmlReportGenerator.py +0 -0
  34. {punit-1.4.0 → punit-1.4.4}/src/punit/reports/JUnitReportGenerator.py +0 -0
  35. {punit-1.4.0 → punit-1.4.4}/src/punit/reports/JsonReportGenerator.py +0 -0
  36. {punit-1.4.0 → punit-1.4.4}/src/punit/reports/__init__.py +0 -0
  37. {punit-1.4.0 → punit-1.4.4}/src/punit/runner.py +0 -0
  38. {punit-1.4.0 → punit-1.4.4}/src/punit/theories/Theory.py +0 -0
  39. {punit-1.4.0 → punit-1.4.4}/src/punit/theories/TheoryManager.py +0 -0
  40. {punit-1.4.0 → punit-1.4.4}/src/punit/theories/__init__.py +0 -0
  41. {punit-1.4.0 → punit-1.4.4}/src/punit/traits/Trait.py +0 -0
  42. {punit-1.4.0 → punit-1.4.4}/src/punit/traits/TraitManager.py +0 -0
  43. {punit-1.4.0 → punit-1.4.4}/src/punit/traits/__init__.py +0 -0
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env python
2
+ # SPDX-FileCopyrightText: © 2026 Shaun Wilson
3
+ # SPDX-License-Identifier: MIT
4
+ ##
5
+ from subprocess import run
6
+ from sys import executable, argv
7
+ from os import _exit
8
+ result = run([executable, "-m", "punit"] + argv[1:], check=False)
9
+ _exit(result.returncode)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pUnit
3
- Version: 1.4.0
3
+ Version: 1.4.4
4
4
  Summary: A modernized unit-test framework for Python.
5
5
  Author-email: Shaun Wilson <mrshaunwilson@msn.com>
6
6
  License: MIT License
@@ -30,10 +30,7 @@ Project-URL: Homepage, https://github.com/wilson0x4d/punit
30
30
  Project-URL: Repository, https://github.com/wilson0x4d/punit.git
31
31
  Keywords: test,unittest,unit-test,xUnit,nUnit,pytest
32
32
  Classifier: Operating System :: OS Independent
33
- Classifier: Programming Language :: Python :: 3.11
34
- Classifier: Programming Language :: Python :: 3.12
35
- Classifier: Programming Language :: Python :: 3.13
36
- Classifier: Programming Language :: Python :: 3.14
33
+ Classifier: Programming Language :: Python
37
34
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
38
35
  Classifier: Intended Audience :: Developers
39
36
  Requires-Python: >=3.11
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pUnit"
3
- version = "1.4.0"
3
+ version = "1.4.4"
4
4
  description = "A modernized unit-test framework for Python."
5
5
  keywords = ["test", "unittest", "unit-test", "xUnit", "nUnit", "pytest"]
6
6
  authors = [
@@ -11,10 +11,7 @@ license = {file = "LICENSE"}
11
11
  requires-python = ">=3.11"
12
12
  classifiers = [
13
13
  "Operating System :: OS Independent",
14
- "Programming Language :: Python :: 3.11",
15
- "Programming Language :: Python :: 3.12",
16
- "Programming Language :: Python :: 3.13",
17
- "Programming Language :: Python :: 3.14",
14
+ "Programming Language :: Python",
18
15
  "Topic :: Software Development :: Libraries :: Python Modules",
19
16
  "Intended Audience :: Developers"
20
17
  ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pUnit
3
- Version: 1.4.0
3
+ Version: 1.4.4
4
4
  Summary: A modernized unit-test framework for Python.
5
5
  Author-email: Shaun Wilson <mrshaunwilson@msn.com>
6
6
  License: MIT License
@@ -30,10 +30,7 @@ Project-URL: Homepage, https://github.com/wilson0x4d/punit
30
30
  Project-URL: Repository, https://github.com/wilson0x4d/punit.git
31
31
  Keywords: test,unittest,unit-test,xUnit,nUnit,pytest
32
32
  Classifier: Operating System :: OS Independent
33
- Classifier: Programming Language :: Python :: 3.11
34
- Classifier: Programming Language :: Python :: 3.12
35
- Classifier: Programming Language :: Python :: 3.13
36
- Classifier: Programming Language :: Python :: 3.14
33
+ Classifier: Programming Language :: Python
37
34
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
38
35
  Classifier: Intended Audience :: Developers
39
36
  Requires-Python: >=3.11
@@ -7,8 +7,8 @@ from .theories import *
7
7
  from .traits import *
8
8
 
9
9
 
10
- __version__ = '1.4.0'
11
- __commit__ = 'e1fa8f0'
10
+ __version__ = '1.4.4'
11
+ __commit__ = '9f70be4'
12
12
  __all__ = [
13
13
  '__version__', '__commit__',
14
14
  'assertions',
@@ -3,6 +3,7 @@
3
3
 
4
4
  import asyncio
5
5
  import os
6
+ import sys
6
7
  import time
7
8
  from .cli import CommandLineInterface
8
9
  from .discovery import TestModuleDiscovery
@@ -13,11 +14,11 @@ from .runner import TestRunner
13
14
  async def async_main() -> None:
14
15
  ts = time.time()
15
16
  cli = CommandLineInterface.parse()
16
- if cli.help: # pragma: no cover
17
+ if cli.help: # pragma: no cover
17
18
  cli.printHelp()
18
- elif cli.verbose and not cli.quiet: # pragma: no cover
19
+ elif cli.verbose and not cli.quiet: # pragma: no cover
19
20
  cli.printSummary()
20
- elif not cli.quiet: # pragma: no cover
21
+ elif not cli.quiet: # pragma: no cover
21
22
  cli.printVersion()
22
23
  os.chdir(cli.workdir)
23
24
  testModuleDiscovery = TestModuleDiscovery(
@@ -32,11 +33,11 @@ async def async_main() -> None:
32
33
  failureCount = 0
33
34
  for result in results:
34
35
  if not result.isSuccess:
35
- failureCount += 1 # pragma: no cover
36
+ failureCount += 1 # pragma: no cover
36
37
  if not cli.quiet:
37
38
  print(f'Total: {len(results)}, Failures: {failureCount}, Took: {totalTime:.3f}s')
38
- if cli.reportFormat is not None: # pragma: no cover
39
- report:str = ''
39
+ if cli.reportFormat is not None: # pragma: no cover
40
+ report: str = ''
40
41
  match cli.reportFormat:
41
42
  case 'html':
42
43
  report = HtmlReportGenerator().generate(results)
@@ -52,10 +53,20 @@ async def async_main() -> None:
52
53
  file.write(report.encode())
53
54
  print(f'\n("{cli.reportFormat}" report written to: {cli.outputFilename})')
54
55
  if failureCount > 0:
55
- exit(119) # pragma: no cover
56
+ sys.stdout.flush()
57
+ sys.stderr.flush()
58
+ try:
59
+ os.fsync(sys.stdout.fileno())
60
+ os.fsync(sys.stderr.fileno())
61
+ except (AttributeError, ValueError, OSError):
62
+ # Pass if the stream doesn't support fsync (e.g. some virtualized environments)
63
+ pass
64
+ os._exit(119) # pragma: no cover
65
+
56
66
 
57
67
  def main() -> None:
58
68
  asyncio.run(async_main())
59
69
 
70
+
60
71
  if (__name__ == '__main__'):
61
72
  main()
@@ -0,0 +1,76 @@
1
+ # SPDX-FileCopyrightText: © 2024 Shaun Wilson
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ from typing import Any, Callable, Optional, Sequence, cast
5
+
6
+
7
+ def areSame(actual: Sequence[Any] | set[Any] | dict[Any, Any] | None, expected: Sequence[Any] | set[Any] | dict[Any, Any] | None, sort: bool = False, sort_function: Optional[Callable[[Any], Any]] = None) -> bool:
8
+ """
9
+ Check if two sequences contain the same elements in the same order.
10
+
11
+ :param actual: The sequence to check
12
+ :param expected: The sequence to compare against
13
+ :param sort: Sort sequences before performing comparisons.
14
+ :param sort_function: Custom function to use when sorting.
15
+ :returns bool: True if the sequences contain the same elements in the same order, False otherwise.
16
+ """
17
+ if actual is expected:
18
+ return True
19
+ elif actual is None and expected is not None:
20
+ return False
21
+ elif actual is not None and expected is None:
22
+ return False
23
+ elif actual is not None and expected is not None:
24
+ if len(actual) != len(expected):
25
+ return False
26
+
27
+ if sort:
28
+ if isinstance(actual, dict) or isinstance(expected, dict):
29
+ sort_function = sort_function if sort_function is not None else lambda e: e[0]
30
+ actual = sorted(cast(dict, actual).items(), key=sort_function)
31
+ expected = sorted(cast(dict, expected).items(), key=sort_function)
32
+ else:
33
+ sort_function = sort_function if sort_function is not None else lambda e: e
34
+ actual = sorted(actual, key=sort_function)
35
+ expected = sorted(expected, key=sort_function)
36
+
37
+ if isinstance(actual, dict) or isinstance(expected, dict):
38
+ for pairs in zip(cast(dict, actual).items(), cast(dict, expected).items()):
39
+ if not areSame(pairs[0], pairs[1]):
40
+ return False
41
+ else:
42
+ for pairs in zip(actual, expected):
43
+ if isinstance(pairs[0], dict) or isinstance(pairs[1], dict):
44
+ if not areSame(pairs[0], pairs[1]):
45
+ return False
46
+ elif pairs[0] != pairs[1]:
47
+ return False
48
+ return True
49
+
50
+ def hasLength(actual: Sequence[Any] | set[Any] | dict[Any, Any] | None, expected: int | None) -> bool:
51
+ """
52
+ Check if actual value has the expected number of elements.
53
+
54
+ :param actual: The actual value to check.
55
+ :param int|None expected: The expected number of elements
56
+
57
+ :returns bool: True if the sequence has exactly the expected number of elements, False otherwise
58
+ """
59
+ if actual is None and (expected is None or expected == 0):
60
+ return True
61
+ elif actual is None and (expected is not None and expected != 0):
62
+ return False
63
+ elif actual is not None and expected is None:
64
+ return False
65
+ return actual is not None and len(actual) == expected
66
+
67
+ def isNoneOrEmpty(actual: Sequence[Any] | set[Any] | dict[Any, Any] | None) -> bool:
68
+ """
69
+ Check if actual value is None or empty.
70
+
71
+ :param actual: The actual to check.
72
+ :returns: True if the value is None or empty, False otherwise.
73
+ """
74
+ if actual is None:
75
+ return True
76
+ return len(actual) == 0
@@ -100,7 +100,7 @@ class CommandLineInterface:
100
100
  case _:
101
101
  print(f'Unsupported value "{arg}" for --report argument, aborting.')
102
102
  print(f'(valid values are "json" and "junit")')
103
- exit(4)
103
+ os._exit(4)
104
104
  continue
105
105
  elif extractOutputFilename:
106
106
  extractOutputFilename = False
@@ -260,7 +260,7 @@ Options:
260
260
  suppress any program output.
261
261
  """
262
262
  )
263
- exit(0)
263
+ os._exit(0)
264
264
 
265
265
  def printSummary(self) -> None:
266
266
  self.printVersion()
@@ -282,10 +282,10 @@ Options:
282
282
  def validate(self) -> None:
283
283
  if self.__workdir is None or len(self.__workdir.lstrip()) == 0 or self.__workdir.startswith('-'):
284
284
  print(f'Invalid working directory specified: {self.__workdir}')
285
- exit(1)
285
+ os._exit(1)
286
286
  elif not os.path.isdir(self.__workdir):
287
287
  print(f'Working directory does not exist: {self.__workdir}')
288
- exit(2)
288
+ os._exit(2)
289
289
  self.__workdir = os.path.abspath(self.__workdir)
290
290
  if not self.__no_default_patterns:
291
291
  # if no other patterns specified, default to including all files found in the directory matching `testPackageName`
@@ -45,15 +45,15 @@ class FilterManager:
45
45
  ready, _, _ = select.select([sys.stdin], [], [], 5)
46
46
  if not ready:
47
47
  print('No data from stdin, aborting.')
48
- sys.exit(7)
48
+ os._exit(7)
49
49
  lines = sys.stdin.read().splitlines()
50
50
  if len(lines) == 0:
51
51
  print('No filters from stdin, aborting.')
52
- sys.exit(6)
52
+ os._exit(6)
53
53
  else:
54
54
  if not os.path.exists(filepath):
55
55
  print(f'file missing or not accessible: {filepath}')
56
- sys.exit(5)
56
+ os._exit(5)
57
57
  with open(filepath, 'rb') as f:
58
58
  lines = f.read().decode().splitlines()
59
59
  for line in lines:
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env python
2
- # SPDX-FileCopyrightText: © 2026 Shaun Wilson
3
- # SPDX-License-Identifier: MIT
4
- ##
5
- from subprocess import run
6
- from sys import executable, argv, exit
7
- result = run([ executable, "-m", "punit" ] + argv[1:], check=False)
8
- exit(result.returncode)
@@ -1,76 +0,0 @@
1
- # SPDX-FileCopyrightText: © 2024 Shaun Wilson
2
- # SPDX-License-Identifier: MIT
3
-
4
- from typing import Any, Callable, Sequence, Optional, cast
5
-
6
-
7
- def areSame(a:Sequence[Any]|list[Any]|dict[Any,Any]|None, b:Sequence[Any]|list[Any]|dict[Any,Any]|None, sort:bool=False, sortFunction:Optional[Callable[[Any], Any]]=None) -> bool:
8
- """
9
- Check if two sequences contain the same elements in the same order.
10
-
11
- :param Sequence[Any]|None a: The sequence to check
12
- :param Sequence[Any]|None b: The sequence to compare against
13
- :param Optional[bool] sort: Sort sequences before performing comparisons.
14
- :param Optional[Callable[[Any], Any]] sortFunction: Custom function to use when sorting.
15
- :returns bool: True if the sequences contain the same elements in the same order, False otherwise.
16
- """
17
- if a is b:
18
- return True
19
- elif a is None and b is not None:
20
- return False
21
- elif a is not None and b is None:
22
- return False
23
- elif a is not None and b is not None:
24
- if len(a) != len(b):
25
- return False
26
-
27
- if sort:
28
- if isinstance(a, dict) or isinstance(b, dict):
29
- sortFunction = sortFunction if sortFunction is not None else lambda e: e[0]
30
- a = sorted(cast(dict,a).items(), key=sortFunction)
31
- b = sorted(cast(dict,b).items(), key=sortFunction)
32
- else:
33
- sortFunction = sortFunction if sortFunction is not None else lambda e: e
34
- a = sorted(a, key=sortFunction)
35
- b = sorted(b, key=sortFunction)
36
-
37
- if isinstance(a, dict) or isinstance(b, dict):
38
- for pairs in zip(cast(dict,a).items(), cast(dict,b).items()):
39
- if not areSame(pairs[0], pairs[1]):
40
- return False
41
- else:
42
- for pairs in zip(a, b):
43
- if isinstance(pairs[0], dict) or isinstance(pairs[1], dict):
44
- if not areSame(pairs[0], pairs[1]):
45
- return False
46
- elif pairs[0] != pairs[1]:
47
- return False
48
- return True
49
-
50
- def hasLength(sequence:Sequence[Any]|list[Any]|dict[Any,Any]|None, expected:int|None) -> bool:
51
- """
52
- Check if a sequence has the expected number of elements.
53
-
54
- :param Sequence[Any]|None sequence: The sequence to check
55
- :param int|None expected: The expected number of elements
56
-
57
- :returns bool: True if the sequence has exactly the expected number of elements, False otherwise
58
- """
59
- if sequence is None and (expected is None or expected == 0):
60
- return True
61
- elif sequence is None and (expected is not None and expected != 0):
62
- return False
63
- elif sequence is not None and expected is None:
64
- return False
65
- return sequence is not None and len(sequence) == expected
66
-
67
- def isNoneOrEmpty(sequence:Sequence[Any]|list[Any]|dict[Any,Any]|None) -> bool:
68
- """
69
- Check if a sequence is None or empty.
70
-
71
- :param Sequence[Any]|None sequence: The sequence to check
72
- :returns bool: True if the sequence is None or empty, False otherwise
73
- """
74
- if sequence is None:
75
- return True
76
- return len(sequence) == 0
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