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.
- provide/foundation/__init__.py +41 -23
- provide/foundation/archive/__init__.py +23 -0
- provide/foundation/archive/base.py +70 -0
- provide/foundation/archive/bzip2.py +157 -0
- provide/foundation/archive/gzip.py +159 -0
- provide/foundation/archive/operations.py +334 -0
- provide/foundation/archive/tar.py +164 -0
- provide/foundation/archive/zip.py +203 -0
- provide/foundation/cli/__init__.py +2 -2
- provide/foundation/cli/commands/deps.py +13 -7
- provide/foundation/cli/commands/logs/__init__.py +1 -1
- provide/foundation/cli/commands/logs/query.py +1 -1
- provide/foundation/cli/commands/logs/send.py +1 -1
- provide/foundation/cli/commands/logs/tail.py +1 -1
- provide/foundation/cli/decorators.py +11 -10
- provide/foundation/cli/main.py +1 -1
- provide/foundation/cli/testing.py +2 -35
- provide/foundation/cli/utils.py +21 -17
- provide/foundation/config/__init__.py +35 -2
- provide/foundation/config/base.py +2 -2
- provide/foundation/config/converters.py +479 -0
- provide/foundation/config/defaults.py +67 -0
- provide/foundation/config/env.py +4 -19
- provide/foundation/config/loader.py +9 -3
- provide/foundation/config/sync.py +19 -4
- provide/foundation/console/input.py +5 -5
- provide/foundation/console/output.py +35 -13
- provide/foundation/context/__init__.py +8 -4
- provide/foundation/context/core.py +85 -109
- provide/foundation/core.py +1 -2
- provide/foundation/crypto/__init__.py +2 -0
- provide/foundation/crypto/certificates/__init__.py +34 -0
- provide/foundation/crypto/certificates/base.py +173 -0
- provide/foundation/crypto/certificates/certificate.py +290 -0
- provide/foundation/crypto/certificates/factory.py +213 -0
- provide/foundation/crypto/certificates/generator.py +138 -0
- provide/foundation/crypto/certificates/loader.py +130 -0
- provide/foundation/crypto/certificates/operations.py +198 -0
- provide/foundation/crypto/certificates/trust.py +107 -0
- provide/foundation/errors/__init__.py +2 -3
- provide/foundation/errors/decorators.py +0 -231
- provide/foundation/errors/types.py +0 -97
- provide/foundation/eventsets/__init__.py +0 -0
- provide/foundation/eventsets/display.py +84 -0
- provide/foundation/eventsets/registry.py +160 -0
- provide/foundation/eventsets/resolver.py +192 -0
- provide/foundation/eventsets/sets/das.py +128 -0
- provide/foundation/eventsets/sets/database.py +125 -0
- provide/foundation/eventsets/sets/http.py +153 -0
- provide/foundation/eventsets/sets/llm.py +139 -0
- provide/foundation/eventsets/sets/task_queue.py +107 -0
- provide/foundation/eventsets/types.py +70 -0
- provide/foundation/file/directory.py +13 -22
- provide/foundation/file/lock.py +3 -1
- provide/foundation/hub/components.py +77 -515
- provide/foundation/hub/config.py +151 -0
- provide/foundation/hub/discovery.py +62 -0
- provide/foundation/hub/handlers.py +81 -0
- provide/foundation/hub/lifecycle.py +194 -0
- provide/foundation/hub/manager.py +4 -4
- provide/foundation/hub/processors.py +44 -0
- provide/foundation/integrations/__init__.py +11 -0
- provide/foundation/{observability → integrations}/openobserve/__init__.py +10 -7
- provide/foundation/{observability → integrations}/openobserve/auth.py +1 -1
- provide/foundation/{observability → integrations}/openobserve/client.py +12 -12
- provide/foundation/{observability → integrations}/openobserve/commands.py +3 -3
- provide/foundation/integrations/openobserve/config.py +37 -0
- provide/foundation/{observability → integrations}/openobserve/formatters.py +1 -1
- provide/foundation/{observability → integrations}/openobserve/otlp.py +1 -1
- provide/foundation/{observability → integrations}/openobserve/search.py +2 -2
- provide/foundation/{observability → integrations}/openobserve/streaming.py +4 -4
- provide/foundation/logger/__init__.py +3 -10
- provide/foundation/logger/config/logging.py +68 -298
- provide/foundation/logger/config/telemetry.py +41 -121
- provide/foundation/logger/core.py +0 -2
- provide/foundation/logger/custom_processors.py +1 -0
- provide/foundation/logger/factories.py +11 -2
- provide/foundation/logger/processors/main.py +20 -84
- provide/foundation/logger/setup/__init__.py +5 -1
- provide/foundation/logger/setup/coordinator.py +76 -24
- provide/foundation/logger/setup/processors.py +2 -9
- provide/foundation/logger/trace.py +27 -0
- provide/foundation/metrics/otel.py +10 -10
- provide/foundation/observability/__init__.py +2 -2
- provide/foundation/process/__init__.py +9 -0
- provide/foundation/process/exit.py +47 -0
- provide/foundation/process/lifecycle.py +115 -59
- provide/foundation/resilience/__init__.py +35 -0
- provide/foundation/resilience/circuit.py +164 -0
- provide/foundation/resilience/decorators.py +220 -0
- provide/foundation/resilience/fallback.py +193 -0
- provide/foundation/resilience/retry.py +325 -0
- provide/foundation/streams/config.py +79 -0
- provide/foundation/streams/console.py +7 -8
- provide/foundation/streams/core.py +6 -3
- provide/foundation/streams/file.py +12 -2
- provide/foundation/testing/__init__.py +84 -2
- provide/foundation/testing/archive/__init__.py +24 -0
- provide/foundation/testing/archive/fixtures.py +217 -0
- provide/foundation/testing/cli.py +30 -17
- provide/foundation/testing/common/__init__.py +32 -0
- provide/foundation/testing/common/fixtures.py +236 -0
- provide/foundation/testing/file/__init__.py +40 -0
- provide/foundation/testing/file/content_fixtures.py +316 -0
- provide/foundation/testing/file/directory_fixtures.py +107 -0
- provide/foundation/testing/file/fixtures.py +52 -0
- provide/foundation/testing/file/special_fixtures.py +153 -0
- provide/foundation/testing/logger.py +117 -11
- provide/foundation/testing/mocking/__init__.py +46 -0
- provide/foundation/testing/mocking/fixtures.py +331 -0
- provide/foundation/testing/process/__init__.py +48 -0
- provide/foundation/testing/process/async_fixtures.py +405 -0
- provide/foundation/testing/process/fixtures.py +56 -0
- provide/foundation/testing/process/subprocess_fixtures.py +209 -0
- provide/foundation/testing/threading/__init__.py +38 -0
- provide/foundation/testing/threading/basic_fixtures.py +101 -0
- provide/foundation/testing/threading/data_fixtures.py +99 -0
- provide/foundation/testing/threading/execution_fixtures.py +263 -0
- provide/foundation/testing/threading/fixtures.py +54 -0
- provide/foundation/testing/threading/sync_fixtures.py +97 -0
- provide/foundation/testing/time/__init__.py +32 -0
- provide/foundation/testing/time/fixtures.py +409 -0
- provide/foundation/testing/transport/__init__.py +30 -0
- provide/foundation/testing/transport/fixtures.py +280 -0
- provide/foundation/tools/__init__.py +58 -0
- provide/foundation/tools/base.py +348 -0
- provide/foundation/tools/cache.py +268 -0
- provide/foundation/tools/downloader.py +224 -0
- provide/foundation/tools/installer.py +254 -0
- provide/foundation/tools/registry.py +223 -0
- provide/foundation/tools/resolver.py +321 -0
- provide/foundation/tools/verifier.py +186 -0
- provide/foundation/tracer/otel.py +7 -11
- provide/foundation/tracer/spans.py +2 -2
- provide/foundation/transport/__init__.py +155 -0
- provide/foundation/transport/base.py +171 -0
- provide/foundation/transport/client.py +266 -0
- provide/foundation/transport/config.py +140 -0
- provide/foundation/transport/errors.py +79 -0
- provide/foundation/transport/http.py +232 -0
- provide/foundation/transport/middleware.py +360 -0
- provide/foundation/transport/registry.py +167 -0
- provide/foundation/transport/types.py +45 -0
- provide/foundation/utils/deps.py +14 -12
- provide/foundation/utils/parsing.py +49 -4
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/METADATA +5 -28
- provide_foundation-0.0.0.dev2.dist-info/RECORD +225 -0
- provide/foundation/cli/commands/logs/generate_old.py +0 -569
- provide/foundation/crypto/certificates.py +0 -896
- provide/foundation/logger/emoji/__init__.py +0 -44
- provide/foundation/logger/emoji/matrix.py +0 -209
- provide/foundation/logger/emoji/sets.py +0 -458
- provide/foundation/logger/emoji/types.py +0 -56
- provide/foundation/logger/setup/emoji_resolver.py +0 -64
- provide_foundation-0.0.0.dev0.dist-info/RECORD +0 -149
- /provide/foundation/{observability → integrations}/openobserve/exceptions.py +0 -0
- /provide/foundation/{observability → integrations}/openobserve/models.py +0 -0
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/WHEEL +0 -0
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/entry_points.txt +0 -0
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/licenses/LICENSE +0 -0
- {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
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
+
]
|