provide-foundation 0.0.0.dev0__py3-none-any.whl → 0.0.0.dev2__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 (161) hide show
  1. provide/foundation/__init__.py +41 -23
  2. provide/foundation/archive/__init__.py +23 -0
  3. provide/foundation/archive/base.py +70 -0
  4. provide/foundation/archive/bzip2.py +157 -0
  5. provide/foundation/archive/gzip.py +159 -0
  6. provide/foundation/archive/operations.py +334 -0
  7. provide/foundation/archive/tar.py +164 -0
  8. provide/foundation/archive/zip.py +203 -0
  9. provide/foundation/cli/__init__.py +2 -2
  10. provide/foundation/cli/commands/deps.py +13 -7
  11. provide/foundation/cli/commands/logs/__init__.py +1 -1
  12. provide/foundation/cli/commands/logs/query.py +1 -1
  13. provide/foundation/cli/commands/logs/send.py +1 -1
  14. provide/foundation/cli/commands/logs/tail.py +1 -1
  15. provide/foundation/cli/decorators.py +11 -10
  16. provide/foundation/cli/main.py +1 -1
  17. provide/foundation/cli/testing.py +2 -35
  18. provide/foundation/cli/utils.py +21 -17
  19. provide/foundation/config/__init__.py +35 -2
  20. provide/foundation/config/base.py +2 -2
  21. provide/foundation/config/converters.py +479 -0
  22. provide/foundation/config/defaults.py +67 -0
  23. provide/foundation/config/env.py +4 -19
  24. provide/foundation/config/loader.py +9 -3
  25. provide/foundation/config/sync.py +19 -4
  26. provide/foundation/console/input.py +5 -5
  27. provide/foundation/console/output.py +35 -13
  28. provide/foundation/context/__init__.py +8 -4
  29. provide/foundation/context/core.py +85 -109
  30. provide/foundation/core.py +1 -2
  31. provide/foundation/crypto/__init__.py +2 -0
  32. provide/foundation/crypto/certificates/__init__.py +34 -0
  33. provide/foundation/crypto/certificates/base.py +173 -0
  34. provide/foundation/crypto/certificates/certificate.py +290 -0
  35. provide/foundation/crypto/certificates/factory.py +213 -0
  36. provide/foundation/crypto/certificates/generator.py +138 -0
  37. provide/foundation/crypto/certificates/loader.py +130 -0
  38. provide/foundation/crypto/certificates/operations.py +198 -0
  39. provide/foundation/crypto/certificates/trust.py +107 -0
  40. provide/foundation/errors/__init__.py +2 -3
  41. provide/foundation/errors/decorators.py +0 -231
  42. provide/foundation/errors/types.py +0 -97
  43. provide/foundation/eventsets/__init__.py +0 -0
  44. provide/foundation/eventsets/display.py +84 -0
  45. provide/foundation/eventsets/registry.py +160 -0
  46. provide/foundation/eventsets/resolver.py +192 -0
  47. provide/foundation/eventsets/sets/das.py +128 -0
  48. provide/foundation/eventsets/sets/database.py +125 -0
  49. provide/foundation/eventsets/sets/http.py +153 -0
  50. provide/foundation/eventsets/sets/llm.py +139 -0
  51. provide/foundation/eventsets/sets/task_queue.py +107 -0
  52. provide/foundation/eventsets/types.py +70 -0
  53. provide/foundation/file/directory.py +13 -22
  54. provide/foundation/file/lock.py +3 -1
  55. provide/foundation/hub/components.py +77 -515
  56. provide/foundation/hub/config.py +151 -0
  57. provide/foundation/hub/discovery.py +62 -0
  58. provide/foundation/hub/handlers.py +81 -0
  59. provide/foundation/hub/lifecycle.py +194 -0
  60. provide/foundation/hub/manager.py +4 -4
  61. provide/foundation/hub/processors.py +44 -0
  62. provide/foundation/integrations/__init__.py +11 -0
  63. provide/foundation/{observability → integrations}/openobserve/__init__.py +10 -7
  64. provide/foundation/{observability → integrations}/openobserve/auth.py +1 -1
  65. provide/foundation/{observability → integrations}/openobserve/client.py +12 -12
  66. provide/foundation/{observability → integrations}/openobserve/commands.py +3 -3
  67. provide/foundation/integrations/openobserve/config.py +37 -0
  68. provide/foundation/{observability → integrations}/openobserve/formatters.py +1 -1
  69. provide/foundation/{observability → integrations}/openobserve/otlp.py +1 -1
  70. provide/foundation/{observability → integrations}/openobserve/search.py +2 -2
  71. provide/foundation/{observability → integrations}/openobserve/streaming.py +4 -4
  72. provide/foundation/logger/__init__.py +3 -10
  73. provide/foundation/logger/config/logging.py +68 -298
  74. provide/foundation/logger/config/telemetry.py +41 -121
  75. provide/foundation/logger/core.py +0 -2
  76. provide/foundation/logger/custom_processors.py +1 -0
  77. provide/foundation/logger/factories.py +11 -2
  78. provide/foundation/logger/processors/main.py +20 -84
  79. provide/foundation/logger/setup/__init__.py +5 -1
  80. provide/foundation/logger/setup/coordinator.py +76 -24
  81. provide/foundation/logger/setup/processors.py +2 -9
  82. provide/foundation/logger/trace.py +27 -0
  83. provide/foundation/metrics/otel.py +10 -10
  84. provide/foundation/observability/__init__.py +2 -2
  85. provide/foundation/process/__init__.py +9 -0
  86. provide/foundation/process/exit.py +47 -0
  87. provide/foundation/process/lifecycle.py +115 -59
  88. provide/foundation/resilience/__init__.py +35 -0
  89. provide/foundation/resilience/circuit.py +164 -0
  90. provide/foundation/resilience/decorators.py +220 -0
  91. provide/foundation/resilience/fallback.py +193 -0
  92. provide/foundation/resilience/retry.py +325 -0
  93. provide/foundation/streams/config.py +79 -0
  94. provide/foundation/streams/console.py +7 -8
  95. provide/foundation/streams/core.py +6 -3
  96. provide/foundation/streams/file.py +12 -2
  97. provide/foundation/testing/__init__.py +84 -2
  98. provide/foundation/testing/archive/__init__.py +24 -0
  99. provide/foundation/testing/archive/fixtures.py +217 -0
  100. provide/foundation/testing/cli.py +30 -17
  101. provide/foundation/testing/common/__init__.py +32 -0
  102. provide/foundation/testing/common/fixtures.py +236 -0
  103. provide/foundation/testing/file/__init__.py +40 -0
  104. provide/foundation/testing/file/content_fixtures.py +316 -0
  105. provide/foundation/testing/file/directory_fixtures.py +107 -0
  106. provide/foundation/testing/file/fixtures.py +52 -0
  107. provide/foundation/testing/file/special_fixtures.py +153 -0
  108. provide/foundation/testing/logger.py +117 -11
  109. provide/foundation/testing/mocking/__init__.py +46 -0
  110. provide/foundation/testing/mocking/fixtures.py +331 -0
  111. provide/foundation/testing/process/__init__.py +48 -0
  112. provide/foundation/testing/process/async_fixtures.py +405 -0
  113. provide/foundation/testing/process/fixtures.py +56 -0
  114. provide/foundation/testing/process/subprocess_fixtures.py +209 -0
  115. provide/foundation/testing/threading/__init__.py +38 -0
  116. provide/foundation/testing/threading/basic_fixtures.py +101 -0
  117. provide/foundation/testing/threading/data_fixtures.py +99 -0
  118. provide/foundation/testing/threading/execution_fixtures.py +263 -0
  119. provide/foundation/testing/threading/fixtures.py +54 -0
  120. provide/foundation/testing/threading/sync_fixtures.py +97 -0
  121. provide/foundation/testing/time/__init__.py +32 -0
  122. provide/foundation/testing/time/fixtures.py +409 -0
  123. provide/foundation/testing/transport/__init__.py +30 -0
  124. provide/foundation/testing/transport/fixtures.py +280 -0
  125. provide/foundation/tools/__init__.py +58 -0
  126. provide/foundation/tools/base.py +348 -0
  127. provide/foundation/tools/cache.py +268 -0
  128. provide/foundation/tools/downloader.py +224 -0
  129. provide/foundation/tools/installer.py +254 -0
  130. provide/foundation/tools/registry.py +223 -0
  131. provide/foundation/tools/resolver.py +321 -0
  132. provide/foundation/tools/verifier.py +186 -0
  133. provide/foundation/tracer/otel.py +7 -11
  134. provide/foundation/tracer/spans.py +2 -2
  135. provide/foundation/transport/__init__.py +155 -0
  136. provide/foundation/transport/base.py +171 -0
  137. provide/foundation/transport/client.py +266 -0
  138. provide/foundation/transport/config.py +140 -0
  139. provide/foundation/transport/errors.py +79 -0
  140. provide/foundation/transport/http.py +232 -0
  141. provide/foundation/transport/middleware.py +360 -0
  142. provide/foundation/transport/registry.py +167 -0
  143. provide/foundation/transport/types.py +45 -0
  144. provide/foundation/utils/deps.py +14 -12
  145. provide/foundation/utils/parsing.py +49 -4
  146. {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/METADATA +5 -28
  147. provide_foundation-0.0.0.dev2.dist-info/RECORD +225 -0
  148. provide/foundation/cli/commands/logs/generate_old.py +0 -569
  149. provide/foundation/crypto/certificates.py +0 -896
  150. provide/foundation/logger/emoji/__init__.py +0 -44
  151. provide/foundation/logger/emoji/matrix.py +0 -209
  152. provide/foundation/logger/emoji/sets.py +0 -458
  153. provide/foundation/logger/emoji/types.py +0 -56
  154. provide/foundation/logger/setup/emoji_resolver.py +0 -64
  155. provide_foundation-0.0.0.dev0.dist-info/RECORD +0 -149
  156. /provide/foundation/{observability → integrations}/openobserve/exceptions.py +0 -0
  157. /provide/foundation/{observability → integrations}/openobserve/models.py +0 -0
  158. {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/WHEEL +0 -0
  159. {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/entry_points.txt +0 -0
  160. {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/licenses/LICENSE +0 -0
  161. {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/top_level.txt +0 -0
@@ -9,6 +9,8 @@ and ensuring test isolation for the Foundation logging system.
9
9
  """
10
10
 
11
11
  import structlog
12
+ import pytest
13
+ from unittest.mock import Mock
12
14
 
13
15
  from provide.foundation.logger.core import (
14
16
  _LAZY_SETUP_STATE,
@@ -17,6 +19,78 @@ from provide.foundation.logger.core import (
17
19
  from provide.foundation.streams.file import reset_streams
18
20
 
19
21
 
22
+ @pytest.fixture
23
+ def mock_logger():
24
+ """
25
+ Comprehensive mock logger for testing.
26
+
27
+ Provides compatibility with both stdlib logging and structlog interfaces,
28
+ including method call tracking and common logger attributes.
29
+
30
+ Returns:
31
+ Mock logger with debug, info, warning, error methods and structlog compatibility.
32
+ """
33
+ logger = Mock()
34
+ logger.debug = Mock()
35
+ logger.info = Mock()
36
+ logger.warning = Mock()
37
+ logger.warn = Mock() # Alias for warning
38
+ logger.error = Mock()
39
+ logger.exception = Mock()
40
+ logger.critical = Mock()
41
+ logger.fatal = Mock() # Alias for critical
42
+
43
+ # Add common logger attributes
44
+ logger.name = "mock_logger"
45
+ logger.level = 10 # DEBUG level
46
+ logger.handlers = []
47
+ logger.disabled = False
48
+
49
+ # Add structlog compatibility methods
50
+ logger.bind = Mock(return_value=logger)
51
+ logger.unbind = Mock(return_value=logger)
52
+ logger.new = Mock(return_value=logger)
53
+ logger.msg = Mock() # Alternative to info
54
+
55
+ # Add trace method for Foundation's extended logging
56
+ logger.trace = Mock()
57
+
58
+ return logger
59
+
60
+
61
+ def mock_logger_factory():
62
+ """
63
+ Factory function to create mock loggers outside of pytest context.
64
+
65
+ Useful for unit tests that need a mock logger but aren't using pytest fixtures.
66
+
67
+ Returns:
68
+ Mock logger with the same interface as the pytest fixture.
69
+ """
70
+ logger = Mock()
71
+ logger.debug = Mock()
72
+ logger.info = Mock()
73
+ logger.warning = Mock()
74
+ logger.warn = Mock()
75
+ logger.error = Mock()
76
+ logger.exception = Mock()
77
+ logger.critical = Mock()
78
+ logger.fatal = Mock()
79
+
80
+ logger.name = "mock_logger"
81
+ logger.level = 10
82
+ logger.handlers = []
83
+ logger.disabled = False
84
+
85
+ logger.bind = Mock(return_value=logger)
86
+ logger.unbind = Mock(return_value=logger)
87
+ logger.new = Mock(return_value=logger)
88
+ logger.msg = Mock()
89
+ logger.trace = Mock()
90
+
91
+ return logger
92
+
93
+
20
94
  def _reset_opentelemetry_providers() -> None:
21
95
  """
22
96
  Reset OpenTelemetry providers to uninitialized state.
@@ -25,41 +99,52 @@ def _reset_opentelemetry_providers() -> None:
25
99
  and stream closure issues by properly resetting the global providers.
26
100
  """
27
101
  try:
28
- # Reset tracing provider by resetting the Once flag
102
+ # Reset tracing provider more thoroughly
29
103
  import opentelemetry.trace as otel_trace
30
-
104
+
105
+ # Reset the Once flag to allow re-initialization
31
106
  if hasattr(otel_trace, "_TRACER_PROVIDER_SET_ONCE"):
32
107
  once_obj = otel_trace._TRACER_PROVIDER_SET_ONCE
33
108
  if hasattr(once_obj, "_done"):
34
109
  once_obj._done = False
110
+ if hasattr(once_obj, "_lock"):
111
+ with once_obj._lock:
112
+ once_obj._done = False
113
+
35
114
  # Reset to NoOpTracerProvider
36
115
  from opentelemetry.trace import NoOpTracerProvider
37
-
38
- otel_trace._TRACER_PROVIDER = NoOpTracerProvider()
116
+ otel_trace.set_tracer_provider(NoOpTracerProvider())
117
+
39
118
  except ImportError:
40
119
  # OpenTelemetry tracing not available
41
120
  pass
42
121
  except Exception:
43
- # Ignore errors during reset
122
+ # Ignore errors during reset - better to continue than fail
44
123
  pass
45
124
 
46
125
  try:
47
- # Reset metrics provider by resetting the Once flag
126
+ # Reset metrics provider more thoroughly
127
+ import opentelemetry.metrics as otel_metrics
48
128
  import opentelemetry.metrics._internal as otel_metrics_internal
49
-
129
+
130
+ # Reset the Once flag to allow re-initialization
50
131
  if hasattr(otel_metrics_internal, "_METER_PROVIDER_SET_ONCE"):
51
132
  once_obj = otel_metrics_internal._METER_PROVIDER_SET_ONCE
52
133
  if hasattr(once_obj, "_done"):
53
134
  once_obj._done = False
135
+ if hasattr(once_obj, "_lock"):
136
+ with once_obj._lock:
137
+ once_obj._done = False
138
+
54
139
  # Reset to NoOpMeterProvider
55
140
  from opentelemetry.metrics import NoOpMeterProvider
56
-
57
- otel_metrics_internal._METER_PROVIDER = NoOpMeterProvider()
141
+ otel_metrics.set_meter_provider(NoOpMeterProvider())
142
+
58
143
  except ImportError:
59
144
  # OpenTelemetry metrics not available
60
145
  pass
61
146
  except Exception:
62
- # Ignore errors during reset
147
+ # Ignore errors during reset - better to continue than fail
63
148
  pass
64
149
 
65
150
 
@@ -81,7 +166,10 @@ def reset_foundation_state() -> None:
81
166
  reset_streams()
82
167
 
83
168
  # Reset OpenTelemetry providers to avoid "Overriding" warnings and stream closure
84
- _reset_opentelemetry_providers()
169
+ # Note: OpenTelemetry providers are designed to prevent override for safety.
170
+ # In test environments, we suppress this reset to avoid hanging/blocking.
171
+ # The warnings are harmless in test context.
172
+ # _reset_opentelemetry_providers()
85
173
 
86
174
  # Reset foundation logger state
87
175
  foundation_logger._is_configured_by_setup = False
@@ -97,10 +185,28 @@ def reset_foundation_setup_for_testing() -> None:
97
185
  This function ensures clean test isolation by resetting all
98
186
  Foundation logging state between test runs.
99
187
  """
188
+ # Full reset but with improved OpenTelemetry handling
100
189
  reset_foundation_state()
190
+
191
+ # Clear and re-initialize the hub for test isolation
192
+ try:
193
+ from provide.foundation.hub.manager import clear_hub
194
+ clear_hub()
195
+ except ImportError:
196
+ pass
197
+
198
+ # Re-register HTTP transport for tests that need it
199
+ try:
200
+ from provide.foundation.transport.http import _register_http_transport
201
+ _register_http_transport()
202
+ except ImportError:
203
+ # Transport module not available
204
+ pass
101
205
 
102
206
 
103
207
  __all__ = [
104
208
  "reset_foundation_setup_for_testing",
105
209
  "reset_foundation_state",
210
+ "mock_logger",
211
+ "mock_logger_factory",
106
212
  ]
@@ -0,0 +1,46 @@
1
+ """
2
+ Mocking utilities for the provide-io ecosystem.
3
+
4
+ Standardized mocking patterns, fixtures, and utilities to reduce
5
+ boilerplate and ensure consistent mocking across all tests.
6
+ """
7
+
8
+ from provide.foundation.testing.mocking.fixtures import (
9
+ Mock,
10
+ MagicMock,
11
+ AsyncMock,
12
+ PropertyMock,
13
+ patch,
14
+ call,
15
+ ANY,
16
+ mock_factory,
17
+ magic_mock_factory,
18
+ async_mock_factory,
19
+ property_mock_factory,
20
+ patch_fixture,
21
+ patch_multiple_fixture,
22
+ auto_patch,
23
+ mock_open_fixture,
24
+ spy_fixture,
25
+ assert_mock_calls,
26
+ )
27
+
28
+ __all__ = [
29
+ "Mock",
30
+ "MagicMock",
31
+ "AsyncMock",
32
+ "PropertyMock",
33
+ "patch",
34
+ "call",
35
+ "ANY",
36
+ "mock_factory",
37
+ "magic_mock_factory",
38
+ "async_mock_factory",
39
+ "property_mock_factory",
40
+ "patch_fixture",
41
+ "patch_multiple_fixture",
42
+ "auto_patch",
43
+ "mock_open_fixture",
44
+ "spy_fixture",
45
+ "assert_mock_calls",
46
+ ]
@@ -0,0 +1,331 @@
1
+ """
2
+ Mocking Fixtures and Utilities.
3
+
4
+ Standardized mocking patterns and fixtures for the provide-io ecosystem.
5
+ Reduces boilerplate and ensures consistent mocking across all tests.
6
+ """
7
+
8
+ from unittest.mock import Mock, MagicMock, AsyncMock, PropertyMock, patch, call, ANY
9
+ from typing import Any, Callable
10
+ from collections.abc import Generator
11
+ import sys
12
+
13
+ import pytest
14
+
15
+
16
+ @pytest.fixture
17
+ def mock_factory():
18
+ """
19
+ Factory for creating configured mock objects.
20
+
21
+ Returns:
22
+ Function that creates mock objects with common configurations.
23
+ """
24
+ def _create_mock(name: str = None, **kwargs) -> Mock:
25
+ """
26
+ Create a mock with standard configuration.
27
+
28
+ Args:
29
+ name: Optional name for the mock
30
+ **kwargs: Additional mock configuration
31
+
32
+ Returns:
33
+ Configured Mock object
34
+ """
35
+ defaults = {
36
+ "spec_set": True if "spec" in kwargs else False,
37
+ }
38
+ defaults.update(kwargs)
39
+
40
+ mock = Mock(name=name, **defaults)
41
+ return mock
42
+
43
+ return _create_mock
44
+
45
+
46
+ @pytest.fixture
47
+ def magic_mock_factory():
48
+ """
49
+ Factory for creating MagicMock objects.
50
+
51
+ Returns:
52
+ Function that creates MagicMock objects with common configurations.
53
+ """
54
+ def _create_magic_mock(name: str = None, **kwargs) -> MagicMock:
55
+ """
56
+ Create a MagicMock with standard configuration.
57
+
58
+ Args:
59
+ name: Optional name for the mock
60
+ **kwargs: Additional mock configuration
61
+
62
+ Returns:
63
+ Configured MagicMock object
64
+ """
65
+ return MagicMock(name=name, **kwargs)
66
+
67
+ return _create_magic_mock
68
+
69
+
70
+ @pytest.fixture
71
+ def async_mock_factory():
72
+ """
73
+ Factory for creating AsyncMock objects.
74
+
75
+ Returns:
76
+ Function that creates AsyncMock objects with common configurations.
77
+ """
78
+ def _create_async_mock(name: str = None, return_value=None, side_effect=None, **kwargs) -> AsyncMock:
79
+ """
80
+ Create an AsyncMock with standard configuration.
81
+
82
+ Args:
83
+ name: Optional name for the mock
84
+ return_value: Return value for the async mock
85
+ side_effect: Side effect for the async mock
86
+ **kwargs: Additional mock configuration
87
+
88
+ Returns:
89
+ Configured AsyncMock object
90
+ """
91
+ mock = AsyncMock(name=name, **kwargs)
92
+ if return_value is not None:
93
+ mock.return_value = return_value
94
+ if side_effect is not None:
95
+ mock.side_effect = side_effect
96
+ return mock
97
+
98
+ return _create_async_mock
99
+
100
+
101
+ @pytest.fixture
102
+ def property_mock_factory():
103
+ """
104
+ Factory for creating PropertyMock objects.
105
+
106
+ Returns:
107
+ Function that creates PropertyMock objects.
108
+ """
109
+ def _create_property_mock(return_value=None, side_effect=None, **kwargs) -> PropertyMock:
110
+ """
111
+ Create a PropertyMock.
112
+
113
+ Args:
114
+ return_value: Return value for the property
115
+ side_effect: Side effect for the property
116
+ **kwargs: Additional mock configuration
117
+
118
+ Returns:
119
+ Configured PropertyMock object
120
+ """
121
+ return PropertyMock(return_value=return_value, side_effect=side_effect, **kwargs)
122
+
123
+ return _create_property_mock
124
+
125
+
126
+ @pytest.fixture
127
+ def patch_fixture():
128
+ """
129
+ Fixture for patching objects with automatic cleanup.
130
+
131
+ Returns:
132
+ Function that patches objects and returns the mock.
133
+ """
134
+ patches = []
135
+
136
+ def _patch(target: str, **kwargs) -> Mock:
137
+ """
138
+ Patch a target with automatic cleanup.
139
+
140
+ Args:
141
+ target: The target to patch (module.Class.attribute)
142
+ **kwargs: Additional patch configuration
143
+
144
+ Returns:
145
+ The mock object
146
+ """
147
+ patcher = patch(target, **kwargs)
148
+ mock = patcher.start()
149
+ patches.append(patcher)
150
+ return mock
151
+
152
+ yield _patch
153
+
154
+ # Cleanup all patches
155
+ for patcher in patches:
156
+ patcher.stop()
157
+
158
+
159
+ @pytest.fixture
160
+ def patch_multiple_fixture():
161
+ """
162
+ Fixture for patching multiple objects at once.
163
+
164
+ Returns:
165
+ Function that patches multiple targets.
166
+ """
167
+ patches = []
168
+
169
+ def _patch_multiple(target_module: str, **kwargs) -> dict[str, Mock]:
170
+ """
171
+ Patch multiple attributes in a module.
172
+
173
+ Args:
174
+ target_module: The module to patch in
175
+ **kwargs: Mapping of attribute names to mock objects or DEFAULT
176
+
177
+ Returns:
178
+ Dict mapping attribute names to mock objects
179
+ """
180
+ from unittest.mock import patch as mock_patch
181
+ patcher = mock_patch.multiple(target_module, **kwargs)
182
+ mocks = patcher.start()
183
+ patches.append(patcher)
184
+ return mocks
185
+
186
+ yield _patch_multiple
187
+
188
+ # Cleanup all patches
189
+ for patcher in patches:
190
+ patcher.stop()
191
+
192
+
193
+ @pytest.fixture
194
+ def auto_patch():
195
+ """
196
+ Context manager for automatic patching with cleanup.
197
+
198
+ Returns:
199
+ Patch context manager class.
200
+ """
201
+ class AutoPatch:
202
+ def __init__(self):
203
+ self.patches = []
204
+
205
+ def object(self, target: Any, attribute: str, **kwargs) -> Mock:
206
+ """Patch an object's attribute."""
207
+ patcher = patch.object(target, attribute, **kwargs)
208
+ mock = patcher.start()
209
+ self.patches.append(patcher)
210
+ return mock
211
+
212
+ def dict(self, target: dict, values: dict, **kwargs) -> None:
213
+ """Patch a dictionary."""
214
+ patcher = patch.dict(target, values, **kwargs)
215
+ patcher.start()
216
+ self.patches.append(patcher)
217
+
218
+ def env(self, **env_vars) -> None:
219
+ """Patch environment variables."""
220
+ import os
221
+ patcher = patch.dict(os.environ, env_vars)
222
+ patcher.start()
223
+ self.patches.append(patcher)
224
+
225
+ def cleanup(self):
226
+ """Stop all patches."""
227
+ for patcher in self.patches:
228
+ patcher.stop()
229
+
230
+ patcher = AutoPatch()
231
+ yield patcher
232
+ patcher.cleanup()
233
+
234
+
235
+ @pytest.fixture
236
+ def mock_open_fixture():
237
+ """
238
+ Fixture for mocking file operations.
239
+
240
+ Returns:
241
+ Function that creates a mock for open().
242
+ """
243
+ from unittest.mock import mock_open
244
+
245
+ def _mock_open(read_data: str = None) -> Mock:
246
+ """
247
+ Create a mock for the open() builtin.
248
+
249
+ Args:
250
+ read_data: Optional data to return when reading
251
+
252
+ Returns:
253
+ Mock object for open()
254
+ """
255
+ return mock_open(read_data=read_data)
256
+
257
+ return _mock_open
258
+
259
+
260
+ @pytest.fixture
261
+ def spy_fixture():
262
+ """
263
+ Create a spy (mock that calls through to the original).
264
+
265
+ Returns:
266
+ Function that creates spy objects.
267
+ """
268
+ def _create_spy(obj: Any, method_name: str) -> Mock:
269
+ """
270
+ Create a spy on a method.
271
+
272
+ Args:
273
+ obj: The object to spy on
274
+ method_name: The method name to spy on
275
+
276
+ Returns:
277
+ Mock that wraps the original method
278
+ """
279
+ original = getattr(obj, method_name)
280
+ mock = Mock(wraps=original)
281
+ setattr(obj, method_name, mock)
282
+ return mock
283
+
284
+ return _create_spy
285
+
286
+
287
+ @pytest.fixture
288
+ def assert_mock_calls():
289
+ """
290
+ Helper for asserting mock calls with better error messages.
291
+
292
+ Returns:
293
+ Function for asserting mock calls.
294
+ """
295
+ def _assert_calls(mock: Mock, expected_calls: list, any_order: bool = False):
296
+ """
297
+ Assert that a mock was called with expected calls.
298
+
299
+ Args:
300
+ mock: The mock to check
301
+ expected_calls: List of expected call objects
302
+ any_order: Whether calls can be in any order
303
+ """
304
+ if any_order:
305
+ mock.assert_has_calls(expected_calls, any_order=True)
306
+ else:
307
+ mock.assert_has_calls(expected_calls)
308
+
309
+ return _assert_calls
310
+
311
+
312
+ # Re-export commonly used mock utilities
313
+ __all__ = [
314
+ "Mock",
315
+ "MagicMock",
316
+ "AsyncMock",
317
+ "PropertyMock",
318
+ "patch",
319
+ "call",
320
+ "ANY",
321
+ "mock_factory",
322
+ "magic_mock_factory",
323
+ "async_mock_factory",
324
+ "property_mock_factory",
325
+ "patch_fixture",
326
+ "patch_multiple_fixture",
327
+ "auto_patch",
328
+ "mock_open_fixture",
329
+ "spy_fixture",
330
+ "assert_mock_calls",
331
+ ]
@@ -0,0 +1,48 @@
1
+ """
2
+ Process and async testing fixtures for the provide-io ecosystem.
3
+
4
+ Standard fixtures for testing async code, subprocess operations, and
5
+ event loop management across any project that depends on provide.foundation.
6
+ """
7
+
8
+ from provide.foundation.testing.process.fixtures import (
9
+ clean_event_loop,
10
+ async_timeout,
11
+ mock_async_process,
12
+ async_stream_reader,
13
+ event_loop_policy,
14
+ async_context_manager,
15
+ async_iterator,
16
+ async_queue,
17
+ async_lock,
18
+ mock_async_sleep,
19
+ async_subprocess,
20
+ async_gather_helper,
21
+ async_task_group,
22
+ async_condition_waiter,
23
+ async_mock_server,
24
+ async_pipeline,
25
+ async_rate_limiter,
26
+ async_test_client,
27
+ )
28
+
29
+ __all__ = [
30
+ "clean_event_loop",
31
+ "async_timeout",
32
+ "mock_async_process",
33
+ "async_stream_reader",
34
+ "event_loop_policy",
35
+ "async_context_manager",
36
+ "async_iterator",
37
+ "async_queue",
38
+ "async_lock",
39
+ "mock_async_sleep",
40
+ "async_subprocess",
41
+ "async_gather_helper",
42
+ "async_task_group",
43
+ "async_condition_waiter",
44
+ "async_mock_server",
45
+ "async_pipeline",
46
+ "async_rate_limiter",
47
+ "async_test_client",
48
+ ]