provide-foundation 0.0.0.dev0__py3-none-any.whl → 0.0.0.dev1__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 +12 -20
- 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 +336 -0
- provide/foundation/archive/tar.py +164 -0
- provide/foundation/archive/zip.py +203 -0
- provide/foundation/config/base.py +2 -2
- provide/foundation/config/sync.py +19 -4
- 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/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/hub/components.py +7 -133
- provide/foundation/logger/__init__.py +3 -10
- provide/foundation/logger/config/logging.py +6 -6
- 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 +75 -23
- provide/foundation/logger/setup/processors.py +2 -9
- provide/foundation/logger/trace.py +27 -0
- provide/foundation/metrics/otel.py +10 -10
- provide/foundation/process/lifecycle.py +82 -26
- provide/foundation/testing/__init__.py +77 -0
- provide/foundation/testing/archive/__init__.py +24 -0
- provide/foundation/testing/archive/fixtures.py +217 -0
- provide/foundation/testing/common/__init__.py +34 -0
- provide/foundation/testing/common/fixtures.py +263 -0
- provide/foundation/testing/file/__init__.py +40 -0
- provide/foundation/testing/file/fixtures.py +523 -0
- provide/foundation/testing/logger.py +41 -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/fixtures.py +577 -0
- provide/foundation/testing/threading/__init__.py +38 -0
- provide/foundation/testing/threading/fixtures.py +520 -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 +266 -0
- provide/foundation/tools/downloader.py +213 -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/transport/__init__.py +155 -0
- provide/foundation/transport/base.py +171 -0
- provide/foundation/transport/client.py +266 -0
- provide/foundation/transport/config.py +209 -0
- provide/foundation/transport/errors.py +79 -0
- provide/foundation/transport/http.py +232 -0
- provide/foundation/transport/middleware.py +366 -0
- provide/foundation/transport/registry.py +167 -0
- provide/foundation/transport/types.py +45 -0
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev1.dist-info}/METADATA +5 -28
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev1.dist-info}/RECORD +85 -34
- 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 → provide_foundation-0.0.0.dev1.dist-info}/WHEEL +0 -0
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev1.dist-info}/entry_points.txt +0 -0
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev1.dist-info}/licenses/LICENSE +0 -0
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,523 @@
|
|
1
|
+
"""
|
2
|
+
File and Directory Test Fixtures.
|
3
|
+
|
4
|
+
Common fixtures for testing file operations, creating temporary directories,
|
5
|
+
and standard test file structures used across the provide-io ecosystem.
|
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 temp_file():
|
61
|
+
"""
|
62
|
+
Create a temporary file factory with optional content.
|
63
|
+
|
64
|
+
Returns:
|
65
|
+
A function that creates temporary files with specified content and suffix.
|
66
|
+
"""
|
67
|
+
created_files = []
|
68
|
+
|
69
|
+
def _make_temp_file(content: str = "test content", suffix: str = ".txt") -> Path:
|
70
|
+
"""
|
71
|
+
Create a temporary file.
|
72
|
+
|
73
|
+
Args:
|
74
|
+
content: Content to write to the file
|
75
|
+
suffix: File suffix/extension
|
76
|
+
|
77
|
+
Returns:
|
78
|
+
Path to the created temporary file
|
79
|
+
"""
|
80
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix=suffix, delete=False) as f:
|
81
|
+
f.write(content)
|
82
|
+
path = Path(f.name)
|
83
|
+
created_files.append(path)
|
84
|
+
return path
|
85
|
+
|
86
|
+
yield _make_temp_file
|
87
|
+
|
88
|
+
# Cleanup all created files
|
89
|
+
for path in created_files:
|
90
|
+
path.unlink(missing_ok=True)
|
91
|
+
|
92
|
+
|
93
|
+
@pytest.fixture
|
94
|
+
def binary_file() -> Generator[Path, None, None]:
|
95
|
+
"""
|
96
|
+
Create a temporary binary file for testing.
|
97
|
+
|
98
|
+
Yields:
|
99
|
+
Path to a binary file containing sample binary data.
|
100
|
+
"""
|
101
|
+
with tempfile.NamedTemporaryFile(mode='wb', suffix='.bin', delete=False) as f:
|
102
|
+
# Write some binary data
|
103
|
+
f.write(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09')
|
104
|
+
f.write(b'\xFF\xFE\xFD\xFC\xFB\xFA\xF9\xF8\xF7\xF6')
|
105
|
+
path = Path(f.name)
|
106
|
+
|
107
|
+
yield path
|
108
|
+
path.unlink(missing_ok=True)
|
109
|
+
|
110
|
+
|
111
|
+
@pytest.fixture
|
112
|
+
def nested_directory_structure() -> Generator[Path, None, None]:
|
113
|
+
"""
|
114
|
+
Create a deeply nested directory structure for testing.
|
115
|
+
|
116
|
+
Creates:
|
117
|
+
- level1/
|
118
|
+
- level2/
|
119
|
+
- level3/
|
120
|
+
- deep_file.txt
|
121
|
+
- file_l2.txt
|
122
|
+
- file_l1.txt
|
123
|
+
|
124
|
+
Yields:
|
125
|
+
Path to the root of the structure.
|
126
|
+
"""
|
127
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
128
|
+
root = Path(temp_dir)
|
129
|
+
|
130
|
+
# Create nested structure
|
131
|
+
deep_dir = root / "level1" / "level2" / "level3"
|
132
|
+
deep_dir.mkdir(parents=True)
|
133
|
+
|
134
|
+
# Add files at different levels
|
135
|
+
(root / "file_l1.txt").write_text("Level 1 file")
|
136
|
+
(root / "level1" / "file_l2.txt").write_text("Level 2 file")
|
137
|
+
(deep_dir / "deep_file.txt").write_text("Deep file")
|
138
|
+
|
139
|
+
yield root
|
140
|
+
|
141
|
+
|
142
|
+
@pytest.fixture
|
143
|
+
def empty_directory() -> Generator[Path, None, None]:
|
144
|
+
"""
|
145
|
+
Create an empty temporary directory.
|
146
|
+
|
147
|
+
Yields:
|
148
|
+
Path to an empty directory.
|
149
|
+
"""
|
150
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
151
|
+
yield Path(temp_dir)
|
152
|
+
|
153
|
+
|
154
|
+
@pytest.fixture
|
155
|
+
def readonly_file() -> Generator[Path, None, None]:
|
156
|
+
"""
|
157
|
+
Create a read-only file for permission testing.
|
158
|
+
|
159
|
+
Yields:
|
160
|
+
Path to a read-only file.
|
161
|
+
"""
|
162
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as f:
|
163
|
+
f.write("Read-only content")
|
164
|
+
path = Path(f.name)
|
165
|
+
|
166
|
+
# Make file read-only
|
167
|
+
path.chmod(0o444)
|
168
|
+
|
169
|
+
yield path
|
170
|
+
|
171
|
+
# Restore write permission for cleanup
|
172
|
+
path.chmod(0o644)
|
173
|
+
path.unlink(missing_ok=True)
|
174
|
+
|
175
|
+
|
176
|
+
@pytest.fixture
|
177
|
+
def temp_named_file():
|
178
|
+
"""
|
179
|
+
Create a named temporary file factory.
|
180
|
+
|
181
|
+
Returns:
|
182
|
+
Function that creates named temporary files.
|
183
|
+
"""
|
184
|
+
created_files = []
|
185
|
+
|
186
|
+
def _make_named_file(
|
187
|
+
content: bytes | str = None,
|
188
|
+
suffix: str = "",
|
189
|
+
prefix: str = "tmp",
|
190
|
+
dir: Path | str = None,
|
191
|
+
mode: str = 'w+b',
|
192
|
+
delete: bool = False
|
193
|
+
) -> Path:
|
194
|
+
"""
|
195
|
+
Create a named temporary file.
|
196
|
+
|
197
|
+
Args:
|
198
|
+
content: Optional content to write
|
199
|
+
suffix: File suffix
|
200
|
+
prefix: File prefix
|
201
|
+
dir: Directory for the file
|
202
|
+
mode: File mode
|
203
|
+
delete: Whether to delete on close
|
204
|
+
|
205
|
+
Returns:
|
206
|
+
Path to the created file
|
207
|
+
"""
|
208
|
+
if isinstance(dir, Path):
|
209
|
+
dir = str(dir)
|
210
|
+
|
211
|
+
f = tempfile.NamedTemporaryFile(
|
212
|
+
mode=mode,
|
213
|
+
suffix=suffix,
|
214
|
+
prefix=prefix,
|
215
|
+
dir=dir,
|
216
|
+
delete=delete
|
217
|
+
)
|
218
|
+
|
219
|
+
if content is not None:
|
220
|
+
if isinstance(content, str):
|
221
|
+
if 'b' in mode:
|
222
|
+
f.write(content.encode())
|
223
|
+
else:
|
224
|
+
f.write(content)
|
225
|
+
else:
|
226
|
+
f.write(content)
|
227
|
+
f.flush()
|
228
|
+
|
229
|
+
path = Path(f.name)
|
230
|
+
f.close()
|
231
|
+
|
232
|
+
if not delete:
|
233
|
+
created_files.append(path)
|
234
|
+
|
235
|
+
return path
|
236
|
+
|
237
|
+
yield _make_named_file
|
238
|
+
|
239
|
+
# Cleanup
|
240
|
+
for path in created_files:
|
241
|
+
path.unlink(missing_ok=True)
|
242
|
+
|
243
|
+
|
244
|
+
@pytest.fixture
|
245
|
+
def temp_file_with_content():
|
246
|
+
"""
|
247
|
+
Create temporary files with specific content.
|
248
|
+
|
249
|
+
Returns:
|
250
|
+
Function that creates files with content.
|
251
|
+
"""
|
252
|
+
created_files = []
|
253
|
+
|
254
|
+
def _make_file(
|
255
|
+
content: str | bytes,
|
256
|
+
suffix: str = ".txt",
|
257
|
+
encoding: str = "utf-8"
|
258
|
+
) -> Path:
|
259
|
+
"""
|
260
|
+
Create a temporary file with content.
|
261
|
+
|
262
|
+
Args:
|
263
|
+
content: Content to write
|
264
|
+
suffix: File suffix
|
265
|
+
encoding: Text encoding (for str content)
|
266
|
+
|
267
|
+
Returns:
|
268
|
+
Path to created file
|
269
|
+
"""
|
270
|
+
with tempfile.NamedTemporaryFile(
|
271
|
+
mode='wb' if isinstance(content, bytes) else 'w',
|
272
|
+
suffix=suffix,
|
273
|
+
delete=False,
|
274
|
+
encoding=None if isinstance(content, bytes) else encoding
|
275
|
+
) as f:
|
276
|
+
f.write(content)
|
277
|
+
path = Path(f.name)
|
278
|
+
|
279
|
+
created_files.append(path)
|
280
|
+
return path
|
281
|
+
|
282
|
+
yield _make_file
|
283
|
+
|
284
|
+
# Cleanup
|
285
|
+
for path in created_files:
|
286
|
+
path.unlink(missing_ok=True)
|
287
|
+
|
288
|
+
|
289
|
+
@pytest.fixture
|
290
|
+
def temp_binary_file():
|
291
|
+
"""
|
292
|
+
Create temporary binary files.
|
293
|
+
|
294
|
+
Returns:
|
295
|
+
Function that creates binary files.
|
296
|
+
"""
|
297
|
+
created_files = []
|
298
|
+
|
299
|
+
def _make_binary(
|
300
|
+
size: int = 1024,
|
301
|
+
pattern: bytes = None,
|
302
|
+
suffix: str = ".bin"
|
303
|
+
) -> Path:
|
304
|
+
"""
|
305
|
+
Create a temporary binary file.
|
306
|
+
|
307
|
+
Args:
|
308
|
+
size: File size in bytes
|
309
|
+
pattern: Optional byte pattern to repeat
|
310
|
+
suffix: File suffix
|
311
|
+
|
312
|
+
Returns:
|
313
|
+
Path to created binary file
|
314
|
+
"""
|
315
|
+
if pattern is None:
|
316
|
+
# Create pseudo-random binary data
|
317
|
+
import random
|
318
|
+
content = bytes(random.randint(0, 255) for _ in range(size))
|
319
|
+
else:
|
320
|
+
# Repeat pattern to reach size
|
321
|
+
repetitions = size // len(pattern) + 1
|
322
|
+
content = (pattern * repetitions)[:size]
|
323
|
+
|
324
|
+
with tempfile.NamedTemporaryFile(
|
325
|
+
mode='wb',
|
326
|
+
suffix=suffix,
|
327
|
+
delete=False
|
328
|
+
) as f:
|
329
|
+
f.write(content)
|
330
|
+
path = Path(f.name)
|
331
|
+
|
332
|
+
created_files.append(path)
|
333
|
+
return path
|
334
|
+
|
335
|
+
yield _make_binary
|
336
|
+
|
337
|
+
# Cleanup
|
338
|
+
for path in created_files:
|
339
|
+
path.unlink(missing_ok=True)
|
340
|
+
|
341
|
+
|
342
|
+
@pytest.fixture
|
343
|
+
def temp_csv_file():
|
344
|
+
"""
|
345
|
+
Create temporary CSV files for testing.
|
346
|
+
|
347
|
+
Returns:
|
348
|
+
Function that creates CSV files.
|
349
|
+
"""
|
350
|
+
import csv
|
351
|
+
created_files = []
|
352
|
+
|
353
|
+
def _make_csv(
|
354
|
+
headers: list[str],
|
355
|
+
rows: list[list],
|
356
|
+
suffix: str = ".csv"
|
357
|
+
) -> Path:
|
358
|
+
"""
|
359
|
+
Create a temporary CSV file.
|
360
|
+
|
361
|
+
Args:
|
362
|
+
headers: Column headers
|
363
|
+
rows: Data rows
|
364
|
+
suffix: File suffix
|
365
|
+
|
366
|
+
Returns:
|
367
|
+
Path to created CSV file
|
368
|
+
"""
|
369
|
+
with tempfile.NamedTemporaryFile(
|
370
|
+
mode='w',
|
371
|
+
suffix=suffix,
|
372
|
+
delete=False,
|
373
|
+
newline=''
|
374
|
+
) as f:
|
375
|
+
writer = csv.writer(f)
|
376
|
+
writer.writerow(headers)
|
377
|
+
writer.writerows(rows)
|
378
|
+
path = Path(f.name)
|
379
|
+
|
380
|
+
created_files.append(path)
|
381
|
+
return path
|
382
|
+
|
383
|
+
yield _make_csv
|
384
|
+
|
385
|
+
# Cleanup
|
386
|
+
for path in created_files:
|
387
|
+
path.unlink(missing_ok=True)
|
388
|
+
|
389
|
+
|
390
|
+
@pytest.fixture
|
391
|
+
def temp_json_file():
|
392
|
+
"""
|
393
|
+
Create temporary JSON files for testing.
|
394
|
+
|
395
|
+
Returns:
|
396
|
+
Function that creates JSON files.
|
397
|
+
"""
|
398
|
+
import json
|
399
|
+
created_files = []
|
400
|
+
|
401
|
+
def _make_json(
|
402
|
+
data: dict | list,
|
403
|
+
suffix: str = ".json",
|
404
|
+
indent: int = 2
|
405
|
+
) -> Path:
|
406
|
+
"""
|
407
|
+
Create a temporary JSON file.
|
408
|
+
|
409
|
+
Args:
|
410
|
+
data: JSON data to write
|
411
|
+
suffix: File suffix
|
412
|
+
indent: JSON indentation
|
413
|
+
|
414
|
+
Returns:
|
415
|
+
Path to created JSON file
|
416
|
+
"""
|
417
|
+
with tempfile.NamedTemporaryFile(
|
418
|
+
mode='w',
|
419
|
+
suffix=suffix,
|
420
|
+
delete=False
|
421
|
+
) as f:
|
422
|
+
json.dump(data, f, indent=indent)
|
423
|
+
path = Path(f.name)
|
424
|
+
|
425
|
+
created_files.append(path)
|
426
|
+
return path
|
427
|
+
|
428
|
+
yield _make_json
|
429
|
+
|
430
|
+
# Cleanup
|
431
|
+
for path in created_files:
|
432
|
+
path.unlink(missing_ok=True)
|
433
|
+
|
434
|
+
|
435
|
+
@pytest.fixture
|
436
|
+
def temp_symlink():
|
437
|
+
"""
|
438
|
+
Create temporary symbolic links for testing.
|
439
|
+
|
440
|
+
Returns:
|
441
|
+
Function that creates symbolic links.
|
442
|
+
"""
|
443
|
+
created_links = []
|
444
|
+
|
445
|
+
def _make_symlink(
|
446
|
+
target: Path | str,
|
447
|
+
link_name: Path | str = None
|
448
|
+
) -> Path:
|
449
|
+
"""
|
450
|
+
Create a temporary symbolic link.
|
451
|
+
|
452
|
+
Args:
|
453
|
+
target: Target path for the symlink
|
454
|
+
link_name: Optional link name (auto-generated if None)
|
455
|
+
|
456
|
+
Returns:
|
457
|
+
Path to created symlink
|
458
|
+
"""
|
459
|
+
target = Path(target)
|
460
|
+
|
461
|
+
if link_name is None:
|
462
|
+
with tempfile.NamedTemporaryFile(delete=True) as f:
|
463
|
+
link_name = Path(f.name + "_link")
|
464
|
+
else:
|
465
|
+
link_name = Path(link_name)
|
466
|
+
|
467
|
+
link_name.symlink_to(target)
|
468
|
+
created_links.append(link_name)
|
469
|
+
|
470
|
+
return link_name
|
471
|
+
|
472
|
+
yield _make_symlink
|
473
|
+
|
474
|
+
# Cleanup
|
475
|
+
for link in created_links:
|
476
|
+
link.unlink(missing_ok=True)
|
477
|
+
|
478
|
+
|
479
|
+
@pytest.fixture
|
480
|
+
def temp_executable_file():
|
481
|
+
"""
|
482
|
+
Create temporary executable files for testing.
|
483
|
+
|
484
|
+
Returns:
|
485
|
+
Function that creates executable files.
|
486
|
+
"""
|
487
|
+
import stat
|
488
|
+
created_files = []
|
489
|
+
|
490
|
+
def _make_executable(
|
491
|
+
content: str = "#!/bin/sh\necho 'test'\n",
|
492
|
+
suffix: str = ".sh"
|
493
|
+
) -> Path:
|
494
|
+
"""
|
495
|
+
Create a temporary executable file.
|
496
|
+
|
497
|
+
Args:
|
498
|
+
content: Script content
|
499
|
+
suffix: File suffix
|
500
|
+
|
501
|
+
Returns:
|
502
|
+
Path to created executable file
|
503
|
+
"""
|
504
|
+
with tempfile.NamedTemporaryFile(
|
505
|
+
mode='w',
|
506
|
+
suffix=suffix,
|
507
|
+
delete=False
|
508
|
+
) as f:
|
509
|
+
f.write(content)
|
510
|
+
path = Path(f.name)
|
511
|
+
|
512
|
+
# Make executable
|
513
|
+
current = path.stat().st_mode
|
514
|
+
path.chmod(current | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
|
515
|
+
|
516
|
+
created_files.append(path)
|
517
|
+
return path
|
518
|
+
|
519
|
+
yield _make_executable
|
520
|
+
|
521
|
+
# Cleanup
|
522
|
+
for path in created_files:
|
523
|
+
path.unlink(missing_ok=True)
|
@@ -25,41 +25,52 @@ def _reset_opentelemetry_providers() -> None:
|
|
25
25
|
and stream closure issues by properly resetting the global providers.
|
26
26
|
"""
|
27
27
|
try:
|
28
|
-
# Reset tracing provider
|
28
|
+
# Reset tracing provider more thoroughly
|
29
29
|
import opentelemetry.trace as otel_trace
|
30
|
-
|
30
|
+
|
31
|
+
# Reset the Once flag to allow re-initialization
|
31
32
|
if hasattr(otel_trace, "_TRACER_PROVIDER_SET_ONCE"):
|
32
33
|
once_obj = otel_trace._TRACER_PROVIDER_SET_ONCE
|
33
34
|
if hasattr(once_obj, "_done"):
|
34
35
|
once_obj._done = False
|
36
|
+
if hasattr(once_obj, "_lock"):
|
37
|
+
with once_obj._lock:
|
38
|
+
once_obj._done = False
|
39
|
+
|
35
40
|
# Reset to NoOpTracerProvider
|
36
41
|
from opentelemetry.trace import NoOpTracerProvider
|
37
|
-
|
38
|
-
|
42
|
+
otel_trace.set_tracer_provider(NoOpTracerProvider())
|
43
|
+
|
39
44
|
except ImportError:
|
40
45
|
# OpenTelemetry tracing not available
|
41
46
|
pass
|
42
47
|
except Exception:
|
43
|
-
# Ignore errors during reset
|
48
|
+
# Ignore errors during reset - better to continue than fail
|
44
49
|
pass
|
45
50
|
|
46
51
|
try:
|
47
|
-
# Reset metrics provider
|
52
|
+
# Reset metrics provider more thoroughly
|
53
|
+
import opentelemetry.metrics as otel_metrics
|
48
54
|
import opentelemetry.metrics._internal as otel_metrics_internal
|
49
|
-
|
55
|
+
|
56
|
+
# Reset the Once flag to allow re-initialization
|
50
57
|
if hasattr(otel_metrics_internal, "_METER_PROVIDER_SET_ONCE"):
|
51
58
|
once_obj = otel_metrics_internal._METER_PROVIDER_SET_ONCE
|
52
59
|
if hasattr(once_obj, "_done"):
|
53
60
|
once_obj._done = False
|
61
|
+
if hasattr(once_obj, "_lock"):
|
62
|
+
with once_obj._lock:
|
63
|
+
once_obj._done = False
|
64
|
+
|
54
65
|
# Reset to NoOpMeterProvider
|
55
66
|
from opentelemetry.metrics import NoOpMeterProvider
|
56
|
-
|
57
|
-
|
67
|
+
otel_metrics.set_meter_provider(NoOpMeterProvider())
|
68
|
+
|
58
69
|
except ImportError:
|
59
70
|
# OpenTelemetry metrics not available
|
60
71
|
pass
|
61
72
|
except Exception:
|
62
|
-
# Ignore errors during reset
|
73
|
+
# Ignore errors during reset - better to continue than fail
|
63
74
|
pass
|
64
75
|
|
65
76
|
|
@@ -81,7 +92,10 @@ def reset_foundation_state() -> None:
|
|
81
92
|
reset_streams()
|
82
93
|
|
83
94
|
# Reset OpenTelemetry providers to avoid "Overriding" warnings and stream closure
|
84
|
-
|
95
|
+
# Note: OpenTelemetry providers are designed to prevent override for safety.
|
96
|
+
# In test environments, we suppress this reset to avoid hanging/blocking.
|
97
|
+
# The warnings are harmless in test context.
|
98
|
+
# _reset_opentelemetry_providers()
|
85
99
|
|
86
100
|
# Reset foundation logger state
|
87
101
|
foundation_logger._is_configured_by_setup = False
|
@@ -97,7 +111,23 @@ def reset_foundation_setup_for_testing() -> None:
|
|
97
111
|
This function ensures clean test isolation by resetting all
|
98
112
|
Foundation logging state between test runs.
|
99
113
|
"""
|
114
|
+
# Full reset but with improved OpenTelemetry handling
|
100
115
|
reset_foundation_state()
|
116
|
+
|
117
|
+
# Clear and re-initialize the hub for test isolation
|
118
|
+
try:
|
119
|
+
from provide.foundation.hub.manager import clear_hub
|
120
|
+
clear_hub()
|
121
|
+
except ImportError:
|
122
|
+
pass
|
123
|
+
|
124
|
+
# Re-register HTTP transport for tests that need it
|
125
|
+
try:
|
126
|
+
from provide.foundation.transport.http import _register_http_transport
|
127
|
+
_register_http_transport()
|
128
|
+
except ImportError:
|
129
|
+
# Transport module not available
|
130
|
+
pass
|
101
131
|
|
102
132
|
|
103
133
|
__all__ = [
|
@@ -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
|
+
]
|