provide-testkit 0.5.0__tar.gz → 0.5.2__tar.gz
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_testkit-0.5.0/src/provide_testkit.egg-info → provide_testkit-0.5.2}/PKG-INFO +1 -1
- provide_testkit-0.5.2/VERSION +1 -0
- provide_testkit-0.5.2/src/provide/testkit/_capture.py +81 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/_early_init.py +3 -1
- provide_testkit-0.5.2/src/provide/testkit/_streams.py +73 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/pytest_plugin.py +31 -11
- {provide_testkit-0.5.0 → provide_testkit-0.5.2/src/provide_testkit.egg-info}/PKG-INFO +1 -1
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide_testkit.egg-info/SOURCES.txt +5 -1
- provide_testkit-0.5.2/tests/test_capture_stream_swap.py +142 -0
- provide_testkit-0.5.2/tests/test_stream_encoding.py +109 -0
- provide_testkit-0.5.0/VERSION +0 -1
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/LICENSE +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/README.md +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/pyproject.toml +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/setup.cfg +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/__init__.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/__init__.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/__main__.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/_blocker.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/_install_blocker.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/_install_pth.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/base/__init__.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/base/foundation.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/base/harness.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/base/minimal.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/chaos/__init__.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/chaos/concurrency_strategies.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/chaos/fixtures.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/chaos/io_strategies.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/chaos/strategies.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/chaos/time_strategies.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/cli/__init__.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/cli/runner.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/cli/testing.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/common/__init__.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/common/fixtures.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/conftest.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/crypto/__init__.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/crypto/fixtures.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/file/__init__.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/file/content_fixtures.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/file/directory_fixtures.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/file/fixtures.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/file/special_fixtures.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/file/temp.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/hub/__init__.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/hub/fixtures.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/logger/__init__.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/logger/hooks.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/logger/mocks.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/logger/reset.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/main.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/mocking/__init__.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/mocking/fixtures.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/mocking/time.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/process/__init__.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/process/async_fixtures.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/process/fixtures.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/process/script_assertions.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/process/script_fixtures.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/process/subprocess_fixtures.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/process/system_fixtures.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/provide_testkit_init.pth +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/py.typed +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/__init__.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/artifacts.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/base.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/cli.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/complexity/__init__.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/complexity/analyzer.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/complexity/fixture.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/coverage/__init__.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/coverage/fixture.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/coverage/reporter.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/coverage/tracker.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/decorators.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/documentation/__init__.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/documentation/checker.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/documentation/fixture.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/profiling/__init__.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/profiling/fixture.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/profiling/profiler.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/report.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/runner.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/security/__init__.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/security/fixture.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/security/gitleaks_scanner.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/security/pip_audit_scanner.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/security/safety_scanner.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/security/scanner.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/security/semgrep_scanner.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/security/trufflehog_scanner.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/streams/__init__.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/streams/testing.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/threading/__init__.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/threading/basic_fixtures.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/threading/data_fixtures.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/threading/execution_fixtures.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/threading/fixtures.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/threading/sync_fixtures.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/time/__init__.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/time/classes.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/time/controlled.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/time/fixtures.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/time/freezing.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/time/measurement.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/time/mocking.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/time/rate_limiting.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/transport/__init__.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/transport/fixtures.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/utils/__init__.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/utils/environment.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/utils/temp_env.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide_testkit.egg-info/dependency_links.txt +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide_testkit.egg-info/entry_points.txt +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide_testkit.egg-info/requires.txt +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide_testkit.egg-info/top_level.txt +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/tests/test_async_fixtures.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/tests/test_early_init.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/tests/test_placeholder.py +0 -0
- {provide_testkit-0.5.0 → provide_testkit-0.5.2}/tests/test_pth_installation.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.5.2
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#
|
|
2
|
+
# SPDX-FileCopyrightText: Copyright (c) provide.io llc. All rights reserved.
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
"""Capture that gives back the stream it took, not the one it remembers.
|
|
7
|
+
|
|
8
|
+
``SysCaptureBase.suspend`` hands ``sys.stdout`` back to the stream pytest saved
|
|
9
|
+
before capture started, and ``resume`` reinstates ``tmpfile`` -- the stream
|
|
10
|
+
capture installed at ``start``. Neither looks at what is actually there. A test
|
|
11
|
+
that swaps the stream after capture started owns something pytest has no record
|
|
12
|
+
of, so the swap is discarded on the first suspend and every write after it lands
|
|
13
|
+
in pytest's buffer instead of the caller's.
|
|
14
|
+
|
|
15
|
+
``log_cli = true`` makes that ordinary: pytest's live-log handler sits on the
|
|
16
|
+
root logger and suspends global capture around each record. One log line emitted
|
|
17
|
+
from inside ``CliRunner.invoke`` -- from any library, at any depth -- leaves
|
|
18
|
+
``result.output`` empty while the text itself is real and lands in pytest's own
|
|
19
|
+
capture. ``contextlib.redirect_stdout`` and hand-rolled stream fixtures lose
|
|
20
|
+
their swap the same way.
|
|
21
|
+
|
|
22
|
+
The pair below remembers the stream that was in place at suspend and returns it
|
|
23
|
+
at resume, which is what the two methods already claim to do. Teardown is
|
|
24
|
+
untouched: ``done`` still restores the stream capture replaced, so a swap left
|
|
25
|
+
behind by a test cannot outlive it.
|
|
26
|
+
|
|
27
|
+
This belongs in pytest, and the tests in ``tests/test_capture_stream_swap.py``
|
|
28
|
+
drive the real ``SysCapture``, so they fail loudly once pytest changes the
|
|
29
|
+
methods underneath -- whether it fixes this or reshapes the class. Drop this
|
|
30
|
+
module when the pytest floor carries the fix.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from __future__ import annotations
|
|
34
|
+
|
|
35
|
+
import sys
|
|
36
|
+
from typing import Any
|
|
37
|
+
|
|
38
|
+
from _pytest.capture import SysCaptureBase
|
|
39
|
+
|
|
40
|
+
__all__ = ["install_capture_swap_fix"]
|
|
41
|
+
|
|
42
|
+
_INSTALLED_MARKER = "_provide_testkit_swap_safe"
|
|
43
|
+
# The stream is parked on pytest's object, so the name says whose it is.
|
|
44
|
+
_SWAPPED_IN = "_provide_testkit_swapped_in"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _suspend(self: SysCaptureBase[Any]) -> None:
|
|
48
|
+
self._assert_state("suspend", ("started", "suspended"))
|
|
49
|
+
# Suspending while suspended is legal, and the stream sitting there then is
|
|
50
|
+
# the one suspend itself installed -- not a swap worth remembering.
|
|
51
|
+
if self._state == "started":
|
|
52
|
+
setattr(self, _SWAPPED_IN, getattr(sys, self.name))
|
|
53
|
+
setattr(sys, self.name, self._old)
|
|
54
|
+
self._state = "suspended"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _resume(self: SysCaptureBase[Any]) -> None:
|
|
58
|
+
self._assert_state("resume", ("started", "suspended"))
|
|
59
|
+
if self._state == "started":
|
|
60
|
+
return
|
|
61
|
+
# A capture resumed without ever suspending has nothing recorded, and the
|
|
62
|
+
# stream capture installed is the right answer for it.
|
|
63
|
+
setattr(sys, self.name, getattr(self, _SWAPPED_IN, self.tmpfile))
|
|
64
|
+
self._state = "started"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def install_capture_swap_fix() -> bool:
|
|
68
|
+
"""Make pytest's stream capture preserve a swap made after it started.
|
|
69
|
+
|
|
70
|
+
Returns whether this call is the one that installed it. Declining leaves
|
|
71
|
+
pytest exactly as found: the fix is worth having, and silently layering it
|
|
72
|
+
over a patch someone else installed is not.
|
|
73
|
+
"""
|
|
74
|
+
if getattr(SysCaptureBase, _INSTALLED_MARKER, False):
|
|
75
|
+
return False
|
|
76
|
+
if any(getattr(SysCaptureBase, name).__module__ != "_pytest.capture" for name in ("suspend", "resume")):
|
|
77
|
+
return False
|
|
78
|
+
SysCaptureBase.suspend = _suspend # type: ignore[method-assign]
|
|
79
|
+
SysCaptureBase.resume = _resume # type: ignore[method-assign]
|
|
80
|
+
setattr(SysCaptureBase, _INSTALLED_MARKER, True)
|
|
81
|
+
return True
|
|
@@ -124,6 +124,8 @@ def _configure_structlog_for_testing() -> None:
|
|
|
124
124
|
event_dict.pop("_foundation_level_hint", None)
|
|
125
125
|
return event_dict
|
|
126
126
|
|
|
127
|
+
from provide.testkit._streams import unicode_safe
|
|
128
|
+
|
|
127
129
|
structlog.configure(
|
|
128
130
|
processors=[
|
|
129
131
|
structlog.processors.TimeStamper(fmt="iso"),
|
|
@@ -134,7 +136,7 @@ def _configure_structlog_for_testing() -> None:
|
|
|
134
136
|
wrapper_class=structlog.BoundLogger,
|
|
135
137
|
context_class=dict,
|
|
136
138
|
# Use stderr instead of stdout to avoid polluting pytest-xdist pipe communication
|
|
137
|
-
logger_factory=structlog.PrintLoggerFactory(file=sys.stderr),
|
|
139
|
+
logger_factory=structlog.PrintLoggerFactory(file=unicode_safe(sys.stderr)),
|
|
138
140
|
cache_logger_on_first_use=False, # Disable caching for test isolation
|
|
139
141
|
)
|
|
140
142
|
except Exception:
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#
|
|
2
|
+
# SPDX-FileCopyrightText: Copyright (c) provide.io llc. All rights reserved.
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
"""Keeping test logging off the path where a console encoding can raise.
|
|
7
|
+
|
|
8
|
+
This module deliberately imports nothing from Foundation. The plugin that uses
|
|
9
|
+
it configures structlog before any Foundation module is imported, and reaching
|
|
10
|
+
for Foundation here would defeat that ordering.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
__all__ = ["unicode_safe"]
|
|
18
|
+
|
|
19
|
+
_UTF8_NAMES = frozenset({"utf8", "utf_8", "u8", "cp65001"})
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class _UnicodeSafeStream:
|
|
23
|
+
"""A stream whose writes cannot fail on a character it cannot encode.
|
|
24
|
+
|
|
25
|
+
The replacement is written through the same text layer rather than the byte
|
|
26
|
+
layer underneath, so pytest's capture of that stream still sees the output.
|
|
27
|
+
|
|
28
|
+
Everything other than `write` is delegated, so the wrapped stream answers
|
|
29
|
+
for its own `isatty`, `flush` and `encoding`.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
# structlog takes a weak reference to the file it logs through, so the
|
|
33
|
+
# slotted class has to allow one.
|
|
34
|
+
__slots__ = ("__weakref__", "_stream")
|
|
35
|
+
|
|
36
|
+
def __init__(self, stream: Any) -> None:
|
|
37
|
+
self._stream = stream
|
|
38
|
+
|
|
39
|
+
def write(self, text: str) -> int:
|
|
40
|
+
try:
|
|
41
|
+
written = self._stream.write(text)
|
|
42
|
+
except UnicodeEncodeError:
|
|
43
|
+
encoding = getattr(self._stream, "encoding", None) or "ascii"
|
|
44
|
+
# backslashreplace names the character (\U0001f40d) where replace
|
|
45
|
+
# would erase it ('?'), and a test log line is read by a person.
|
|
46
|
+
safe = text.encode(encoding, "backslashreplace").decode(encoding, "replace")
|
|
47
|
+
self._stream.write(safe)
|
|
48
|
+
return len(text)
|
|
49
|
+
# colorama's Windows wrapper returns None rather than a count, so the
|
|
50
|
+
# length of what was handed over stands in for it.
|
|
51
|
+
return written if isinstance(written, int) else len(text)
|
|
52
|
+
|
|
53
|
+
def __getattr__(self, name: str) -> Any:
|
|
54
|
+
return getattr(self._stream, name)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _encodes_everything(stream: Any) -> bool:
|
|
58
|
+
"""Whether the stream takes any str without raising."""
|
|
59
|
+
encoding = getattr(stream, "encoding", None)
|
|
60
|
+
if not isinstance(encoding, str):
|
|
61
|
+
# StringIO and friends hold str directly.
|
|
62
|
+
return True
|
|
63
|
+
return encoding.lower().replace("-", "").replace("_", "") in _UTF8_NAMES
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def unicode_safe(stream: Any) -> Any:
|
|
67
|
+
"""The stream, wrapped only if a character could make it raise.
|
|
68
|
+
|
|
69
|
+
Idempotent, so a stream reconfigured repeatedly does not accumulate proxies.
|
|
70
|
+
"""
|
|
71
|
+
if isinstance(stream, _UnicodeSafeStream) or _encodes_everything(stream):
|
|
72
|
+
return stream
|
|
73
|
+
return _UnicodeSafeStream(stream)
|
|
@@ -29,6 +29,9 @@ import sys
|
|
|
29
29
|
|
|
30
30
|
import structlog
|
|
31
31
|
|
|
32
|
+
from provide.testkit._capture import install_capture_swap_fix
|
|
33
|
+
from provide.testkit._streams import unicode_safe
|
|
34
|
+
|
|
32
35
|
# Configure structlog with test-safe defaults BEFORE Foundation imports.
|
|
33
36
|
# This ensures that even if Foundation's get_logger() falls back to structlog.get_logger()
|
|
34
37
|
# during circular import resolution, the returned loggers will have proper configuration
|
|
@@ -50,17 +53,28 @@ def _strip_foundation_context(
|
|
|
50
53
|
return event_dict
|
|
51
54
|
|
|
52
55
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
)
|
|
56
|
+
def _configure_test_structlog() -> None:
|
|
57
|
+
"""Configure structlog for the test session.
|
|
58
|
+
|
|
59
|
+
The stream is read at call time and wrapped: on a legacy console this is a
|
|
60
|
+
cp1252 stream behind colorama, and a log line carrying an emoji would
|
|
61
|
+
otherwise raise UnicodeEncodeError out of the logging call and into
|
|
62
|
+
whatever the test was doing.
|
|
63
|
+
"""
|
|
64
|
+
structlog.configure(
|
|
65
|
+
processors=[
|
|
66
|
+
structlog.processors.TimeStamper(fmt="iso"),
|
|
67
|
+
_strip_foundation_context, # type: ignore[list-item]
|
|
68
|
+
structlog.dev.ConsoleRenderer(),
|
|
69
|
+
],
|
|
70
|
+
wrapper_class=structlog.BoundLogger,
|
|
71
|
+
context_class=dict,
|
|
72
|
+
logger_factory=structlog.PrintLoggerFactory(file=unicode_safe(sys.stdout)),
|
|
73
|
+
cache_logger_on_first_use=False, # Disable caching for test isolation
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
_configure_test_structlog()
|
|
64
78
|
|
|
65
79
|
# Install the import hook unconditionally
|
|
66
80
|
# This module is a pytest plugin (registered via pytest11 entry point) that ONLY
|
|
@@ -71,6 +85,12 @@ from provide.testkit._install_blocker import install_setproctitle_blocker # noq
|
|
|
71
85
|
|
|
72
86
|
install_setproctitle_blocker(force=True)
|
|
73
87
|
|
|
88
|
+
# Pytest hands a suspended stream back to whatever it saved at start, so a test
|
|
89
|
+
# that swaps sys.stdout after that -- CliRunner.isolation, redirect_stdout --
|
|
90
|
+
# loses the swap the first time a live log record suspends capture, and its
|
|
91
|
+
# output lands in pytest's buffer instead. See provide.testkit._capture.
|
|
92
|
+
install_capture_swap_fix()
|
|
93
|
+
|
|
74
94
|
__all__ = ["SetproctitleImportBlocker", "pytest_load_initial_conftests"]
|
|
75
95
|
|
|
76
96
|
|
|
@@ -6,9 +6,11 @@ src/provide/__init__.py
|
|
|
6
6
|
src/provide/testkit/__init__.py
|
|
7
7
|
src/provide/testkit/__main__.py
|
|
8
8
|
src/provide/testkit/_blocker.py
|
|
9
|
+
src/provide/testkit/_capture.py
|
|
9
10
|
src/provide/testkit/_early_init.py
|
|
10
11
|
src/provide/testkit/_install_blocker.py
|
|
11
12
|
src/provide/testkit/_install_pth.py
|
|
13
|
+
src/provide/testkit/_streams.py
|
|
12
14
|
src/provide/testkit/conftest.py
|
|
13
15
|
src/provide/testkit/main.py
|
|
14
16
|
src/provide/testkit/provide_testkit_init.pth
|
|
@@ -109,6 +111,8 @@ src/provide_testkit.egg-info/entry_points.txt
|
|
|
109
111
|
src/provide_testkit.egg-info/requires.txt
|
|
110
112
|
src/provide_testkit.egg-info/top_level.txt
|
|
111
113
|
tests/test_async_fixtures.py
|
|
114
|
+
tests/test_capture_stream_swap.py
|
|
112
115
|
tests/test_early_init.py
|
|
113
116
|
tests/test_placeholder.py
|
|
114
|
-
tests/test_pth_installation.py
|
|
117
|
+
tests/test_pth_installation.py
|
|
118
|
+
tests/test_stream_encoding.py
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#
|
|
2
|
+
# SPDX-FileCopyrightText: Copyright (c) provide.io llc. All rights reserved.
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
"""Pytest's capture must give back the stream it took, not the one it remembers.
|
|
7
|
+
|
|
8
|
+
``SysCaptureBase.resume`` reinstates the stream capture installed at ``start``.
|
|
9
|
+
Anything that swapped ``sys.stdout`` after that point -- ``CliRunner.isolation``,
|
|
10
|
+
``redirect_stdout``, a fixture of one's own -- is dropped the first time capture
|
|
11
|
+
suspends, and every write after that lands in pytest's buffer. Live logging
|
|
12
|
+
(``log_cli = true``) suspends capture per record, so one log line emitted from
|
|
13
|
+
inside a Click command empties ``result.output`` while the text itself is real.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from collections.abc import Iterator
|
|
19
|
+
import io
|
|
20
|
+
import sys
|
|
21
|
+
|
|
22
|
+
from _pytest.capture import SysCapture
|
|
23
|
+
import pytest
|
|
24
|
+
|
|
25
|
+
from provide.testkit._capture import install_capture_swap_fix
|
|
26
|
+
|
|
27
|
+
STDOUT_FD = 1
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@pytest.fixture
|
|
31
|
+
def stdout_restored() -> Iterator[None]:
|
|
32
|
+
"""Put ``sys.stdout`` back, however the test under it leaves things."""
|
|
33
|
+
saved = sys.stdout
|
|
34
|
+
try:
|
|
35
|
+
yield
|
|
36
|
+
finally:
|
|
37
|
+
sys.stdout = saved
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@pytest.mark.usefixtures("stdout_restored")
|
|
41
|
+
def test_resume_returns_a_swap_made_after_capture_started() -> None:
|
|
42
|
+
"""The case behind an empty ``CliRunner.result.output``."""
|
|
43
|
+
capture = SysCapture(STDOUT_FD)
|
|
44
|
+
capture.start()
|
|
45
|
+
swapped_in = io.StringIO()
|
|
46
|
+
sys.stdout = swapped_in
|
|
47
|
+
|
|
48
|
+
capture.suspend()
|
|
49
|
+
capture.resume()
|
|
50
|
+
|
|
51
|
+
assert sys.stdout is swapped_in
|
|
52
|
+
capture.done()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@pytest.mark.usefixtures("stdout_restored")
|
|
56
|
+
def test_resume_returns_the_capture_stream_when_nothing_swapped_it() -> None:
|
|
57
|
+
"""The ordinary path stays exactly as pytest wrote it."""
|
|
58
|
+
capture = SysCapture(STDOUT_FD)
|
|
59
|
+
capture.start()
|
|
60
|
+
|
|
61
|
+
capture.suspend()
|
|
62
|
+
capture.resume()
|
|
63
|
+
|
|
64
|
+
assert sys.stdout is capture.tmpfile
|
|
65
|
+
capture.done()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@pytest.mark.usefixtures("stdout_restored")
|
|
69
|
+
def test_a_second_suspend_does_not_overwrite_the_remembered_swap() -> None:
|
|
70
|
+
"""Suspending while suspended is legal, and must not record ``_old`` as the swap."""
|
|
71
|
+
capture = SysCapture(STDOUT_FD)
|
|
72
|
+
capture.start()
|
|
73
|
+
swapped_in = io.StringIO()
|
|
74
|
+
sys.stdout = swapped_in
|
|
75
|
+
|
|
76
|
+
capture.suspend()
|
|
77
|
+
capture.suspend()
|
|
78
|
+
capture.resume()
|
|
79
|
+
|
|
80
|
+
assert sys.stdout is swapped_in
|
|
81
|
+
capture.done()
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@pytest.mark.usefixtures("stdout_restored")
|
|
85
|
+
def test_done_restores_the_stream_capture_replaced() -> None:
|
|
86
|
+
"""Teardown belongs to pytest: a foreign swap does not outlive the capture."""
|
|
87
|
+
original = sys.stdout
|
|
88
|
+
capture = SysCapture(STDOUT_FD)
|
|
89
|
+
capture.start()
|
|
90
|
+
sys.stdout = io.StringIO()
|
|
91
|
+
|
|
92
|
+
capture.done()
|
|
93
|
+
|
|
94
|
+
assert sys.stdout is original
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_installing_the_fix_again_reports_that_it_was_already_there() -> None:
|
|
98
|
+
"""The plugin installs it at import; a second call must not stack a layer."""
|
|
99
|
+
assert install_capture_swap_fix() is False
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
CLI_UNDER_LIVE_LOGGING = """
|
|
103
|
+
import logging
|
|
104
|
+
|
|
105
|
+
import click
|
|
106
|
+
from click.testing import CliRunner
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@click.group(invoke_without_command=True)
|
|
110
|
+
@click.pass_context
|
|
111
|
+
def cli(ctx: click.Context) -> None:
|
|
112
|
+
# Any library logging from inside the command does this; the record only has
|
|
113
|
+
# to reach the root logger, where live logging is listening.
|
|
114
|
+
logging.getLogger("some.library").warning("a record reaches the root logger")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@cli.command()
|
|
118
|
+
def sub() -> None:
|
|
119
|
+
click.echo("SUBCOMMAND OUTPUT")
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def test_the_runner_sees_what_the_command_printed() -> None:
|
|
123
|
+
assert CliRunner().invoke(cli, ["sub"]).output != ""
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def test_a_cli_runner_keeps_its_output_across_a_live_log_record(pytester: pytest.Pytester) -> None:
|
|
128
|
+
"""The reported symptom, through the real Click and the real live-log handler."""
|
|
129
|
+
pytester.makepyfile(CLI_UNDER_LIVE_LOGGING)
|
|
130
|
+
|
|
131
|
+
result = pytester.runpytest_subprocess("-o", "log_cli=true")
|
|
132
|
+
|
|
133
|
+
result.assert_outcomes(passed=1)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def test_the_same_run_loses_its_output_without_this_plugin(pytester: pytest.Pytester) -> None:
|
|
137
|
+
"""Without this, the test above could pass for the wrong reason."""
|
|
138
|
+
pytester.makepyfile(CLI_UNDER_LIVE_LOGGING)
|
|
139
|
+
|
|
140
|
+
result = pytester.runpytest_subprocess("-o", "log_cli=true", "-p", "no:provide_testkit")
|
|
141
|
+
|
|
142
|
+
result.assert_outcomes(failed=1)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#
|
|
2
|
+
# SPDX-FileCopyrightText: Copyright (c) provide.io llc. All rights reserved.
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
"""Test logging must not raise on a console that cannot encode the message.
|
|
7
|
+
|
|
8
|
+
The plugin configures structlog before Foundation is imported, so the stream it
|
|
9
|
+
hands structlog is whatever pytest is holding at that moment. On Windows that is
|
|
10
|
+
a cp1252 console behind colorama, and a log line carrying an emoji raised
|
|
11
|
+
UnicodeEncodeError out of the logging call and into the test.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import io
|
|
17
|
+
|
|
18
|
+
from provide.testkit._streams import unicode_safe
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class _Cp1252Stream(io.TextIOWrapper):
|
|
22
|
+
"""Stands in for the Windows console pytest hands the plugin."""
|
|
23
|
+
|
|
24
|
+
def __init__(self) -> None:
|
|
25
|
+
super().__init__(io.BytesIO(), encoding="cp1252", newline="")
|
|
26
|
+
|
|
27
|
+
def written(self) -> str:
|
|
28
|
+
self.flush()
|
|
29
|
+
return self.buffer.getvalue().decode("cp1252") # type: ignore[attr-defined]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_the_stand_in_really_does_raise() -> None:
|
|
33
|
+
"""Without this, every test below could pass for the wrong reason."""
|
|
34
|
+
try:
|
|
35
|
+
_Cp1252Stream().write("\U0001f40d")
|
|
36
|
+
except UnicodeEncodeError:
|
|
37
|
+
return
|
|
38
|
+
raise AssertionError("the cp1252 stand-in accepted an emoji")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_an_unencodable_character_does_not_raise() -> None:
|
|
42
|
+
unicode_safe(_Cp1252Stream()).write("repo \U0001f40d state\n")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_the_surrounding_text_survives() -> None:
|
|
46
|
+
stream = _Cp1252Stream()
|
|
47
|
+
|
|
48
|
+
unicode_safe(stream).write("repo \U0001f40d state\n")
|
|
49
|
+
|
|
50
|
+
written = stream.written()
|
|
51
|
+
assert "repo " in written
|
|
52
|
+
assert "state" in written
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_the_character_is_named_rather_than_dropped() -> None:
|
|
56
|
+
"""backslashreplace keeps the emoji readable; 'replace' would give '?'."""
|
|
57
|
+
stream = _Cp1252Stream()
|
|
58
|
+
|
|
59
|
+
unicode_safe(stream).write("\U0001f40d\n")
|
|
60
|
+
|
|
61
|
+
assert "\\U0001f40d" in stream.written()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_a_stream_that_can_encode_is_handed_back_unchanged() -> None:
|
|
65
|
+
stream = io.StringIO()
|
|
66
|
+
|
|
67
|
+
assert unicode_safe(stream) is stream
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_wrapping_is_idempotent() -> None:
|
|
71
|
+
wrapped = unicode_safe(_Cp1252Stream())
|
|
72
|
+
|
|
73
|
+
assert unicode_safe(wrapped) is wrapped
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def test_the_wrapper_delegates_what_it_does_not_define() -> None:
|
|
77
|
+
stream = _Cp1252Stream()
|
|
78
|
+
|
|
79
|
+
assert unicode_safe(stream).encoding == "cp1252"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class _NoReturnStream:
|
|
83
|
+
"""colorama's Windows wrapper: write() returns None, not a count."""
|
|
84
|
+
|
|
85
|
+
encoding = "cp1252"
|
|
86
|
+
|
|
87
|
+
def __init__(self) -> None:
|
|
88
|
+
self.text = ""
|
|
89
|
+
|
|
90
|
+
def write(self, text: str) -> None:
|
|
91
|
+
self.text += text.encode("cp1252", "strict").decode("cp1252")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def test_a_stream_whose_write_returns_none_is_accepted() -> None:
|
|
95
|
+
"""colorama returns None from write; coercing that to int raised TypeError."""
|
|
96
|
+
assert unicode_safe(_NoReturnStream()).write("plain\n") == len("plain\n")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def test_the_plugin_configures_a_stream_that_cannot_raise(monkeypatch) -> None:
|
|
100
|
+
"""The regression: the plugin handed structlog the raw console."""
|
|
101
|
+
import structlog
|
|
102
|
+
|
|
103
|
+
monkeypatch.setattr("sys.stdout", _Cp1252Stream())
|
|
104
|
+
|
|
105
|
+
import provide.testkit.pytest_plugin as plugin
|
|
106
|
+
|
|
107
|
+
plugin._configure_test_structlog()
|
|
108
|
+
|
|
109
|
+
structlog.get_config()["logger_factory"]().msg("repo \U0001f40d state")
|
provide_testkit-0.5.0/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.5.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/chaos/concurrency_strategies.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/chaos/time_strategies.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/file/content_fixtures.py
RENAMED
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/file/directory_fixtures.py
RENAMED
|
File without changes
|
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/file/special_fixtures.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/process/async_fixtures.py
RENAMED
|
File without changes
|
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/process/script_assertions.py
RENAMED
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/process/script_fixtures.py
RENAMED
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/process/subprocess_fixtures.py
RENAMED
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/process/system_fixtures.py
RENAMED
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/provide_testkit_init.pth
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/complexity/__init__.py
RENAMED
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/complexity/analyzer.py
RENAMED
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/complexity/fixture.py
RENAMED
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/coverage/__init__.py
RENAMED
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/coverage/fixture.py
RENAMED
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/coverage/reporter.py
RENAMED
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/coverage/tracker.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/documentation/checker.py
RENAMED
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/documentation/fixture.py
RENAMED
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/profiling/__init__.py
RENAMED
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/profiling/fixture.py
RENAMED
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/profiling/profiler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/security/__init__.py
RENAMED
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/security/fixture.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/quality/security/scanner.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/threading/basic_fixtures.py
RENAMED
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/threading/data_fixtures.py
RENAMED
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/threading/execution_fixtures.py
RENAMED
|
File without changes
|
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide/testkit/threading/sync_fixtures.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide_testkit.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{provide_testkit-0.5.0 → provide_testkit-0.5.2}/src/provide_testkit.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|