pUnit 1.7.2__tar.gz → 1.7.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.
Files changed (48) hide show
  1. {punit-1.7.2/src/pUnit.egg-info → punit-1.7.3}/PKG-INFO +1 -1
  2. {punit-1.7.2 → punit-1.7.3}/pyproject.toml +1 -1
  3. {punit-1.7.2 → punit-1.7.3/src/pUnit.egg-info}/PKG-INFO +1 -1
  4. {punit-1.7.2 → punit-1.7.3}/src/punit/TestResult.py +17 -1
  5. {punit-1.7.2 → punit-1.7.3}/src/punit/__init__.py +2 -2
  6. {punit-1.7.2 → punit-1.7.3}/src/punit/__main__.py +14 -14
  7. {punit-1.7.2 → punit-1.7.3}/src/punit/cli.py +26 -7
  8. {punit-1.7.2 → punit-1.7.3}/src/punit/filters/Filter.py +13 -2
  9. {punit-1.7.2 → punit-1.7.3}/src/punit/reports/JUnitReportGenerator.py +2 -2
  10. {punit-1.7.2 → punit-1.7.3}/src/punit/runner.py +7 -6
  11. {punit-1.7.2 → punit-1.7.3}/src/punit/theories/Theory.py +17 -1
  12. {punit-1.7.2 → punit-1.7.3}/LICENSE +0 -0
  13. {punit-1.7.2 → punit-1.7.3}/README.md +0 -0
  14. {punit-1.7.2 → punit-1.7.3}/setup.cfg +0 -0
  15. {punit-1.7.2 → punit-1.7.3}/src/pUnit.egg-info/SOURCES.txt +0 -0
  16. {punit-1.7.2 → punit-1.7.3}/src/pUnit.egg-info/dependency_links.txt +0 -0
  17. {punit-1.7.2 → punit-1.7.3}/src/pUnit.egg-info/entry_points.txt +0 -0
  18. {punit-1.7.2 → punit-1.7.3}/src/pUnit.egg-info/requires.txt +0 -0
  19. {punit-1.7.2 → punit-1.7.3}/src/pUnit.egg-info/top_level.txt +0 -0
  20. {punit-1.7.2 → punit-1.7.3}/src/punit/assertions/__init__.py +0 -0
  21. {punit-1.7.2 → punit-1.7.3}/src/punit/assertions/collections.py +0 -0
  22. {punit-1.7.2 → punit-1.7.3}/src/punit/assertions/exceptions.py +0 -0
  23. {punit-1.7.2 → punit-1.7.3}/src/punit/assertions/numeric.py +0 -0
  24. {punit-1.7.2 → punit-1.7.3}/src/punit/assertions/strings.py +0 -0
  25. {punit-1.7.2 → punit-1.7.3}/src/punit/discovery/TestModuleDiscovery.py +0 -0
  26. {punit-1.7.2 → punit-1.7.3}/src/punit/discovery/__init__.py +0 -0
  27. {punit-1.7.2 → punit-1.7.3}/src/punit/facts/Fact.py +0 -0
  28. {punit-1.7.2 → punit-1.7.3}/src/punit/facts/FactManager.py +0 -0
  29. {punit-1.7.2 → punit-1.7.3}/src/punit/facts/__init__.py +0 -0
  30. {punit-1.7.2 → punit-1.7.3}/src/punit/filters/FilterManager.py +0 -0
  31. {punit-1.7.2 → punit-1.7.3}/src/punit/filters/__init__.py +0 -0
  32. {punit-1.7.2 → punit-1.7.3}/src/punit/metadata/CallableMetadata.py +0 -0
  33. {punit-1.7.2 → punit-1.7.3}/src/punit/metadata/__init__.py +0 -0
  34. {punit-1.7.2 → punit-1.7.3}/src/punit/py.typed +0 -0
  35. {punit-1.7.2 → punit-1.7.3}/src/punit/reports/HtmlReportGenerator.py +0 -0
  36. {punit-1.7.2 → punit-1.7.3}/src/punit/reports/JsonReportGenerator.py +0 -0
  37. {punit-1.7.2 → punit-1.7.3}/src/punit/reports/__init__.py +0 -0
  38. {punit-1.7.2 → punit-1.7.3}/src/punit/setups/Setup.py +0 -0
  39. {punit-1.7.2 → punit-1.7.3}/src/punit/setups/SetupManager.py +0 -0
  40. {punit-1.7.2 → punit-1.7.3}/src/punit/setups/__init__.py +0 -0
  41. {punit-1.7.2 → punit-1.7.3}/src/punit/teardowns/Teardown.py +0 -0
  42. {punit-1.7.2 → punit-1.7.3}/src/punit/teardowns/TeardownManager.py +0 -0
  43. {punit-1.7.2 → punit-1.7.3}/src/punit/teardowns/__init__.py +0 -0
  44. {punit-1.7.2 → punit-1.7.3}/src/punit/theories/TheoryManager.py +0 -0
  45. {punit-1.7.2 → punit-1.7.3}/src/punit/theories/__init__.py +0 -0
  46. {punit-1.7.2 → punit-1.7.3}/src/punit/traits/Trait.py +0 -0
  47. {punit-1.7.2 → punit-1.7.3}/src/punit/traits/TraitManager.py +0 -0
  48. {punit-1.7.2 → punit-1.7.3}/src/punit/traits/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pUnit
