provide-foundation 0.0.0.dev1__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 +29 -3
- provide/foundation/archive/operations.py +4 -6
- 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/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/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/crypto/certificates/operations.py +1 -1
- provide/foundation/errors/__init__.py +2 -3
- provide/foundation/errors/decorators.py +0 -231
- provide/foundation/errors/types.py +0 -97
- provide/foundation/file/directory.py +13 -22
- provide/foundation/file/lock.py +3 -1
- provide/foundation/hub/components.py +72 -384
- 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/config/logging.py +68 -298
- provide/foundation/logger/config/telemetry.py +41 -121
- provide/foundation/logger/setup/coordinator.py +1 -1
- 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 +33 -33
- 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 +7 -2
- provide/foundation/testing/cli.py +30 -17
- provide/foundation/testing/common/__init__.py +0 -2
- provide/foundation/testing/common/fixtures.py +0 -27
- provide/foundation/testing/file/content_fixtures.py +316 -0
- provide/foundation/testing/file/directory_fixtures.py +107 -0
- provide/foundation/testing/file/fixtures.py +45 -516
- provide/foundation/testing/file/special_fixtures.py +153 -0
- provide/foundation/testing/logger.py +76 -0
- provide/foundation/testing/process/async_fixtures.py +405 -0
- provide/foundation/testing/process/fixtures.py +50 -571
- provide/foundation/testing/process/subprocess_fixtures.py +209 -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 +34 -500
- provide/foundation/testing/threading/sync_fixtures.py +97 -0
- provide/foundation/testing/time/fixtures.py +4 -4
- provide/foundation/tools/cache.py +8 -6
- provide/foundation/tools/downloader.py +23 -12
- provide/foundation/tracer/spans.py +2 -2
- provide/foundation/transport/config.py +26 -95
- provide/foundation/transport/middleware.py +30 -36
- provide/foundation/utils/deps.py +14 -12
- provide/foundation/utils/parsing.py +49 -4
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev2.dist-info}/METADATA +1 -1
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev2.dist-info}/RECORD +93 -68
- /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.dev2.dist-info}/WHEEL +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev2.dist-info}/entry_points.txt +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev2.dist-info}/licenses/LICENSE +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev2.dist-info}/top_level.txt +0 -0
@@ -66,15 +66,20 @@ def __getattr__(name: str) -> Any:
|
|
66
66
|
"isolated_cli_runner",
|
67
67
|
"temp_config_file",
|
68
68
|
"create_test_cli",
|
69
|
-
"mock_logger",
|
70
69
|
"CliTestCase",
|
70
|
+
"click_testing_mode",
|
71
71
|
]:
|
72
72
|
import provide.foundation.testing.cli as cli_module
|
73
73
|
|
74
74
|
return getattr(cli_module, name)
|
75
75
|
|
76
76
|
# Logger testing utilities
|
77
|
-
elif name in [
|
77
|
+
elif name in [
|
78
|
+
"reset_foundation_setup_for_testing",
|
79
|
+
"reset_foundation_state",
|
80
|
+
"mock_logger",
|
81
|
+
"mock_logger_factory",
|
82
|
+
]:
|
78
83
|
import provide.foundation.testing.logger as logger_module
|
79
84
|
|
80
85
|
return getattr(logger_module, name)
|
@@ -15,14 +15,15 @@ from unittest.mock import MagicMock
|
|
15
15
|
|
16
16
|
import click
|
17
17
|
from click.testing import CliRunner
|
18
|
+
import pytest
|
18
19
|
|
19
|
-
from provide.foundation.context import
|
20
|
+
from provide.foundation.context import CLIContext
|
20
21
|
from provide.foundation.logger import get_logger
|
21
22
|
|
22
23
|
log = get_logger(__name__)
|
23
24
|
|
24
25
|
|
25
|
-
class MockContext(
|
26
|
+
class MockContext(CLIContext):
|
26
27
|
"""Mock context for testing that tracks method calls."""
|
27
28
|
|
28
29
|
def __init__(self, **kwargs) -> None:
|
@@ -157,7 +158,7 @@ def create_test_cli(
|
|
157
158
|
@click.pass_context
|
158
159
|
def cli(ctx, **kwargs) -> None:
|
159
160
|
"""Test CLI for testing."""
|
160
|
-
ctx.obj =
|
161
|
+
ctx.obj = CLIContext(**{k: v for k, v in kwargs.items() if v is not None})
|
161
162
|
|
162
163
|
if commands:
|
163
164
|
for cmd in commands:
|
@@ -166,20 +167,6 @@ def create_test_cli(
|
|
166
167
|
return cli
|
167
168
|
|
168
169
|
|
169
|
-
def mock_logger():
|
170
|
-
"""
|
171
|
-
Create a mock logger for testing.
|
172
|
-
|
173
|
-
Returns:
|
174
|
-
MagicMock with common logger methods
|
175
|
-
"""
|
176
|
-
mock = MagicMock()
|
177
|
-
mock.debug = MagicMock()
|
178
|
-
mock.info = MagicMock()
|
179
|
-
mock.warning = MagicMock()
|
180
|
-
mock.error = MagicMock()
|
181
|
-
mock.critical = MagicMock()
|
182
|
-
return mock
|
183
170
|
|
184
171
|
|
185
172
|
class CliTestCase:
|
@@ -225,3 +212,29 @@ class CliTestCase:
|
|
225
212
|
assert output[key] == value, (
|
226
213
|
f"Value mismatch for '{key}': {output[key]} != {value}"
|
227
214
|
)
|
215
|
+
|
216
|
+
|
217
|
+
@pytest.fixture
|
218
|
+
def click_testing_mode():
|
219
|
+
"""
|
220
|
+
Pytest fixture to enable Click testing mode.
|
221
|
+
|
222
|
+
Sets CLICK_TESTING=1 environment variable for the duration of the test,
|
223
|
+
then restores the original value. This fixture makes it easy to enable
|
224
|
+
Click testing mode without manual environment variable management.
|
225
|
+
|
226
|
+
Usage:
|
227
|
+
def test_my_cli(click_testing_mode):
|
228
|
+
# Test CLI code here - CLICK_TESTING is automatically set
|
229
|
+
pass
|
230
|
+
"""
|
231
|
+
original_value = os.environ.get("CLICK_TESTING")
|
232
|
+
os.environ["CLICK_TESTING"] = "1"
|
233
|
+
|
234
|
+
try:
|
235
|
+
yield
|
236
|
+
finally:
|
237
|
+
if original_value is None:
|
238
|
+
os.environ.pop("CLICK_TESTING", None)
|
239
|
+
else:
|
240
|
+
os.environ["CLICK_TESTING"] = original_value
|
@@ -7,7 +7,6 @@ in any project that depends on provide.foundation.
|
|
7
7
|
|
8
8
|
from provide.foundation.testing.common.fixtures import (
|
9
9
|
mock_http_config,
|
10
|
-
mock_logger,
|
11
10
|
mock_telemetry_config,
|
12
11
|
mock_config_source,
|
13
12
|
mock_event_emitter,
|
@@ -21,7 +20,6 @@ from provide.foundation.testing.common.fixtures import (
|
|
21
20
|
|
22
21
|
__all__ = [
|
23
22
|
"mock_http_config",
|
24
|
-
"mock_logger",
|
25
23
|
"mock_telemetry_config",
|
26
24
|
"mock_config_source",
|
27
25
|
"mock_event_emitter",
|
@@ -37,33 +37,6 @@ def mock_http_config():
|
|
37
37
|
)
|
38
38
|
|
39
39
|
|
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
40
|
|
68
41
|
|
69
42
|
@pytest.fixture
|
@@ -0,0 +1,316 @@
|
|
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
|
+
import random
|
11
|
+
import tempfile
|
12
|
+
from pathlib import Path
|
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,
|
90
|
+
suffix=suffix,
|
91
|
+
prefix=prefix,
|
92
|
+
dir=dir,
|
93
|
+
delete=delete
|
94
|
+
)
|
95
|
+
|
96
|
+
if content is not None:
|
97
|
+
if isinstance(content, str):
|
98
|
+
if 'b' in mode:
|
99
|
+
f.write(content.encode())
|
100
|
+
else:
|
101
|
+
f.write(content)
|
102
|
+
else:
|
103
|
+
f.write(content)
|
104
|
+
f.flush()
|
105
|
+
|
106
|
+
path = Path(f.name)
|
107
|
+
f.close()
|
108
|
+
|
109
|
+
if not delete:
|
110
|
+
created_files.append(path)
|
111
|
+
|
112
|
+
return path
|
113
|
+
|
114
|
+
yield _make_named_file
|
115
|
+
|
116
|
+
# Cleanup
|
117
|
+
for path in created_files:
|
118
|
+
safe_delete(path, missing_ok=True)
|
119
|
+
|
120
|
+
|
121
|
+
@pytest.fixture
|
122
|
+
def temp_file_with_content():
|
123
|
+
"""
|
124
|
+
Create temporary files with specific content.
|
125
|
+
|
126
|
+
Returns:
|
127
|
+
Function that creates files with content.
|
128
|
+
"""
|
129
|
+
created_files = []
|
130
|
+
|
131
|
+
def _make_file(
|
132
|
+
content: str | bytes,
|
133
|
+
suffix: str = ".txt",
|
134
|
+
encoding: str = "utf-8"
|
135
|
+
) -> Path:
|
136
|
+
"""
|
137
|
+
Create a temporary file with content.
|
138
|
+
|
139
|
+
Args:
|
140
|
+
content: Content to write
|
141
|
+
suffix: File suffix
|
142
|
+
encoding: Text encoding (for str content)
|
143
|
+
|
144
|
+
Returns:
|
145
|
+
Path to created file
|
146
|
+
"""
|
147
|
+
with tempfile.NamedTemporaryFile(
|
148
|
+
mode='wb' if isinstance(content, bytes) else 'w',
|
149
|
+
suffix=suffix,
|
150
|
+
delete=False,
|
151
|
+
encoding=None if isinstance(content, bytes) else encoding
|
152
|
+
) as f:
|
153
|
+
f.write(content)
|
154
|
+
path = Path(f.name)
|
155
|
+
|
156
|
+
created_files.append(path)
|
157
|
+
return path
|
158
|
+
|
159
|
+
yield _make_file
|
160
|
+
|
161
|
+
# Cleanup
|
162
|
+
for path in created_files:
|
163
|
+
safe_delete(path, missing_ok=True)
|
164
|
+
|
165
|
+
|
166
|
+
@pytest.fixture
|
167
|
+
def temp_binary_file():
|
168
|
+
"""
|
169
|
+
Create temporary binary files.
|
170
|
+
|
171
|
+
Returns:
|
172
|
+
Function that creates binary files.
|
173
|
+
"""
|
174
|
+
created_files = []
|
175
|
+
|
176
|
+
def _make_binary(
|
177
|
+
size: int = 1024,
|
178
|
+
pattern: bytes = None,
|
179
|
+
suffix: str = ".bin"
|
180
|
+
) -> Path:
|
181
|
+
"""
|
182
|
+
Create a temporary binary file.
|
183
|
+
|
184
|
+
Args:
|
185
|
+
size: File size in bytes
|
186
|
+
pattern: Optional byte pattern to repeat
|
187
|
+
suffix: File suffix
|
188
|
+
|
189
|
+
Returns:
|
190
|
+
Path to created binary file
|
191
|
+
"""
|
192
|
+
if pattern is None:
|
193
|
+
# Create pseudo-random binary data
|
194
|
+
content = bytes(random.randint(0, 255) for _ in range(size))
|
195
|
+
else:
|
196
|
+
# Repeat pattern to reach size
|
197
|
+
repetitions = size // len(pattern) + 1
|
198
|
+
content = (pattern * repetitions)[:size]
|
199
|
+
|
200
|
+
with tempfile.NamedTemporaryFile(
|
201
|
+
mode='wb',
|
202
|
+
suffix=suffix,
|
203
|
+
delete=False
|
204
|
+
) as f:
|
205
|
+
f.write(content)
|
206
|
+
path = Path(f.name)
|
207
|
+
|
208
|
+
created_files.append(path)
|
209
|
+
return path
|
210
|
+
|
211
|
+
yield _make_binary
|
212
|
+
|
213
|
+
# Cleanup
|
214
|
+
for path in created_files:
|
215
|
+
safe_delete(path, missing_ok=True)
|
216
|
+
|
217
|
+
|
218
|
+
@pytest.fixture
|
219
|
+
def temp_csv_file():
|
220
|
+
"""
|
221
|
+
Create temporary CSV files for testing.
|
222
|
+
|
223
|
+
Returns:
|
224
|
+
Function that creates CSV files.
|
225
|
+
"""
|
226
|
+
created_files = []
|
227
|
+
|
228
|
+
def _make_csv(
|
229
|
+
headers: list[str],
|
230
|
+
rows: list[list],
|
231
|
+
suffix: str = ".csv"
|
232
|
+
) -> Path:
|
233
|
+
"""
|
234
|
+
Create a temporary CSV file.
|
235
|
+
|
236
|
+
Args:
|
237
|
+
headers: Column headers
|
238
|
+
rows: Data rows
|
239
|
+
suffix: File suffix
|
240
|
+
|
241
|
+
Returns:
|
242
|
+
Path to created CSV file
|
243
|
+
"""
|
244
|
+
with tempfile.NamedTemporaryFile(
|
245
|
+
mode='w',
|
246
|
+
suffix=suffix,
|
247
|
+
delete=False,
|
248
|
+
newline=''
|
249
|
+
) as f:
|
250
|
+
writer = csv.writer(f)
|
251
|
+
writer.writerow(headers)
|
252
|
+
writer.writerows(rows)
|
253
|
+
path = Path(f.name)
|
254
|
+
|
255
|
+
created_files.append(path)
|
256
|
+
return path
|
257
|
+
|
258
|
+
yield _make_csv
|
259
|
+
|
260
|
+
# Cleanup
|
261
|
+
for path in created_files:
|
262
|
+
safe_delete(path, missing_ok=True)
|
263
|
+
|
264
|
+
|
265
|
+
@pytest.fixture
|
266
|
+
def temp_json_file():
|
267
|
+
"""
|
268
|
+
Create temporary JSON files for testing.
|
269
|
+
|
270
|
+
Returns:
|
271
|
+
Function that creates JSON files.
|
272
|
+
"""
|
273
|
+
created_files = []
|
274
|
+
|
275
|
+
def _make_json(
|
276
|
+
data: dict | list,
|
277
|
+
suffix: str = ".json",
|
278
|
+
indent: int = 2
|
279
|
+
) -> Path:
|
280
|
+
"""
|
281
|
+
Create a temporary JSON file.
|
282
|
+
|
283
|
+
Args:
|
284
|
+
data: JSON data to write
|
285
|
+
suffix: File suffix
|
286
|
+
indent: JSON indentation
|
287
|
+
|
288
|
+
Returns:
|
289
|
+
Path to created JSON file
|
290
|
+
"""
|
291
|
+
with tempfile.NamedTemporaryFile(
|
292
|
+
mode='w',
|
293
|
+
suffix=suffix,
|
294
|
+
delete=False
|
295
|
+
) as f:
|
296
|
+
json.dump(data, f, indent=indent)
|
297
|
+
path = Path(f.name)
|
298
|
+
|
299
|
+
created_files.append(path)
|
300
|
+
return path
|
301
|
+
|
302
|
+
yield _make_json
|
303
|
+
|
304
|
+
# Cleanup
|
305
|
+
for path in created_files:
|
306
|
+
safe_delete(path, missing_ok=True)
|
307
|
+
|
308
|
+
|
309
|
+
__all__ = [
|
310
|
+
"temp_file",
|
311
|
+
"temp_named_file",
|
312
|
+
"temp_file_with_content",
|
313
|
+
"temp_binary_file",
|
314
|
+
"temp_csv_file",
|
315
|
+
"temp_json_file",
|
316
|
+
]
|
@@ -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
|
+
import tempfile
|
9
|
+
from pathlib import Path
|
10
|
+
from collections.abc import Generator
|
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
|
+
"temp_directory",
|
104
|
+
"test_files_structure",
|
105
|
+
"nested_directory_structure",
|
106
|
+
"empty_directory",
|
107
|
+
]
|