winipedia-utils 0.1.0__py3-none-any.whl

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 (64) hide show
  1. winipedia_utils/__init__.py +1 -0
  2. winipedia_utils/concurrent/__init__.py +1 -0
  3. winipedia_utils/concurrent/concurrent.py +242 -0
  4. winipedia_utils/concurrent/multiprocessing.py +115 -0
  5. winipedia_utils/concurrent/multithreading.py +93 -0
  6. winipedia_utils/consts.py +22 -0
  7. winipedia_utils/data/__init__.py +1 -0
  8. winipedia_utils/data/dataframe.py +7 -0
  9. winipedia_utils/django/__init__.py +27 -0
  10. winipedia_utils/django/bulk.py +536 -0
  11. winipedia_utils/django/command.py +334 -0
  12. winipedia_utils/django/database.py +304 -0
  13. winipedia_utils/git/__init__.py +1 -0
  14. winipedia_utils/git/gitignore.py +80 -0
  15. winipedia_utils/git/pre_commit/__init__.py +1 -0
  16. winipedia_utils/git/pre_commit/config.py +60 -0
  17. winipedia_utils/git/pre_commit/hooks.py +109 -0
  18. winipedia_utils/git/pre_commit/run_hooks.py +49 -0
  19. winipedia_utils/iterating/__init__.py +1 -0
  20. winipedia_utils/iterating/iterate.py +29 -0
  21. winipedia_utils/logging/__init__.py +1 -0
  22. winipedia_utils/logging/ansi.py +6 -0
  23. winipedia_utils/logging/config.py +64 -0
  24. winipedia_utils/logging/logger.py +26 -0
  25. winipedia_utils/modules/__init__.py +1 -0
  26. winipedia_utils/modules/class_.py +76 -0
  27. winipedia_utils/modules/function.py +86 -0
  28. winipedia_utils/modules/module.py +361 -0
  29. winipedia_utils/modules/package.py +350 -0
  30. winipedia_utils/oop/__init__.py +1 -0
  31. winipedia_utils/oop/mixins/__init__.py +1 -0
  32. winipedia_utils/oop/mixins/meta.py +315 -0
  33. winipedia_utils/oop/mixins/mixin.py +28 -0
  34. winipedia_utils/os/__init__.py +1 -0
  35. winipedia_utils/os/os.py +61 -0
  36. winipedia_utils/projects/__init__.py +1 -0
  37. winipedia_utils/projects/poetry/__init__.py +1 -0
  38. winipedia_utils/projects/poetry/config.py +91 -0
  39. winipedia_utils/projects/poetry/poetry.py +30 -0
  40. winipedia_utils/setup.py +36 -0
  41. winipedia_utils/testing/__init__.py +1 -0
  42. winipedia_utils/testing/assertions.py +23 -0
  43. winipedia_utils/testing/convention.py +177 -0
  44. winipedia_utils/testing/create_tests.py +286 -0
  45. winipedia_utils/testing/fixtures.py +28 -0
  46. winipedia_utils/testing/tests/__init__.py +1 -0
  47. winipedia_utils/testing/tests/base/__init__.py +1 -0
  48. winipedia_utils/testing/tests/base/fixtures/__init__.py +1 -0
  49. winipedia_utils/testing/tests/base/fixtures/fixture.py +6 -0
  50. winipedia_utils/testing/tests/base/fixtures/scopes/__init__.py +1 -0
  51. winipedia_utils/testing/tests/base/fixtures/scopes/class_.py +33 -0
  52. winipedia_utils/testing/tests/base/fixtures/scopes/function.py +7 -0
  53. winipedia_utils/testing/tests/base/fixtures/scopes/module.py +31 -0
  54. winipedia_utils/testing/tests/base/fixtures/scopes/package.py +7 -0
  55. winipedia_utils/testing/tests/base/fixtures/scopes/session.py +224 -0
  56. winipedia_utils/testing/tests/base/utils/__init__.py +1 -0
  57. winipedia_utils/testing/tests/base/utils/utils.py +82 -0
  58. winipedia_utils/testing/tests/conftest.py +26 -0
  59. winipedia_utils/text/__init__.py +1 -0
  60. winipedia_utils/text/string.py +126 -0
  61. winipedia_utils-0.1.0.dist-info/LICENSE +21 -0
  62. winipedia_utils-0.1.0.dist-info/METADATA +350 -0
  63. winipedia_utils-0.1.0.dist-info/RECORD +64 -0
  64. winipedia_utils-0.1.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,177 @@