3
- Version: 1.7.2
3
+ Version: 1.7.3
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.7.2"
3
+ version = "1.7.3"
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.7.2
3
+ Version: 1.7.3
4
4
  Summary: A modernized unit-test framework for Python.
5
5
  Author-email: Shaun Wilson <mrshaunwilson@msn.com>
6
6
  License: MIT License
@@ -27,6 +27,22 @@ class TextIOCapture:
27
27
 
28
28
 
29
29
  class TestResult:
30
+ """
31
+ Accumulates the outcome of a single test execution.
32
+
33
+ Example
34
+ -------
35
+
36
+ Create and populate a result manually:
37
+
38
+ .. code-block:: python
39
+
40
+ result = TestResult()
41
+ result.module_name = 'tests.example'
42
+ result.test_name = 'test_something'
43
+ result.is_success = True
44
+
45
+ """
30
46
 
31
47
  __class_name: Optional[str]
32
48
  __exception: Exception | None
@@ -175,7 +191,7 @@ class TestResult:
175
191
  elif took >= 0.000000001:
176
192
  return f'{(took*1000000):.3f}'.rstrip('0').rstrip('.') + 'μs'
177
193
  else:
178
- return f'{(took*1000):.3f}'.rstrip('0').rstrip('.') + 'ms'
194
+ return f'{(took*1000000):.3f}'.rstrip('0').rstrip('.') + 'ns'
179
195
 
180
196
  def capture_output(self, quiet: bool = False) -> None:
181
197
  self.__stdout_capture = TextIOCapture(sys.stdout, quiet)
@@ -12,8 +12,8 @@ from .assertions.exceptions import raises
12
12
  from .assertions.numeric import approx
13
13
 
14
14
 
