provide-foundation 0.0.0.dev1__py3-none-any.whl → 0.0.0.dev3__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 +36 -10
- provide/foundation/archive/__init__.py +1 -1
- provide/foundation/archive/base.py +15 -14
- provide/foundation/archive/bzip2.py +40 -40
- provide/foundation/archive/gzip.py +42 -42
- provide/foundation/archive/operations.py +93 -96
- provide/foundation/archive/tar.py +33 -31
- provide/foundation/archive/zip.py +52 -50
- provide/foundation/asynctools/__init__.py +20 -0
- provide/foundation/asynctools/core.py +126 -0
- provide/foundation/cli/__init__.py +2 -2
- provide/foundation/cli/commands/deps.py +15 -9
- provide/foundation/cli/commands/logs/__init__.py +3 -3
- provide/foundation/cli/commands/logs/generate.py +2 -2
- provide/foundation/cli/commands/logs/query.py +4 -4
- provide/foundation/cli/commands/logs/send.py +3 -3
- provide/foundation/cli/commands/logs/tail.py +3 -3
- provide/foundation/cli/decorators.py +11 -11
- provide/foundation/cli/main.py +1 -1
- provide/foundation/cli/testing.py +2 -40
- provide/foundation/cli/utils.py +21 -18
- provide/foundation/config/__init__.py +35 -2
- provide/foundation/config/base.py +2 -2
- provide/foundation/config/converters.py +477 -0
- provide/foundation/config/defaults.py +67 -0
- provide/foundation/config/env.py +6 -20
- provide/foundation/config/loader.py +10 -4
- provide/foundation/config/sync.py +8 -6
- provide/foundation/config/types.py +5 -5
- provide/foundation/config/validators.py +4 -4
- provide/foundation/console/input.py +5 -5
- provide/foundation/console/output.py +36 -14
- provide/foundation/context/__init__.py +8 -4
- provide/foundation/context/core.py +88 -110
- provide/foundation/crypto/certificates/__init__.py +9 -5
- provide/foundation/crypto/certificates/base.py +2 -2
- provide/foundation/crypto/certificates/certificate.py +48 -19
- provide/foundation/crypto/certificates/factory.py +26 -18
- provide/foundation/crypto/certificates/generator.py +24 -23
- provide/foundation/crypto/certificates/loader.py +24 -16
- provide/foundation/crypto/certificates/operations.py +17 -10
- provide/foundation/crypto/certificates/trust.py +21 -21
- provide/foundation/env/__init__.py +28 -0
- provide/foundation/env/core.py +218 -0
- provide/foundation/errors/__init__.py +3 -3
- provide/foundation/errors/decorators.py +0 -234
- provide/foundation/errors/types.py +0 -98
- provide/foundation/eventsets/display.py +13 -14
- provide/foundation/eventsets/registry.py +61 -31
- provide/foundation/eventsets/resolver.py +50 -46
- provide/foundation/eventsets/sets/das.py +8 -8
- provide/foundation/eventsets/sets/database.py +14 -14
- provide/foundation/eventsets/sets/http.py +21 -21
- provide/foundation/eventsets/sets/llm.py +16 -16
- provide/foundation/eventsets/sets/task_queue.py +13 -13
- provide/foundation/eventsets/types.py +7 -7
- provide/foundation/file/directory.py +14 -23
- provide/foundation/file/lock.py +4 -3
- provide/foundation/hub/components.py +75 -389
- provide/foundation/hub/config.py +157 -0
- provide/foundation/hub/discovery.py +63 -0
- provide/foundation/hub/handlers.py +89 -0
- provide/foundation/hub/lifecycle.py +195 -0
- provide/foundation/hub/manager.py +7 -4
- provide/foundation/hub/processors.py +49 -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 +14 -14
- provide/foundation/{observability → integrations}/openobserve/commands.py +12 -12
- provide/foundation/integrations/openobserve/config.py +37 -0
- provide/foundation/{observability → integrations}/openobserve/formatters.py +1 -1
- provide/foundation/{observability → integrations}/openobserve/otlp.py +2 -2
- provide/foundation/{observability → integrations}/openobserve/search.py +2 -3
- provide/foundation/{observability → integrations}/openobserve/streaming.py +5 -5
- provide/foundation/logger/__init__.py +0 -1
- provide/foundation/logger/config/base.py +1 -1
- provide/foundation/logger/config/logging.py +69 -299
- provide/foundation/logger/config/telemetry.py +39 -121
- provide/foundation/logger/factories.py +2 -2
- provide/foundation/logger/processors/main.py +12 -10
- provide/foundation/logger/ratelimit/limiters.py +4 -4
- provide/foundation/logger/ratelimit/processor.py +1 -1
- provide/foundation/logger/setup/coordinator.py +39 -25
- provide/foundation/logger/setup/processors.py +3 -3
- provide/foundation/logger/setup/testing.py +14 -0
- provide/foundation/logger/trace.py +5 -5
- provide/foundation/metrics/__init__.py +1 -1
- provide/foundation/metrics/otel.py +3 -1
- provide/foundation/observability/__init__.py +3 -3
- provide/foundation/process/__init__.py +9 -0
- provide/foundation/process/exit.py +48 -0
- provide/foundation/process/lifecycle.py +69 -46
- provide/foundation/resilience/__init__.py +36 -0
- provide/foundation/resilience/circuit.py +166 -0
- provide/foundation/resilience/decorators.py +236 -0
- provide/foundation/resilience/fallback.py +208 -0
- provide/foundation/resilience/retry.py +327 -0
- provide/foundation/serialization/__init__.py +16 -0
- provide/foundation/serialization/core.py +70 -0
- provide/foundation/streams/config.py +78 -0
- provide/foundation/streams/console.py +4 -5
- provide/foundation/streams/core.py +5 -2
- provide/foundation/streams/file.py +12 -2
- provide/foundation/testing/__init__.py +29 -9
- provide/foundation/testing/archive/__init__.py +7 -7
- provide/foundation/testing/archive/fixtures.py +58 -54
- provide/foundation/testing/cli.py +30 -20
- provide/foundation/testing/common/__init__.py +13 -15
- provide/foundation/testing/common/fixtures.py +27 -57
- provide/foundation/testing/file/__init__.py +15 -15
- provide/foundation/testing/file/content_fixtures.py +289 -0
- provide/foundation/testing/file/directory_fixtures.py +107 -0
- provide/foundation/testing/file/fixtures.py +42 -516
- provide/foundation/testing/file/special_fixtures.py +145 -0
- provide/foundation/testing/logger.py +89 -8
- provide/foundation/testing/mocking/__init__.py +21 -21
- provide/foundation/testing/mocking/fixtures.py +80 -67
- provide/foundation/testing/process/__init__.py +23 -23
- provide/foundation/testing/process/async_fixtures.py +414 -0
- provide/foundation/testing/process/fixtures.py +48 -571
- provide/foundation/testing/process/subprocess_fixtures.py +210 -0
- provide/foundation/testing/threading/__init__.py +17 -17
- provide/foundation/testing/threading/basic_fixtures.py +105 -0
- provide/foundation/testing/threading/data_fixtures.py +101 -0
- provide/foundation/testing/threading/execution_fixtures.py +278 -0
- provide/foundation/testing/threading/fixtures.py +32 -502
- provide/foundation/testing/threading/sync_fixtures.py +100 -0
- provide/foundation/testing/time/__init__.py +11 -11
- provide/foundation/testing/time/fixtures.py +95 -83
- provide/foundation/testing/transport/__init__.py +9 -9
- provide/foundation/testing/transport/fixtures.py +54 -54
- provide/foundation/time/__init__.py +18 -0
- provide/foundation/time/core.py +63 -0
- provide/foundation/tools/__init__.py +2 -2
- provide/foundation/tools/base.py +68 -67
- provide/foundation/tools/cache.py +69 -74
- provide/foundation/tools/downloader.py +68 -62
- provide/foundation/tools/installer.py +51 -57
- provide/foundation/tools/registry.py +38 -45
- provide/foundation/tools/resolver.py +70 -68
- provide/foundation/tools/verifier.py +39 -50
- provide/foundation/tracer/spans.py +2 -14
- provide/foundation/transport/__init__.py +26 -33
- provide/foundation/transport/base.py +32 -30
- provide/foundation/transport/client.py +44 -49
- provide/foundation/transport/config.py +36 -107
- provide/foundation/transport/errors.py +13 -27
- provide/foundation/transport/http.py +69 -55
- provide/foundation/transport/middleware.py +113 -114
- provide/foundation/transport/registry.py +29 -27
- provide/foundation/transport/types.py +6 -6
- provide/foundation/utils/deps.py +17 -14
- provide/foundation/utils/parsing.py +49 -4
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/METADATA +2 -2
- provide_foundation-0.0.0.dev3.dist-info/RECORD +233 -0
- provide_foundation-0.0.0.dev1.dist-info/RECORD +0 -200
- /provide/foundation/{observability → integrations}/openobserve/exceptions.py +0 -0
- /provide/foundation/{observability → integrations}/openobserve/models.py +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/WHEEL +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/entry_points.txt +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/licenses/LICENSE +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/top_level.txt +0 -0
@@ -5,8 +5,7 @@ Reusable mock objects for configuration, logging, and other common
|
|
5
5
|
testing scenarios across the provide-io ecosystem.
|
6
6
|
"""
|
7
7
|
|
8
|
-
from unittest.mock import Mock,
|
9
|
-
from typing import Any
|
8
|
+
from unittest.mock import Mock, PropertyMock
|
10
9
|
|
11
10
|
import pytest
|
12
11
|
|
@@ -18,12 +17,12 @@ from provide.foundation.logger.config.logging import LoggingConfig
|
|
18
17
|
def mock_http_config():
|
19
18
|
"""
|
20
19
|
Standard HTTP configuration for testing.
|
21
|
-
|
20
|
+
|
22
21
|
Returns:
|
23
22
|
HTTPConfig with common test settings.
|
24
23
|
"""
|
25
24
|
from provide.foundation.transport.config import HTTPConfig
|
26
|
-
|
25
|
+
|
27
26
|
return HTTPConfig(
|
28
27
|
timeout=30.0,
|
29
28
|
max_retries=3,
|
@@ -37,40 +36,11 @@ def mock_http_config():
|
|
37
36
|
)
|
38
37
|
|
39
38
|
|
40
|
-
@pytest.fixture
|
41
|
-
def mock_logger():
|
42
|
-
"""
|
43
|
-
Mock logger with captured method calls.
|
44
|
-
|
45
|
-
Returns:
|
46
|
-
Mock logger with debug, info, warning, error methods.
|
47
|
-
"""
|
48
|
-
logger = Mock()
|
49
|
-
logger.debug = Mock()
|
50
|
-
logger.info = Mock()
|
51
|
-
logger.warning = Mock()
|
52
|
-
logger.error = Mock()
|
53
|
-
logger.exception = Mock()
|
54
|
-
logger.critical = Mock()
|
55
|
-
|
56
|
-
# Add common logger attributes
|
57
|
-
logger.name = "mock_logger"
|
58
|
-
logger.level = 10 # DEBUG level
|
59
|
-
logger.handlers = []
|
60
|
-
logger.disabled = False
|
61
|
-
|
62
|
-
# Add bind method for structlog compatibility
|
63
|
-
logger.bind = Mock(return_value=logger)
|
64
|
-
logger.unbind = Mock(return_value=logger)
|
65
|
-
|
66
|
-
return logger
|
67
|
-
|
68
|
-
|
69
39
|
@pytest.fixture
|
70
40
|
def mock_telemetry_config():
|
71
41
|
"""
|
72
42
|
Standard telemetry configuration for testing.
|
73
|
-
|
43
|
+
|
74
44
|
Returns:
|
75
45
|
TelemetryConfig with debug logging enabled.
|
76
46
|
"""
|
@@ -86,7 +56,7 @@ def mock_telemetry_config():
|
|
86
56
|
def mock_config_source():
|
87
57
|
"""
|
88
58
|
Mock configuration source for testing config loading.
|
89
|
-
|
59
|
+
|
90
60
|
Returns:
|
91
61
|
Mock that simulates a configuration source.
|
92
62
|
"""
|
@@ -97,7 +67,7 @@ def mock_config_source():
|
|
97
67
|
source.watch = Mock()
|
98
68
|
source.priority = 100
|
99
69
|
source.name = "mock_source"
|
100
|
-
|
70
|
+
|
101
71
|
return source
|
102
72
|
|
103
73
|
|
@@ -105,7 +75,7 @@ def mock_config_source():
|
|
105
75
|
def mock_event_emitter():
|
106
76
|
"""
|
107
77
|
Mock event emitter for testing event-driven components.
|
108
|
-
|
78
|
+
|
109
79
|
Returns:
|
110
80
|
Mock with emit, on, off methods.
|
111
81
|
"""
|
@@ -116,7 +86,7 @@ def mock_event_emitter():
|
|
116
86
|
emitter.once = Mock()
|
117
87
|
emitter.listeners = Mock(return_value=[])
|
118
88
|
emitter.remove_all_listeners = Mock()
|
119
|
-
|
89
|
+
|
120
90
|
return emitter
|
121
91
|
|
122
92
|
|
@@ -124,7 +94,7 @@ def mock_event_emitter():
|
|
124
94
|
def mock_transport():
|
125
95
|
"""
|
126
96
|
Mock transport for testing network operations.
|
127
|
-
|
97
|
+
|
128
98
|
Returns:
|
129
99
|
Mock transport with request/response methods.
|
130
100
|
"""
|
@@ -135,7 +105,7 @@ def mock_transport():
|
|
135
105
|
transport.put = Mock(return_value={"status": 200, "data": {}})
|
136
106
|
transport.delete = Mock(return_value={"status": 204})
|
137
107
|
transport.close = Mock()
|
138
|
-
|
108
|
+
|
139
109
|
return transport
|
140
110
|
|
141
111
|
|
@@ -143,7 +113,7 @@ def mock_transport():
|
|
143
113
|
def mock_metrics_collector():
|
144
114
|
"""
|
145
115
|
Mock metrics collector for testing instrumentation.
|
146
|
-
|
116
|
+
|
147
117
|
Returns:
|
148
118
|
Mock with common metrics methods.
|
149
119
|
"""
|
@@ -154,13 +124,13 @@ def mock_metrics_collector():
|
|
154
124
|
collector.histogram = Mock()
|
155
125
|
collector.timer = Mock()
|
156
126
|
collector.flush = Mock()
|
157
|
-
|
127
|
+
|
158
128
|
# Add context manager support for timing
|
159
129
|
timer_cm = Mock()
|
160
130
|
timer_cm.__enter__ = Mock(return_value=timer_cm)
|
161
131
|
timer_cm.__exit__ = Mock(return_value=None)
|
162
132
|
collector.timer.return_value = timer_cm
|
163
|
-
|
133
|
+
|
164
134
|
return collector
|
165
135
|
|
166
136
|
|
@@ -168,12 +138,12 @@ def mock_metrics_collector():
|
|
168
138
|
def mock_cache():
|
169
139
|
"""
|
170
140
|
Mock cache for testing caching behavior.
|
171
|
-
|
141
|
+
|
172
142
|
Returns:
|
173
143
|
Mock with get, set, delete, clear methods.
|
174
144
|
"""
|
175
145
|
cache_data = {}
|
176
|
-
|
146
|
+
|
177
147
|
cache = Mock()
|
178
148
|
cache.get = Mock(side_effect=lambda k, default=None: cache_data.get(k, default))
|
179
149
|
cache.set = Mock(side_effect=lambda k, v, ttl=None: cache_data.update({k: v}))
|
@@ -181,10 +151,10 @@ def mock_cache():
|
|
181
151
|
cache.clear = Mock(side_effect=cache_data.clear)
|
182
152
|
cache.exists = Mock(side_effect=lambda k: k in cache_data)
|
183
153
|
cache.keys = Mock(return_value=list(cache_data.keys()))
|
184
|
-
|
154
|
+
|
185
155
|
# Store reference to data for test assertions
|
186
156
|
cache._data = cache_data
|
187
|
-
|
157
|
+
|
188
158
|
return cache
|
189
159
|
|
190
160
|
|
@@ -192,7 +162,7 @@ def mock_cache():
|
|
192
162
|
def mock_database():
|
193
163
|
"""
|
194
164
|
Mock database connection for testing.
|
195
|
-
|
165
|
+
|
196
166
|
Returns:
|
197
167
|
Mock with execute, fetch, commit, rollback methods.
|
198
168
|
"""
|
@@ -205,11 +175,11 @@ def mock_database():
|
|
205
175
|
db.rollback = Mock()
|
206
176
|
db.close = Mock()
|
207
177
|
db.is_connected = PropertyMock(return_value=True)
|
208
|
-
|
178
|
+
|
209
179
|
# Add context manager support
|
210
180
|
db.__enter__ = Mock(return_value=db)
|
211
181
|
db.__exit__ = Mock(return_value=None)
|
212
|
-
|
182
|
+
|
213
183
|
return db
|
214
184
|
|
215
185
|
|
@@ -217,7 +187,7 @@ def mock_database():
|
|
217
187
|
def mock_file_system():
|
218
188
|
"""
|
219
189
|
Mock file system operations.
|
220
|
-
|
190
|
+
|
221
191
|
Returns:
|
222
192
|
Mock with read, write, exists, delete methods.
|
223
193
|
"""
|
@@ -230,7 +200,7 @@ def mock_file_system():
|
|
230
200
|
fs.rmdir = Mock()
|
231
201
|
fs.list = Mock(return_value=[])
|
232
202
|
fs.stat = Mock(return_value=Mock(st_size=1024, st_mtime=0))
|
233
|
-
|
203
|
+
|
234
204
|
return fs
|
235
205
|
|
236
206
|
|
@@ -238,19 +208,19 @@ def mock_file_system():
|
|
238
208
|
def mock_subprocess():
|
239
209
|
"""
|
240
210
|
Mock subprocess for testing command execution.
|
241
|
-
|
211
|
+
|
242
212
|
Returns:
|
243
213
|
Mock with run, Popen methods.
|
244
214
|
"""
|
245
215
|
subprocess = Mock()
|
246
|
-
|
216
|
+
|
247
217
|
# Mock run method
|
248
218
|
result = Mock()
|
249
219
|
result.returncode = 0
|
250
220
|
result.stdout = "output"
|
251
221
|
result.stderr = ""
|
252
222
|
subprocess.run = Mock(return_value=result)
|
253
|
-
|
223
|
+
|
254
224
|
# Mock Popen
|
255
225
|
process = Mock()
|
256
226
|
process.communicate = Mock(return_value=("output", ""))
|
@@ -259,5 +229,5 @@ def mock_subprocess():
|
|
259
229
|
process.poll = Mock(return_value=0)
|
260
230
|
process.wait = Mock(return_value=0)
|
261
231
|
subprocess.Popen = Mock(return_value=process)
|
262
|
-
|
263
|
-
return subprocess
|
232
|
+
|
233
|
+
return subprocess
|
@@ -6,35 +6,35 @@ across any project that depends on provide.foundation.
|
|
6
6
|
"""
|
7
7
|
|
8
8
|
from provide.foundation.testing.file.fixtures import (
|
9
|
-
temp_directory,
|
10
|
-
test_files_structure,
|
11
|
-
temp_file,
|
12
9
|
binary_file,
|
13
|
-
nested_directory_structure,
|
14
10
|
empty_directory,
|
11
|
+
nested_directory_structure,
|
15
12
|
readonly_file,
|
16
|
-
temp_named_file,
|
17
|
-
temp_file_with_content,
|
18
13
|
temp_binary_file,
|
19
14
|
temp_csv_file,
|
15
|
+
temp_directory,
|
16
|
+
temp_executable_file,
|
17
|
+
temp_file,
|
18
|
+
temp_file_with_content,
|
20
19
|
temp_json_file,
|
20
|
+
temp_named_file,
|
21
21
|
temp_symlink,
|
22
|
-
|
22
|
+
test_files_structure,
|
23
23
|
)
|
24
24
|
|
25
25
|
__all__ = [
|
26
|
-
"temp_directory",
|
27
|
-
"test_files_structure",
|
28
|
-
"temp_file",
|
29
26
|
"binary_file",
|
30
|
-
"nested_directory_structure",
|
31
27
|
"empty_directory",
|
28
|
+
"nested_directory_structure",
|
32
29
|
"readonly_file",
|
33
|
-
"temp_named_file",
|
34
|
-
"temp_file_with_content",
|
35
30
|
"temp_binary_file",
|
36
31
|
"temp_csv_file",
|
32
|
+
"temp_directory",
|
33
|
+
"temp_executable_file",
|
34
|
+
"temp_file",
|
35
|
+
"temp_file_with_content",
|
37
36
|
"temp_json_file",
|
37
|
+
"temp_named_file",
|
38
38
|
"temp_symlink",
|
39
|
-
"
|
40
|
-
]
|
39
|
+
"test_files_structure",
|
40
|
+
]
|
@@ -0,0 +1,289 @@
|
|
1
|
+
"""
|
2
|
+
Content-based file test fixtures.
|
3
|
+
|
4
|
+
Fixtures for creating files with specific content types like text, binary,
|
5
|
+
CSV, JSON, and other structured data.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import csv
|
9
|
+
import json
|
10
|
+
from pathlib import Path
|
11
|
+
import random
|
12
|
+
import tempfile
|
13
|
+
|
14
|
+
import pytest
|
15
|
+
|
16
|
+
from provide.foundation.file.safe import safe_delete
|
17
|
+
|
18
|
+
|
19
|
+
@pytest.fixture
|
20
|
+
def temp_file():
|
21
|
+
"""
|
22
|
+
Create a temporary file factory with optional content.
|
23
|
+
|
24
|
+
Returns:
|
25
|
+
A function that creates temporary files with specified content and suffix.
|
26
|
+
"""
|
27
|
+
created_files = []
|
28
|
+
|
29
|
+
def _make_temp_file(content: str = "test content", suffix: str = ".txt") -> Path:
|
30
|
+
"""
|
31
|
+
Create a temporary file.
|
32
|
+
|
33
|
+
Args:
|
34
|
+
content: Content to write to the file
|
35
|
+
suffix: File suffix/extension
|
36
|
+
|
37
|
+
Returns:
|
38
|
+
Path to the created temporary file
|
39
|
+
"""
|
40
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=suffix, delete=False) as f:
|
41
|
+
f.write(content)
|
42
|
+
path = Path(f.name)
|
43
|
+
created_files.append(path)
|
44
|
+
return path
|
45
|
+
|
46
|
+
yield _make_temp_file
|
47
|
+
|
48
|
+
# Cleanup all created files
|
49
|
+
for path in created_files:
|
50
|
+
safe_delete(path, missing_ok=True)
|
51
|
+
|
52
|
+
|
53
|
+
@pytest.fixture
|
54
|
+
def temp_named_file():
|
55
|
+
"""
|
56
|
+
Create a named temporary file factory.
|
57
|
+
|
58
|
+
Returns:
|
59
|
+
Function that creates named temporary files.
|
60
|
+
"""
|
61
|
+
created_files = []
|
62
|
+
|
63
|
+
def _make_named_file(
|
64
|
+
content: bytes | str = None,
|
65
|
+
suffix: str = "",
|
66
|
+
prefix: str = "tmp",
|
67
|
+
dir: Path | str = None,
|
68
|
+
mode: str = "w+b",
|
69
|
+
delete: bool = False,
|
70
|
+
) -> Path:
|
71
|
+
"""
|
72
|
+
Create a named temporary file.
|
73
|
+
|
74
|
+
Args:
|
75
|
+
content: Optional content to write
|
76
|
+
suffix: File suffix
|
77
|
+
prefix: File prefix
|
78
|
+
dir: Directory for the file
|
79
|
+
mode: File mode
|
80
|
+
delete: Whether to delete on close
|
81
|
+
|
82
|
+
Returns:
|
83
|
+
Path to the created file
|
84
|
+
"""
|
85
|
+
if isinstance(dir, Path):
|
86
|
+
dir = str(dir)
|
87
|
+
|
88
|
+
f = tempfile.NamedTemporaryFile(
|
89
|
+
mode=mode, suffix=suffix, prefix=prefix, dir=dir, delete=delete
|
90
|
+
)
|
91
|
+
|
92
|
+
if content is not None:
|
93
|
+
if isinstance(content, str):
|
94
|
+
if "b" in mode:
|
95
|
+
f.write(content.encode())
|
96
|
+
else:
|
97
|
+
f.write(content)
|
98
|
+
else:
|
99
|
+
f.write(content)
|
100
|
+
f.flush()
|
101
|
+
|
102
|
+
path = Path(f.name)
|
103
|
+
f.close()
|
104
|
+
|
105
|
+
if not delete:
|
106
|
+
created_files.append(path)
|
107
|
+
|
108
|
+
return path
|
109
|
+
|
110
|
+
yield _make_named_file
|
111
|
+
|
112
|
+
# Cleanup
|
113
|
+
for path in created_files:
|
114
|
+
safe_delete(path, missing_ok=True)
|
115
|
+
|
116
|
+
|
117
|
+
@pytest.fixture
|
118
|
+
def temp_file_with_content():
|
119
|
+
"""
|
120
|
+
Create temporary files with specific content.
|
121
|
+
|
122
|
+
Returns:
|
123
|
+
Function that creates files with content.
|
124
|
+
"""
|
125
|
+
created_files = []
|
126
|
+
|
127
|
+
def _make_file(
|
128
|
+
content: str | bytes, suffix: str = ".txt", encoding: str = "utf-8"
|
129
|
+
) -> Path:
|
130
|
+
"""
|
131
|
+
Create a temporary file with content.
|
132
|
+
|
133
|
+
Args:
|
134
|
+
content: Content to write
|
135
|
+
suffix: File suffix
|
136
|
+
encoding: Text encoding (for str content)
|
137
|
+
|
138
|
+
Returns:
|
139
|
+
Path to created file
|
140
|
+
"""
|
141
|
+
with tempfile.NamedTemporaryFile(
|
142
|
+
mode="wb" if isinstance(content, bytes) else "w",
|
143
|
+
suffix=suffix,
|
144
|
+
delete=False,
|
145
|
+
encoding=None if isinstance(content, bytes) else encoding,
|
146
|
+
) as f:
|
147
|
+
f.write(content)
|
148
|
+
path = Path(f.name)
|
149
|
+
|
150
|
+
created_files.append(path)
|
151
|
+
return path
|
152
|
+
|
153
|
+
yield _make_file
|
154
|
+
|
155
|
+
# Cleanup
|
156
|
+
for path in created_files:
|
157
|
+
safe_delete(path, missing_ok=True)
|
158
|
+
|
159
|
+
|
160
|
+
@pytest.fixture
|
161
|
+
def temp_binary_file():
|
162
|
+
"""
|
163
|
+
Create temporary binary files.
|
164
|
+
|
165
|
+
Returns:
|
166
|
+
Function that creates binary files.
|
167
|
+
"""
|
168
|
+
created_files = []
|
169
|
+
|
170
|
+
def _make_binary(
|
171
|
+
size: int = 1024, pattern: bytes = None, suffix: str = ".bin"
|
172
|
+
) -> Path:
|
173
|
+
"""
|
174
|
+
Create a temporary binary file.
|
175
|
+
|
176
|
+
Args:
|
177
|
+
size: File size in bytes
|
178
|
+
pattern: Optional byte pattern to repeat
|
179
|
+
suffix: File suffix
|
180
|
+
|
181
|
+
Returns:
|
182
|
+
Path to created binary file
|
183
|
+
"""
|
184
|
+
if pattern is None:
|
185
|
+
# Create pseudo-random binary data
|
186
|
+
content = bytes(random.randint(0, 255) for _ in range(size))
|
187
|
+
else:
|
188
|
+
# Repeat pattern to reach size
|
189
|
+
repetitions = size // len(pattern) + 1
|
190
|
+
content = (pattern * repetitions)[:size]
|
191
|
+
|
192
|
+
with tempfile.NamedTemporaryFile(mode="wb", suffix=suffix, delete=False) as f:
|
193
|
+
f.write(content)
|
194
|
+
path = Path(f.name)
|
195
|
+
|
196
|
+
created_files.append(path)
|
197
|
+
return path
|
198
|
+
|
199
|
+
yield _make_binary
|
200
|
+
|
201
|
+
# Cleanup
|
202
|
+
for path in created_files:
|
203
|
+
safe_delete(path, missing_ok=True)
|
204
|
+
|
205
|
+
|
206
|
+
@pytest.fixture
|
207
|
+
def temp_csv_file():
|
208
|
+
"""
|
209
|
+
Create temporary CSV files for testing.
|
210
|
+
|
211
|
+
Returns:
|
212
|
+
Function that creates CSV files.
|
213
|
+
"""
|
214
|
+
created_files = []
|
215
|
+
|
216
|
+
def _make_csv(headers: list[str], rows: list[list], suffix: str = ".csv") -> Path:
|
217
|
+
"""
|
218
|
+
Create a temporary CSV file.
|
219
|
+
|
220
|
+
Args:
|
221
|
+
headers: Column headers
|
222
|
+
rows: Data rows
|
223
|
+
suffix: File suffix
|
224
|
+
|
225
|
+
Returns:
|
226
|
+
Path to created CSV file
|
227
|
+
"""
|
228
|
+
with tempfile.NamedTemporaryFile(
|
229
|
+
mode="w", suffix=suffix, delete=False, newline=""
|
230
|
+
) as f:
|
231
|
+
writer = csv.writer(f)
|
232
|
+
writer.writerow(headers)
|
233
|
+
writer.writerows(rows)
|
234
|
+
path = Path(f.name)
|
235
|
+
|
236
|
+
created_files.append(path)
|
237
|
+
return path
|
238
|
+
|
239
|
+
yield _make_csv
|
240
|
+
|
241
|
+
# Cleanup
|
242
|
+
for path in created_files:
|
243
|
+
safe_delete(path, missing_ok=True)
|
244
|
+
|
245
|
+
|
246
|
+
@pytest.fixture
|
247
|
+
def temp_json_file():
|
248
|
+
"""
|
249
|
+
Create temporary JSON files for testing.
|
250
|
+
|
251
|
+
Returns:
|
252
|
+
Function that creates JSON files.
|
253
|
+
"""
|
254
|
+
created_files = []
|
255
|
+
|
256
|
+
def _make_json(data: dict | list, suffix: str = ".json", indent: int = 2) -> Path:
|
257
|
+
"""
|
258
|
+
Create a temporary JSON file.
|
259
|
+
|
260
|
+
Args:
|
261
|
+
data: JSON data to write
|
262
|
+
suffix: File suffix
|
263
|
+
indent: JSON indentation
|
264
|
+
|
265
|
+
Returns:
|
266
|
+
Path to created JSON file
|
267
|
+
"""
|
268
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=suffix, delete=False) as f:
|
269
|
+
json.dump(data, f, indent=indent)
|
270
|
+
path = Path(f.name)
|
271
|
+
|
272
|
+
created_files.append(path)
|
273
|
+
return path
|
274
|
+
|
275
|
+
yield _make_json
|
276
|
+
|
277
|
+
# Cleanup
|
278
|
+
for path in created_files:
|
279
|
+
safe_delete(path, missing_ok=True)
|
280
|
+
|
281
|
+
|
282
|
+
__all__ = [
|
283
|
+
"temp_binary_file",
|
284
|
+
"temp_csv_file",
|
285
|
+
"temp_file",
|
286
|
+
"temp_file_with_content",
|
287
|
+
"temp_json_file",
|
288
|
+
"temp_named_file",
|
289
|
+
]
|
@@ -0,0 +1,107 @@
|
|
1
|
+
"""
|
2
|
+
Directory-specific test fixtures.
|
3
|
+
|
4
|
+
Fixtures for creating temporary directories, nested structures,
|
5
|
+
and standard test directory layouts.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from collections.abc import Generator
|
9
|
+
from pathlib import Path
|
10
|
+
import tempfile
|
11
|
+
|
12
|
+
import pytest
|
13
|
+
|
14
|
+
|
15
|
+
@pytest.fixture
|
16
|
+
def temp_directory() -> Generator[Path, None, None]:
|
17
|
+
"""
|
18
|
+
Create a temporary directory that's cleaned up after test.
|
19
|
+
|
20
|
+
Yields:
|
21
|
+
Path to the temporary directory.
|
22
|
+
"""
|
23
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
24
|
+
yield Path(temp_dir)
|
25
|
+
|
26
|
+
|
27
|
+
@pytest.fixture
|
28
|
+
def test_files_structure() -> Generator[tuple[Path, Path], None, None]:
|
29
|
+
"""
|
30
|
+
Create standard test file structure with files and subdirectories.
|
31
|
+
|
32
|
+
Creates:
|
33
|
+
- source/
|
34
|
+
- file1.txt (contains "Content 1")
|
35
|
+
- file2.txt (contains "Content 2")
|
36
|
+
- subdir/
|
37
|
+
- file3.txt (contains "Content 3")
|
38
|
+
|
39
|
+
Yields:
|
40
|
+
Tuple of (temp_path, source_path)
|
41
|
+
"""
|
42
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
43
|
+
path = Path(temp_dir)
|
44
|
+
source = path / "source"
|
45
|
+
source.mkdir()
|
46
|
+
|
47
|
+
# Create test files
|
48
|
+
(source / "file1.txt").write_text("Content 1")
|
49
|
+
(source / "file2.txt").write_text("Content 2")
|
50
|
+
|
51
|
+
# Create subdirectory with files
|
52
|
+
subdir = source / "subdir"
|
53
|
+
subdir.mkdir()
|
54
|
+
(subdir / "file3.txt").write_text("Content 3")
|
55
|
+
|
56
|
+
yield path, source
|
57
|
+
|
58
|
+
|
59
|
+
@pytest.fixture
|
60
|
+
def nested_directory_structure() -> Generator[Path, None, None]:
|
61
|
+
"""
|
62
|
+
Create a deeply nested directory structure for testing.
|
63
|
+
|
64
|
+
Creates:
|
65
|
+
- level1/
|
66
|
+
- level2/
|
67
|
+
- level3/
|
68
|
+
- deep_file.txt
|
69
|
+
- file_l2.txt
|
70
|
+
- file_l1.txt
|
71
|
+
|
72
|
+
Yields:
|
73
|
+
Path to the root of the structure.
|
74
|
+
"""
|
75
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
76
|
+
root = Path(temp_dir)
|
77
|
+
|
78
|
+
# Create nested structure
|
79
|
+
deep_dir = root / "level1" / "level2" / "level3"
|
80
|
+
deep_dir.mkdir(parents=True)
|
81
|
+
|
82
|
+
# Add files at different levels
|
83
|
+
(root / "file_l1.txt").write_text("Level 1 file")
|
84
|
+
(root / "level1" / "file_l2.txt").write_text("Level 2 file")
|
85
|
+
(deep_dir / "deep_file.txt").write_text("Deep file")
|
86
|
+
|
87
|
+
yield root
|
88
|
+
|
89
|
+
|
90
|
+
@pytest.fixture
|
91
|
+
def empty_directory() -> Generator[Path, None, None]:
|
92
|
+
"""
|
93
|
+
Create an empty temporary directory.
|
94
|
+
|
95
|
+
Yields:
|
96
|
+
Path to an empty directory.
|
97
|
+
"""
|
98
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
99
|
+
yield Path(temp_dir)
|
100
|
+
|
101
|
+
|
102
|
+
__all__ = [
|
103
|
+
"empty_directory",
|
104
|
+
"nested_directory_structure",
|
105
|
+
"temp_directory",
|
106
|
+
"test_files_structure",
|
107
|
+
]
|