1
+ """Testing conventions and utilities.
2
+
3
+ This module provides functions and constants for managing test naming conventions,
4
+ mapping between test objects and their corresponding implementation objects,
5
+ and utilities for test discovery and validation.
6
+
7
+ Returns:
8
+ Various utility functions and constants for testing conventions.
9
+
10
+ """
11
+
12
+ from collections.abc import Callable, Iterable
13
+ from types import ModuleType
14
+ from typing import Any
15
+
16
+ from winipedia_utils.modules.module import (
17
+ get_isolated_obj_name,
18
+ import_obj_from_importpath,
19
+ make_obj_importpath,
20
+ )
21
+
22
+ TEST_FUNCTION_PREFIX = "test_"
23
+
24
+ TEST_CLASS_PREFIX = "Test"
25
+
26
+ TEST_MODULE_PREFIX = TEST_FUNCTION_PREFIX
27
+
28
+ TEST_PREFIXES = [TEST_FUNCTION_PREFIX, TEST_CLASS_PREFIX, TEST_MODULE_PREFIX]
29
+
30
+ TESTS_PACKAGE_NAME = "tests"
31
+
32
+
33
+ def get_right_test_prefix(obj: Callable[..., Any] | type | ModuleType) -> str:
34
+ """Get the appropriate test prefix for an object based on its type.
35
+
36
+ Args:
37
+ obj: The object to get the test prefix for (function, class, or module)
38
+
39
+ Returns:
40
+ The appropriate test prefix string for the given object type
41
+
42
+ """
43
+ if isinstance(obj, ModuleType):
44
+ return TEST_MODULE_PREFIX
45
+ if isinstance(obj, type):
46
+ return TEST_CLASS_PREFIX
47
+ return TEST_FUNCTION_PREFIX
48
+
49
+
50
+ def make_test_obj_name(obj: Callable[..., Any] | type | ModuleType) -> str:
51
+ """Create a test name for an object by adding the appropriate prefix.
52
+
53
+ Args:
54
+ obj: The object to create a test name for
55
+
56
+ Returns:
57
+ The test name with the appropriate prefix
58
+
59
+ """
60
+ prefix = get_right_test_prefix(obj)
61
+ name = get_isolated_obj_name(obj)
62
+ return prefix + name
63
+
64
+
65
+ def reverse_make_test_obj_name(test_name: str) -> str:
66
+ """Extract the original object name from a test name by removing the prefix.
67
+
68
+ Args:
69
+ test_name: The test name to extract the original name from
70
+
71
+ Returns:
72
+ The original object name without the test prefix
73
+
74
+ Raises:
75
+ ValueError: If the test name doesn't start with any of the expected prefixes
76
+
77
+ """
78
+ for prefix in TEST_PREFIXES:
79
+ if test_name.startswith(prefix):
80
+ return test_name.removeprefix(prefix)
81
+ msg = f"{test_name=} is expected to start with one of {TEST_PREFIXES=}"
82
+ raise ValueError(msg)
83
+
84
+
85
+ def make_test_obj_importpath_from_obj(
86
+ obj: Callable[..., Any] | type | ModuleType,
87
+ ) -> str:
88
+ """Create an import path for a test object based on the original object.
89
+
90
+ Args:
91
+ obj: The original object to create a test import path for
92
+
93
+ Returns:
94
+ The import path for the corresponding test object
95
+
96
+ """
97
+ parts = make_obj_importpath(obj).split(".")
98
+ test_name = make_test_obj_name(obj)
99
+ test_parts = [
100
+ (TEST_MODULE_PREFIX if part[0].islower() else TEST_CLASS_PREFIX) + part
101
+ for part in parts
102
+ ]
103
+ test_parts[-1] = test_name
104
+ test_parts.insert(0, TESTS_PACKAGE_NAME)
105
+ return ".".join(test_parts)
106
+
107
+
108
+ def make_obj_importpath_from_test_obj(
109
+ test_obj: Callable[..., Any] | type | ModuleType,
110
+ ) -> str:
111
+ """Create an import path for an original object based on its test object.
112
+
113
+ Args:
114
+ test_obj: The test object to create an original import path for
115
+
116
+ Returns:
117
+ The import path for the corresponding original object
118
+
119
+ """
120
+ test_parts = make_obj_importpath(test_obj).split(".")
121
+ test_parts = test_parts[1:]
122
+ parts = [reverse_make_test_obj_name(part) for part in test_parts]
123
+ return ".".join(parts)
124
+
125
+
126
+ def get_test_obj_from_obj(
127
+ obj: Callable[..., Any] | type | ModuleType,
128
+ ) -> Callable[..., Any] | type | ModuleType:
129
+ """Get the test object corresponding to an original object.
130
+
131
+ Args:
132
+ obj: The original object to get the test object for
133
+
134
+ Returns:
135
+ The corresponding test object
136
+
137
+ """
138
+ test_obj_path = make_test_obj_importpath_from_obj(obj)
139
+ return import_obj_from_importpath(test_obj_path)
140
+
141
+
142
+ def get_obj_from_test_obj(
143
+ test_obj: Callable[..., Any] | type | ModuleType,
144
+ ) -> Callable[..., Any] | type | ModuleType:
145
+ """Get the original object corresponding to a test object.
146
+
147
+ Args:
148
+ test_obj: The test object to get the original object for
149
+
150
+ Returns:
151
+ The corresponding original object
152
+
153
+ """
154
+ obj_importpath = make_obj_importpath_from_test_obj(test_obj)
155
+ return import_obj_from_importpath(obj_importpath)
156
+
157
+
158
+ def make_untested_summary_error_msg(
159
+ untested_objs: Iterable[str],
160
+ ) -> str:
161
+ """Create an error message summarizing untested objects.
162
+
163
+ Args:
164
+ untested_objs: Collection of import paths for untested objects
165
+
166
+ Returns:
167
+ A formatted error message listing all untested objects
168
+
169
+ """
170
+ msg = """
171
+ Found untested objects:
172
+ """
173
+ for untested in untested_objs:
174
+ msg += f"""
175
+ - {untested}
176
+ """
177
+ return msg
@@ -0,0 +1,286 @@
1
+ """Utilities for automatically creating test files for the project.
2
+
3
+ This module provides functions to generate test files for all modules and classes
4
+ in the project, ensuring that every function and method has a corresponding test.
5
+ It creates the basic test structure and generates skeleton test functions with
6
+ NotImplementedError to indicate tests that need to be written.
7
+ """
8
+
9
+ from pathlib import Path
10
+ from types import ModuleType
11
+ from typing import cast
12
+
13
+ from winipedia_utils.modules.class_ import (
14
+ get_all_cls_from_module,
15
+ get_all_methods_from_cls,
16
+ )
17
+ from winipedia_utils.modules.function import get_all_functions_from_module
18
+ from winipedia_utils.modules.module import (
19
+ create_module,
20
+ get_isolated_obj_name,
21
+ get_module_content_as_str,
22
+ get_qualname_of_obj,
23
+ to_path,
24
+ )
25
+ from winipedia_utils.modules.package import (
26
+ copy_package,
27
+ get_scr_package,
28
+ walk_package,
29
+ )
30
+ from winipedia_utils.testing import tests
31
+ from winipedia_utils.testing.convention import (
32
+ TESTS_PACKAGE_NAME,
33
+ get_test_obj_from_obj,
34
+ make_test_obj_importpath_from_obj,
35
+ make_test_obj_name,
36
+ reverse_make_test_obj_name,
37
+ )
38
+ from winipedia_utils.testing.tests.base.utils.utils import (
39
+ _conftest_content_is_correct,
40
+ _get_conftest_content,
41
+ )
42
+
43
+
44
+ def create_tests() -> None:
45
+ """Create all test files for the project.
46
+
47
+ This function orchestrates the test creation process by first setting up the base
48
+ test structure and then creating test files for all source packages.
49
+ """
50
+ create_tests_base()
51
+ create_tests_for_src_package()
52
+
53
+
54
+ def create_tests_base() -> None:
55
+ """Create the base test structure.
56
+
57
+ This function:
58
+ 1. Creates a mirror of winipedia_utils.testing.tests in the tests directory
59
+ 2. Skips copying __init__.py file contents
60
+ 3. Writes new __init__.py files for all packages
61
+ 4. Creates a conftest.py file with the appropriate pytest plugin configuration
62
+ 5. Does not overwrite anything if it already exists except conftest.py
63
+ """
64
+ tests_path = Path(TESTS_PACKAGE_NAME)
65
+ copy_package(
66
+ src_package=tests,
67
+ dst=".",
68
+ with_file_content=False,
69
+ )
70
+ # write pytest_plugin in the conftest.py
71
+ conftest_path = tests_path / "conftest.py"
72
+ # if conftest does not exist or the content is not the same, overwrite it
73
+ if not _conftest_content_is_correct(conftest_path):
74
+ conftest_path.write_text(_get_conftest_content())
75
+
76
+
77
+ def create_tests_for_src_package() -> None:
78
+ """Create test files for all modules in the source package.
79
+
80
+ This function walks through the source package hierarchy and creates corresponding
81
+ test packages and modules for each package and module found in the source.
82
+ """
83
+ src_package = get_scr_package()
84
+ for package, modules in walk_package(src_package):
85
+ create_test_package(package)
86
+ for module in modules:
87
+ create_test_module(module)
88
+
89
+
90
+ def create_test_package(package: ModuleType) -> None:
91
+ """Create a test package for a source package.
92
+
93
+ Args:
94
+ package: The source package module to create a test package for
95
+
96
+ This function creates a test package with the appropriate naming convention
97
+ if it doesn't already exist.
98
+
99
+ """
100
+ test_package_name = make_test_obj_importpath_from_obj(package)
101
+ # create package if it doesn't exist
102
+ create_module(test_package_name, is_package=True)
103
+
104
+
105
+ def create_test_module(module: ModuleType) -> None:
106
+ """Create a test module for a source module.
107
+
108
+ Args:
109
+ module: The source module to create a test module for
110
+
111
+ This function:
112
+ 1. Creates a test module with the appropriate naming convention
113
+ 2. Generates the test module content with skeleton test functions
114
+ 3. Writes the content to the test module file
115
+
116
+ """
117
+ test_module_name = make_test_obj_importpath_from_obj(module)
118
+ test_module = create_module(test_module_name, is_package=False)
119
+ test_module_path = to_path(test_module, is_package=False)
120
+ test_module_path.write_text(get_test_module_content(module))
121
+
122
+
123
+ def get_test_module_content(module: ModuleType) -> str:
124
+ """Generate the content for a test module.
125
+
126
+ Args:
127
+ module: The source module to generate test content for
128
+
129
+ Returns:
130
+ The generated test module content as a string
131
+
132
+ This function:
133
+ 1. Gets the existing test module content if it exists
134
+ 2. Adds test functions for all functions in the source module
135
+ 3. Adds test classes for all classes in the source module
136
+
137
+ """
138
+ test_module = cast("ModuleType", (get_test_obj_from_obj(module)))
139
+ test_module_content = get_module_content_as_str(test_module)
140
+
141
+ test_module_content = get_test_functions_content(
142
+ module, test_module, test_module_content
143
+ )
144
+
145
+ return get_test_classes_content(module, test_module, test_module_content)
146
+
147
+
148
+ def get_test_functions_content(
149
+ module: ModuleType,
150
+ test_module: ModuleType,
151
+ test_module_content: str,
152
+ ) -> str:
153
+ """Generate test function content for a module.
154
+
155
+ Args:
156
+ module: The source module containing functions to test
157
+ test_module: The test module to add function tests to
158
+ test_module_content: The current content of the test module
159
+
160
+ Returns:
161
+ The updated test module content with function tests added
162
+
163
+ This function:
164
+ 1. Identifies all functions in the source module
165
+ 2. Determines which functions don't have corresponding tests
166
+ 3. Generates skeleton test functions for untested functions
167
+
168
+ """
169
+ funcs = get_all_functions_from_module(module)
170
+ test_functions = get_all_functions_from_module(test_module)
171
+ supposed_test_funcs_names = [make_test_obj_name(f) for f in funcs]
172
+
173
+ test_funcs_names = [get_qualname_of_obj(f) for f in test_functions]
174
+
175
+ untested_funcs_names = [
176
+ f for f in supposed_test_funcs_names if f not in test_funcs_names
177
+ ]
178
+
179
+ for test_func_name in untested_funcs_names:
180
+ test_module_content += f"""
181
+
182
+ def {test_func_name}() -> None:
183
+ \"\"\"Test func for {reverse_make_test_obj_name(test_func_name)}.\"\"\"
184
+ raise {NotImplementedError.__name__}
185
+ """
186
+
187
+ return test_module_content
188
+
189
+
190
+ def get_test_classes_content(
191
+ module: ModuleType,
192
+ test_module: ModuleType,
193
+ test_module_content: str,
194
+ ) -> str:
195
+ """Generate test class content for a module.
196
+
197
+ Args:
198
+ module: The source module containing classes to test
199
+ test_module: The test module to add class tests to
200
+ test_module_content: The current content of the test module
201
+
202
+ Returns:
203
+ The updated test module content with class tests added
204
+
205
+ This function:
206
+ 1. Identifies all classes in the source module
207
+ 2. Determines which classes and methods don't have corresponding tests
208
+ 3. Generates skeleton test classes and methods for untested classes and methods
209
+ 4. Inserts the new test classes into the existing content
210
+ if the class already exists
211
+
212
+ Raises:
213
+ ValueError: If a test class declaration appears multiple
214
+ times in the test module
215
+
216
+ """
217
+ classes = get_all_cls_from_module(module)
218
+ test_classes = get_all_cls_from_module(test_module)
219
+
220
+ class_to_methods = {
221
+ c: get_all_methods_from_cls(c, exclude_parent_methods=True) for c in classes
222
+ }
223
+ test_class_to_methods = {
224
+ tc: get_all_methods_from_cls(tc, exclude_parent_methods=True)
225
+ for tc in test_classes
226
+ }
227
+
228
+ supposed_test_class_to_methods_names = {
229
+ make_test_obj_name(c): [make_test_obj_name(m) for m in ms]
230
+ for c, ms in class_to_methods.items()
231
+ }
232
+ test_class_to_methods_names = {
233
+ get_isolated_obj_name(tc): [get_isolated_obj_name(tm) for tm in tms]
234
+ for tc, tms in test_class_to_methods.items()
235
+ }
236
+
237
+ untested_class_to_methods_names: dict[str, list[str]] = {}
238
+ for (
239
+ test_class_name,
240
+ supposed_test_methods_names,
241
+ ) in supposed_test_class_to_methods_names.items():
242
+ test_methods_names = test_class_to_methods_names.get(test_class_name, [])
243
+ untested_methods_names = [
244
+ tmn for tmn in supposed_test_methods_names if tmn not in test_methods_names
245
+ ]
246
+ if not supposed_test_methods_names:
247
+ untested_class_to_methods_names[test_class_name] = []
248
+ if untested_methods_names:
249
+ untested_class_to_methods_names[test_class_name] = untested_methods_names
250
+
251
+ for (
252
+ test_class_name,
253
+ untested_methods_names,
254
+ ) in untested_class_to_methods_names.items():
255
+ test_class_declaration = f"""
256
+ class {test_class_name}:
257
+ \"\"\"Test class for {reverse_make_test_obj_name(test_class_name)}.\"\"\"
258
+ """
259
+ test_class_content = test_class_declaration
260
+ for untested_method_name in untested_methods_names:
261
+ test_class_content += f"""
262
+ def {untested_method_name}(self) -> None:
263
+ \"\"\"Test method for {reverse_make_test_obj_name(untested_method_name)}.\"\"\"
264
+ raise {NotImplementedError.__name__}
265
+ """
266
+ parts = test_module_content.split(test_class_declaration)
267
+ expected_parts = 2
268
+ if len(parts) > expected_parts:
269
+ msg = f"Found {len(parts)} parts, expected 2"
270
+ raise ValueError(msg)
271
+ parts.insert(1, test_class_content)
272
+ test_module_content = "".join(parts)
273
+
274
+ return test_module_content
275
+
276
+
277
+ def main() -> None:
278
+ """Entry point for the create_tests script.
279
+
280
+ Calls the create_tests function to generate all test files.
281
+ """
282
+ create_tests()
283
+
284
+
285
+ if __name__ == "__main__":
286
+ main()
@@ -0,0 +1,28 @@
1
+ """Testing fixtures for pytest.
2
+
3
+ This module provides custom fixtures for pytest that can be used to
4
+ automate common testing tasks and provide consistent setup and teardown
5
+ for tests.
6
+ """
7
+
8
+ import functools
9
+
10
+ import pytest
11
+
12
+ function_fixture = functools.partial(pytest.fixture, scope="function")
13
+ class_fixture = functools.partial(pytest.fixture, scope="class")
14
+ module_fixture = functools.partial(pytest.fixture, scope="module")
15
+ package_fixture = functools.partial(pytest.fixture, scope="package")
16
+ session_fixture = functools.partial(pytest.fixture, scope="session")
17
+
18
+ autouse_function_fixture = functools.partial(
19
+ pytest.fixture, scope="function", autouse=True
20
+ )
21
+ autouse_class_fixture = functools.partial(pytest.fixture, scope="class", autouse=True)
22
+ autouse_module_fixture = functools.partial(pytest.fixture, scope="module", autouse=True)
23
+ autouse_package_fixture = functools.partial(
24
+ pytest.fixture, scope="package", autouse=True
25
+ )
26
+ autouse_session_fixture = functools.partial(
27
+ pytest.fixture, scope="session", autouse=True
28
+ )
@@ -0,0 +1 @@
1
+ """__init__ module for winipedia_utils.testing.tests."""
@@ -0,0 +1 @@
1
+ """__init__ module for winipedia_utils.testing.tests.base."""
@@ -0,0 +1 @@
1
+ """__init__ module for winipedia_utils.testing.tests.base.fixtures."""
@@ -0,0 +1,6 @@
1
+ """Fixtures for testing.
2
+
3
+ This module provides custom fixtures for pytest that can be used to
4
+ automate common testing tasks and provide consistent setup and teardown
5
+ for tests.
6
+ """
@@ -0,0 +1 @@
1
+ """__init__ module for winipedia_utils.testing.tests.base.fixtures.scopes."""
@@ -0,0 +1,33 @@
1
+ """Class-level test fixtures and utilities.
2
+
3
+ This module provides fixtures and test functions that operate at the class scope,
4
+ ensuring that all methods within a test class have corresponding tests.
5
+ These fixtures are automatically applied to all test classes through pytest's autouse
6
+ mechanism.
7
+ """
8
+
9
+ import pytest
10
+
11
+ from winipedia_utils.testing.fixtures import autouse_class_fixture
12
+ from winipedia_utils.testing.tests.base.utils.utils import _assert_no_untested_objs
13
+
14
+
15
+ @autouse_class_fixture
16
+ def _test_all_methods_tested(request: pytest.FixtureRequest) -> None:
17
+ """Verify that all methods in a class have corresponding tests.
18
+
19
+ This fixture runs automatically for each test class and checks that every
20
+ method defined in the corresponding source class has a test method defined
21
+ in the test class.
22
+
23
+ Args:
24
+ request: The pytest fixture request object containing the current class
25
+
26
+ Raises:
27
+ AssertionError: If any method in the source class lacks a test
28
+
29
+ """
30
+ class_ = request.node.cls
31
+ if class_ is None:
32
+ return
33
+ _assert_no_untested_objs(class_)
@@ -0,0 +1,7 @@
1
+ """Function-level test fixtures and utilities.
2
+
3
+ This module provides fixtures and test functions that operate at the function scope,
4
+ ensuring proper setup and teardown for individual test functions.
5
+ These fixtures are automatically applied to all test functions through pytest's autouse
6
+ mechanism.
7
+ """
@@ -0,0 +1,31 @@
1
+ """Module-level test fixtures and utilities.
2
+
3
+ This module provides fixtures and test functions that operate at the module scope,
4
+ ensuring that all functions and classes within a module have corresponding tests.
5
+ These fixtures are automatically applied to all test modules through pytest's autouse
6
+ mechanism.
7
+ """
8
+
9
+ import pytest
10
+
11
+ from winipedia_utils.testing.fixtures import autouse_module_fixture
12
+ from winipedia_utils.testing.tests.base.utils.utils import _assert_no_untested_objs
13
+
14
+
15
+ @autouse_module_fixture
16
+ def _test_all_funcs_and_classes_tested(request: pytest.FixtureRequest) -> None:
17
+ """Verify that all functions and classes in a module have corresponding tests.
18
+
19
+ This fixture runs automatically for each test module and checks that every
20
+ function and class defined in the corresponding source module has a test
21
+ function or class defined in the test module.
22
+
23
+ Args:
24
+ request: The pytest fixture request object containing the current module
25
+
26
+ Raises:
27
+ AssertionError: If any function or class in the source module lacks a test
28
+
29
+ """
30
+ module = request.module
31
+ _assert_no_untested_objs(module)
@@ -0,0 +1,7 @@
1
+ """Package-level test fixtures and utilities.
2
+
3
+ This module provides fixtures and test functions that operate at the package scope,
4
+ ensuring that all modules within a package have corresponding tests.
5
+ These fixtures are automatically applied to all test packages through pytest's autouse
6
+ mechanism.
7
+ """