15
- __version__ = '1.7.2'
16
- __commit__ = 'b60afab'
15
+ __version__ = '1.7.3'
16
+ __commit__ = 'a403690'
17
17
  __all__ = [
18
18
  '__version__', '__commit__',
19
19
  'assertions',
@@ -16,7 +16,7 @@ from .teardowns.TeardownManager import TeardownManager
16
16
 
17
17
 
18
18
  async def async_main() -> None:
19
- ts = time.time()
19
+ start_time = time.time()
20
20
  cli = CommandLineInterface.parse()
21
21
  if cli.help: # pragma: no cover
22
22
  cli.print_help()
@@ -26,27 +26,27 @@ async def async_main() -> None:
26
26
  cli.print_version()
27
27
  os.chdir(cli.workdir)
28
28
  if cli.no_pathfix is not True:
29
- pathbase = str(Path.cwd())
30
- srcbase = os.path.join(pathbase, 'src')
31
- if os.path.exists(srcbase) and srcbase not in sys.path:
32
- sys.path.append(srcbase)
33
- if pathbase not in sys.path:
34
- sys.path.append(pathbase)
29
+ cwd_path = str(Path.cwd())
30
+ src_path = os.path.join(cwd_path, 'src')
31
+ if os.path.exists(src_path) and src_path not in sys.path:
32
+ sys.path.append(src_path)
33
+ if cwd_path not in sys.path:
34
+ sys.path.append(cwd_path)
35
35
  test_module_discovery = TestModuleDiscovery(
36
36
  os.path.join(cli.workdir, cli.test_package_name),
37
37
  cli.includePatterns,
38
38
  cli.excludePatterns,
39
39
  cli)
40
40
  test_module_discovery.discover()
41
- testRunner = TestRunner(cli.test_package_name, test_module_discovery.filenames, cli)
42
- results = await testRunner.run()
43
- totalTime = time.time() - ts
44
- failureCount = 0
41
+ test_runner = TestRunner(cli.test_package_name, test_module_discovery.filenames, cli)
42
+ results = await test_runner.run()
43
+ total_time = time.time() - start_time
44
+ failure_count = 0
45
45
  for result in results:
46
46
  if not result.is_success:
47
- failureCount += 1 # pragma: no cover
47
+ failure_count += 1 # pragma: no cover
48
48
  if not cli.quiet:
49
- print(f'Total: {len(results)}, Failures: {failureCount}, Took: {totalTime:.3f}s')
49
+ print(f'Total: {len(results)}, Failures: {failure_count}, Took: {total_time:.3f}s')
50
50
  if cli.reportFormat is not None: # pragma: no cover
51
51
  report: str = ''
52
52
  match cli.reportFormat:
@@ -74,7 +74,7 @@ async def async_main() -> None:
74
74
  pass
75
75
 
76
76
  if cli.no_exitcode is not True:
77
- if failureCount:
77
+ if failure_count:
78
78
  # test failures trigger exit code 119 (for automation gating)
79
79
  sys.exit(119)
80
80
  if TeardownManager.instance().teardown_error_count > 0:
@@ -12,6 +12,20 @@ from .traits import Trait
12
12
 
13
13
 
14
14
  class CommandLineInterface:
15
+ """
16
+ Command-line argument parser and display helper for pUnit.
17
+
18
+ Example
19
+ -------
20
+
21
+ Parse CLI arguments from ``sys.argv``:
22
+
23
+ .. code-block:: python
24
+
25
+ cli = CommandLineInterface.parse()
26
+ print(cli.test_package_name)
27
+
28
+ """
15
29
 
16
30
  __aliases: dict[str, str]
17
31
  __excludePatterns: list[str]
@@ -49,6 +63,12 @@ class CommandLineInterface:
49
63
  self.__verbose = False
50
64
 
51
65
  def __parse(self, argv: list[str]) -> CommandLineInterface:
66
+ """
67
+ Parse command-line arguments using a finite-state machine.
68
+
69
+ :param argv: List of argument strings (typically ``sys.argv``).
70
+ :returns: ``self`` for method chaining.
71
+ """
52
72
  aliasName: str | None = None
53
73
  extractFilter: bool = False
54
74
  extractExcludePattern: bool = False
@@ -160,7 +180,7 @@ class CommandLineInterface:
160
180
  FilterManager.instance().add(arg)
161
181
 
162
182
  @property
163
- def aliases(self) -> dict:
183
+ def aliases(self) -> dict[str, str]:
164
184
  return self.__aliases
165
185
 
166
186
  @property
@@ -213,16 +233,15 @@ class CommandLineInterface:
213
233
 
214
234
  @property
215
235
  def no_exitcode(self) -> bool:
216
- return self.__no_exitcode is True
236
+ return self.__no_exitcode
217
237
 
218
238
  @property
219
239
  def no_pathfix(self) -> bool:
220
- return self.__no_pathfix is True
240
+ return self.__no_pathfix
221
241
 
222
242
  def print_help(self) -> None:
223
- if True: # pragma: no cover
224
- self.print_version()
225
- print("""
243
+ self.print_version()
244
+ print("""
226
245
  Usage: python3 -m punit [-h|--help]
227
246
  [-q|--quiet] [-v|--verbose]
228
247
  [-z|--failfast]
@@ -281,7 +300,7 @@ Options:
281
300
  write to FILENAME. In this case `--report` does not
282
301
  suppress any program output.
283
302
  """)
284
- os._exit(0)
303
+ os._exit(0)
285
304
 
286
305
  def print_summary(self) -> None:
287
306
  self.print_version()
@@ -5,17 +5,28 @@ import re
5
5
 
6
6
 
7
7
  class Filter:
8
+ """
9
+ Wraps a glob-style filter pattern and compiles it to a case-insensitive regex.
10
+
11
+ Patterns use ``*`` for multi-character wildcards and ``?`` for single characters.
12
+ Prefix with ``!`` to mark the filter as an exclusion rule.
13
+ """
8
14
 
9
15
  __isExclude: bool
10
16
  __pattern: str
11
- __re: re.Pattern
17
+ __re: re.Pattern[str]
12
18
 
13
19
  def __init__(self, pattern: str) -> None:
14
20
  self.__isExclude = pattern.startswith('!')
15
21
  self.__pattern = pattern
16
22
  self.__re = self.__toRegex(pattern)
17
23
 
18
- def __toRegex(self, pattern: str) -> re.Pattern:
24
+ def __toRegex(self, pattern: str) -> re.Pattern[str]:
25
+ """Convert a glob-style pattern to a compiled regex.
26
+
27
+ Supports ``*`` (multi-char wildcard), ``?`` (single-char wildcard),
28
+ and ``!`` prefix (exclusion). Matching is case-insensitive.
29
+ """
19
30
  if self.__isExclude:
20
31
  pattern = pattern[1:]
21
32
  pattern = re.escape(pattern)\
@@ -65,7 +65,7 @@ class JUnitTestSuite:
65
65
  result: int = 0
66
66
  if self.testCases is not None:
67
67
  for testCase in self.testCases:
68
- if testCase.disabled == True:
68
+ if testCase.disabled is True:
69
69
  result += 1
70
70
  return result
71
71
 
@@ -187,7 +187,7 @@ class JUnitReportGenerator:
187
187
  ele.text = testCase.stderr
188
188
  testSuitesEle.attrib['disabled'] = str(totalDisabledCount)
189
189
  testSuitesEle.attrib['errors'] = str(totalErrorCount)
190
- testSuitesEle.attrib['failures'] = str(totalErrorCount)
190
+ testSuitesEle.attrib['failures'] = str(totalFailureCount)
191
191
  testSuitesEle.attrib['tests'] = str(totalTestCount)
192
192
  testSuitesEle.attrib['time'] = f'{totalTime:.6f}'.rstrip('0').rstrip('.')
193
193
  et.indent(testSuitesEle, space=" ")
@@ -113,7 +113,8 @@ class TestRunner:
113
113
  )
114
114
  print(f'Teardown Error ({target_desc}): {ex}')
115
115
 
116
- def print_test_result(self, test_result: TestResult):
116
+ def print_test_result(self, test_result: TestResult) -> None:
117
+ """Print the test result using a coloured emoji and timing."""
117
118
  if self.__cli.quiet:
118
119
  return
119
120
  glyph = '🟩' if test_result.is_success else '🟥'
@@ -204,13 +205,13 @@ class TestRunner:
204
205
  test_package_path = os.path.join(os.path.abspath(os.curdir), self.__test_package_name).replace('\\', '/')
205
206
  for filename in self.__filenames:
206
207
  ts = time.time()
207
- moduleImportName = filename.replace(test_package_path, '').replace('/', '.')
208
- if moduleImportName.endswith('.py'):
209
- moduleImportName = moduleImportName[:-3]
210
- module_report_name = moduleImportName.lstrip('.')
208
+ module_import_name = filename.replace(test_package_path, '').replace('/', '.')
209
+ if module_import_name.endswith('.py'):
210
+ module_import_name = module_import_name[:-3]
211
+ module_report_name = module_import_name.lstrip('.')
211
212
  try:
212
213
  host_name: str = socket.gethostname()
213
- test_module = importlib.import_module(moduleImportName, self.__test_package_name)
214
+ test_module = importlib.import_module(module_import_name, self.__test_package_name)
214
215
 
215
216
  # execute all facts
216
217
  await self.__run_facts(host_name, test_module, filename, module_report_name, results)
@@ -10,6 +10,23 @@ from ..metadata import CallableMetadata
10
10
 
11
11
 
12
12
  class Theory:
13
+ """
14
+ Wraps a parameterised test (``@theory``) and its collected data points.
15
+
16
+ Example
17
+ -------
18
+
19
+ .. code-block:: python
20
+
21
+ @theory
22
+ def add_correct(x, y): ...
23
+
24
+ @inlinedata((1, 2), (3, 4))
25
+ @theory
26
+ def add_correct(x, y):
27
+ assert x + y == x + y
28
+
29
+ """
13
30
 
14
31
  __datas: list[tuple]
15
32
  __target: Union[FunctionType, MethodType, BuiltinFunctionType, BuiltinMethodType, Callable]
@@ -52,7 +69,6 @@ class Theory:
52
69
  args = (class_instance,) + data
53
70
  coro = self.__target(*args)
54
71
  else:
55
- self.__test_name = self.__target.__name__
56
72
  coro = self.__target(*data)
57
73
  if inspect.iscoroutine(coro):
58
74
  await coro
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes