looptime 0.2__tar.gz → 0.3__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.
Potentially problematic release.
This version of looptime might be problematic. Click here for more details.
- {looptime-0.2 → looptime-0.3}/.github/workflows/ci.yaml +22 -16
- {looptime-0.2 → looptime-0.3}/.github/workflows/publish.yaml +3 -3
- {looptime-0.2 → looptime-0.3}/.github/workflows/thorough.yaml +15 -15
- {looptime-0.2 → looptime-0.3}/.pre-commit-config.yaml +3 -3
- {looptime-0.2/looptime.egg-info → looptime-0.3}/PKG-INFO +143 -17
- looptime-0.2/PKG-INFO → looptime-0.3/README.md +127 -31
- {looptime-0.2 → looptime-0.3}/looptime/__init__.py +2 -1
- {looptime-0.2 → looptime-0.3}/looptime/loops.py +71 -4
- {looptime-0.2 → looptime-0.3}/looptime/math.py +3 -1
- looptime-0.3/looptime/plugin.py +316 -0
- {looptime-0.2 → looptime-0.3}/looptime/timeproxies.py +0 -10
- looptime-0.2/README.md → looptime-0.3/looptime.egg-info/PKG-INFO +157 -11
- {looptime-0.2 → looptime-0.3}/looptime.egg-info/entry_points.txt +0 -1
- {looptime-0.2 → looptime-0.3}/requirements.txt +1 -2
- {looptime-0.2 → looptime-0.3}/setup.py +1 -1
- {looptime-0.2 → looptime-0.3}/tests/test_chronometers.py +2 -1
- looptime-0.3/tests/test_patching.py +92 -0
- {looptime-0.2 → looptime-0.3}/tests/test_plugin.py +46 -19
- {looptime-0.2 → looptime-0.3}/tests/test_time_moves.py +37 -0
- looptime-0.2/looptime/plugin.py +0 -45
- looptime-0.2/tests/test_patching.py +0 -44
- {looptime-0.2 → looptime-0.3}/.codecov.yml +0 -0
- {looptime-0.2 → looptime-0.3}/.github/CODEOWNERS +0 -0
- {looptime-0.2 → looptime-0.3}/.github/FUNDING.yml +0 -0
- {looptime-0.2 → looptime-0.3}/.github/ISSUE_TEMPLATE.md +0 -0
- {looptime-0.2 → looptime-0.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {looptime-0.2 → looptime-0.3}/.gitignore +0 -0
- {looptime-0.2 → looptime-0.3}/.isort.cfg +0 -0
- {looptime-0.2 → looptime-0.3}/LICENSE +0 -0
- {looptime-0.2 → looptime-0.3}/MAINTAINERS +0 -0
- {looptime-0.2 → looptime-0.3}/SECURITY.md +0 -0
- {looptime-0.2 → looptime-0.3}/looptime/chronometers.py +0 -0
- {looptime-0.2 → looptime-0.3}/looptime/patchers.py +0 -0
- {looptime-0.2 → looptime-0.3}/looptime/py.typed +0 -0
- {looptime-0.2 → looptime-0.3}/looptime.egg-info/SOURCES.txt +0 -0
- {looptime-0.2 → looptime-0.3}/looptime.egg-info/dependency_links.txt +0 -0
- {looptime-0.2 → looptime-0.3}/looptime.egg-info/top_level.txt +0 -0
- {looptime-0.2 → looptime-0.3}/looptime.egg-info/zip-safe +0 -0
- {looptime-0.2 → looptime-0.3}/mypy.ini +0 -0
- {looptime-0.2 → looptime-0.3}/setup.cfg +0 -0
- {looptime-0.2 → looptime-0.3}/tests/conftest.py +0 -0
- {looptime-0.2 → looptime-0.3}/tests/test_initialisation.py +0 -0
- {looptime-0.2 → looptime-0.3}/tests/test_readme.py +0 -0
- {looptime-0.2 → looptime-0.3}/tests/test_subclassing.py +0 -0
- {looptime-0.2 → looptime-0.3}/tests/test_time_on_executors.py +0 -0
- {looptime-0.2 → looptime-0.3}/tests/test_time_on_io_idle.py +0 -0
- {looptime-0.2 → looptime-0.3}/tests/test_timeproxies.py +0 -0
|
@@ -9,13 +9,13 @@ on:
|
|
|
9
9
|
jobs:
|
|
10
10
|
linters:
|
|
11
11
|
name: Linting and static analysis
|
|
12
|
-
runs-on: ubuntu-
|
|
12
|
+
runs-on: ubuntu-24.04
|
|
13
13
|
timeout-minutes: 5
|
|
14
14
|
steps:
|
|
15
|
-
- uses: actions/checkout@
|
|
16
|
-
- uses: actions/setup-python@
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
17
|
with:
|
|
18
|
-
python-version: "3.
|
|
18
|
+
python-version: "3.13"
|
|
19
19
|
- run: pip install -r requirements.txt
|
|
20
20
|
- run: pre-commit run --all-files
|
|
21
21
|
- run: mypy looptime --strict
|
|
@@ -24,18 +24,24 @@ jobs:
|
|
|
24
24
|
strategy:
|
|
25
25
|
fail-fast: false
|
|
26
26
|
matrix:
|
|
27
|
-
python-version: [ "3.
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
python-version: [ "3.9", "3.10", "3.11", "3.12" ]
|
|
28
|
+
include:
|
|
29
|
+
- python-version: "3.13"
|
|
30
|
+
- python-version: "3.13"
|
|
31
|
+
install-extras: "pytest-asyncio<1.0.0"
|
|
32
|
+
name: Python ${{ matrix.python-version }}${{ matrix.install-extras && ', ' || '' }}${{ matrix.install-extras }}
|
|
33
|
+
runs-on: ubuntu-24.04
|
|
30
34
|
timeout-minutes: 5
|
|
31
35
|
steps:
|
|
32
|
-
- uses: actions/checkout@
|
|
33
|
-
- uses: actions/setup-python@
|
|
36
|
+
- uses: actions/checkout@v4
|
|
37
|
+
- uses: actions/setup-python@v5
|
|
34
38
|
with:
|
|
35
39
|
python-version: ${{ matrix.python-version }}
|
|
36
40
|
|
|
37
41
|
- run: pip install -r requirements.txt
|
|
38
42
|
- run: pip install -e .
|
|
43
|
+
- run: pip install "${{ matrix.install-extras }}"
|
|
44
|
+
if: ${{ matrix.install-extras }}
|
|
39
45
|
- run: pytest --color=yes --cov=looptime --cov-branch
|
|
40
46
|
|
|
41
47
|
- name: Publish coverage to Coveralls.io
|
|
@@ -45,7 +51,7 @@ jobs:
|
|
|
45
51
|
GITHUB_TOKEN: ${{ secrets.github_token }}
|
|
46
52
|
continue-on-error: true
|
|
47
53
|
- name: Publish coverage to CodeCov.io
|
|
48
|
-
uses: codecov/codecov-action@
|
|
54
|
+
uses: codecov/codecov-action@v3
|
|
49
55
|
if: success()
|
|
50
56
|
env:
|
|
51
57
|
PYTHON: ${{ matrix.python-version }}
|
|
@@ -59,13 +65,13 @@ jobs:
|
|
|
59
65
|
strategy:
|
|
60
66
|
fail-fast: false
|
|
61
67
|
matrix:
|
|
62
|
-
python-version: [ "pypy-3.
|
|
68
|
+
python-version: [ "pypy-3.9", "pypy-3.10", "pypy-3.11" ]
|
|
63
69
|
name: Python ${{ matrix.python-version }}
|
|
64
|
-
runs-on: ubuntu-
|
|
70
|
+
runs-on: ubuntu-24.04
|
|
65
71
|
timeout-minutes: 5
|
|
66
72
|
steps:
|
|
67
|
-
- uses: actions/checkout@
|
|
68
|
-
- uses: actions/setup-python@
|
|
73
|
+
- uses: actions/checkout@v4
|
|
74
|
+
- uses: actions/setup-python@v5
|
|
69
75
|
with:
|
|
70
76
|
python-version: ${{ matrix.python-version }}
|
|
71
77
|
|
|
@@ -76,9 +82,9 @@ jobs:
|
|
|
76
82
|
coveralls-finish:
|
|
77
83
|
name: Finalize coveralls.io
|
|
78
84
|
needs: [unit-tests]
|
|
79
|
-
runs-on: ubuntu-
|
|
85
|
+
runs-on: ubuntu-24.04
|
|
80
86
|
steps:
|
|
81
|
-
- uses: actions/setup-python@
|
|
87
|
+
- uses: actions/setup-python@v5
|
|
82
88
|
- run: pip install coveralls
|
|
83
89
|
- run: coveralls --service=github --finish
|
|
84
90
|
env:
|
|
@@ -11,10 +11,10 @@ on:
|
|
|
11
11
|
jobs:
|
|
12
12
|
publish:
|
|
13
13
|
name: Build and publish
|
|
14
|
-
runs-on: ubuntu-
|
|
14
|
+
runs-on: ubuntu-24.04
|
|
15
15
|
steps:
|
|
16
|
-
- uses: actions/checkout@
|
|
17
|
-
- uses: actions/setup-python@
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
18
|
with:
|
|
19
19
|
python-version: "3.10"
|
|
20
20
|
- run: pip install --upgrade setuptools wheel twine
|
|
@@ -11,13 +11,13 @@ on:
|
|
|
11
11
|
jobs:
|
|
12
12
|
linters:
|
|
13
13
|
name: Linting and static analysis
|
|
14
|
-
runs-on: ubuntu-
|
|
14
|
+
runs-on: ubuntu-24.04
|
|
15
15
|
timeout-minutes: 5
|
|
16
16
|
steps:
|
|
17
|
-
- uses: actions/checkout@
|
|
18
|
-
- uses: actions/setup-python@
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
19
|
with:
|
|
20
|
-
python-version: "3.
|
|
20
|
+
python-version: "3.13"
|
|
21
21
|
- run: pip install -r requirements.txt
|
|
22
22
|
- run: pre-commit run --all-files
|
|
23
23
|
- run: mypy looptime --strict
|
|
@@ -26,13 +26,13 @@ jobs:
|
|
|
26
26
|
strategy:
|
|
27
27
|
fail-fast: false
|
|
28
28
|
matrix:
|
|
29
|
-
python-version: [ "3.
|
|
29
|
+
python-version: [ "3.9", "3.10", "3.11", "3.12", "3.13" ]
|
|
30
30
|
name: Python ${{ matrix.python-version }}
|
|
31
|
-
runs-on: ubuntu-
|
|
31
|
+
runs-on: ubuntu-24.04
|
|
32
32
|
timeout-minutes: 5
|
|
33
33
|
steps:
|
|
34
|
-
- uses: actions/checkout@
|
|
35
|
-
- uses: actions/setup-python@
|
|
34
|
+
- uses: actions/checkout@v4
|
|
35
|
+
- uses: actions/setup-python@v5
|
|
36
36
|
with:
|
|
37
37
|
python-version: ${{ matrix.python-version }}
|
|
38
38
|
|
|
@@ -47,7 +47,7 @@ jobs:
|
|
|
47
47
|
GITHUB_TOKEN: ${{ secrets.github_token }}
|
|
48
48
|
continue-on-error: true
|
|
49
49
|
- name: Publish coverage to CodeCov.io
|
|
50
|
-
uses: codecov/codecov-action@
|
|
50
|
+
uses: codecov/codecov-action@v3
|
|
51
51
|
if: success()
|
|
52
52
|
env:
|
|
53
53
|
PYTHON: ${{ matrix.python-version }}
|
|
@@ -61,13 +61,13 @@ jobs:
|
|
|
61
61
|
strategy:
|
|
62
62
|
fail-fast: false
|
|
63
63
|
matrix:
|
|
64
|
-
python-version: [ "pypy-3.
|
|
64
|
+
python-version: [ "pypy-3.9", "pypy-3.10", "pypy-3.11" ]
|
|
65
65
|
name: Python ${{ matrix.python-version }}
|
|
66
|
-
runs-on: ubuntu-
|
|
66
|
+
runs-on: ubuntu-24.04
|
|
67
67
|
timeout-minutes: 5
|
|
68
68
|
steps:
|
|
69
|
-
- uses: actions/checkout@
|
|
70
|
-
- uses: actions/setup-python@
|
|
69
|
+
- uses: actions/checkout@v4
|
|
70
|
+
- uses: actions/setup-python@v5
|
|
71
71
|
with:
|
|
72
72
|
python-version: ${{ matrix.python-version }}
|
|
73
73
|
|
|
@@ -78,9 +78,9 @@ jobs:
|
|
|
78
78
|
coveralls-finish:
|
|
79
79
|
name: Finalize coveralls.io
|
|
80
80
|
needs: [unit-tests]
|
|
81
|
-
runs-on: ubuntu-
|
|
81
|
+
runs-on: ubuntu-24.04
|
|
82
82
|
steps:
|
|
83
|
-
- uses: actions/setup-python@
|
|
83
|
+
- uses: actions/setup-python@v5
|
|
84
84
|
- run: pip install coveralls
|
|
85
85
|
- run: coveralls --service=github --finish
|
|
86
86
|
env:
|
|
@@ -7,7 +7,7 @@ exclude: |
|
|
|
7
7
|
)
|
|
8
8
|
repos:
|
|
9
9
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
10
|
-
rev:
|
|
10
|
+
rev: v5.0.0
|
|
11
11
|
hooks:
|
|
12
12
|
- id: check-ast
|
|
13
13
|
- id: trailing-whitespace
|
|
@@ -46,7 +46,7 @@ repos:
|
|
|
46
46
|
# - id: double-quote-string-fixer
|
|
47
47
|
|
|
48
48
|
- repo: https://github.com/pre-commit/pygrep-hooks
|
|
49
|
-
rev: v1.
|
|
49
|
+
rev: v1.10.0
|
|
50
50
|
hooks:
|
|
51
51
|
- id: python-check-blanket-noqa
|
|
52
52
|
- id: python-check-mock-methods
|
|
@@ -59,7 +59,7 @@ repos:
|
|
|
59
59
|
- id: text-unicode-replacement-char
|
|
60
60
|
|
|
61
61
|
- repo: https://github.com/PyCQA/isort
|
|
62
|
-
rev:
|
|
62
|
+
rev: 6.0.1
|
|
63
63
|
hooks:
|
|
64
64
|
- id: isort
|
|
65
65
|
name: isort-source-code
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: looptime
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3
|
|
4
4
|
Summary: Fast-forward asyncio event loop time (in tests)
|
|
5
5
|
Home-page: https://github.com/nolar/looptime
|
|
6
6
|
Author: Sergey Vasilyev
|
|
@@ -11,18 +11,28 @@ License: MIT
|
|
|
11
11
|
Project-URL: Bug Tracker, https://github.com/nolar/looptime/issues
|
|
12
12
|
Project-URL: Source Code, https://github.com/nolar/looptime
|
|
13
13
|
Keywords: asyncio,event loop,time,python,pytest
|
|
14
|
-
|
|
15
|
-
Requires-Python: >=3.7
|
|
14
|
+
Requires-Python: >=3.9
|
|
16
15
|
Description-Content-Type: text/markdown
|
|
17
16
|
License-File: LICENSE
|
|
17
|
+
Dynamic: author
|
|
18
|
+
Dynamic: author-email
|
|
19
|
+
Dynamic: description
|
|
20
|
+
Dynamic: description-content-type
|
|
21
|
+
Dynamic: home-page
|
|
22
|
+
Dynamic: keywords
|
|
23
|
+
Dynamic: license
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
Dynamic: maintainer
|
|
26
|
+
Dynamic: maintainer-email
|
|
27
|
+
Dynamic: project-url
|
|
28
|
+
Dynamic: requires-python
|
|
29
|
+
Dynamic: summary
|
|
18
30
|
|
|
19
31
|
# Fast-forward asyncio event loop time (in tests)
|
|
20
32
|
|
|
21
33
|
[](https://github.com/nolar/looptime/actions/workflows/thorough.yaml)
|
|
22
34
|
[](https://codecov.io/gh/nolar/looptime)
|
|
23
35
|
[](https://coveralls.io/github/nolar/looptime?branch=main)
|
|
24
|
-
[](https://lgtm.com/projects/g/nolar/looptime/alerts/)
|
|
25
|
-
[](https://lgtm.com/projects/g/nolar/looptime/context:python)
|
|
26
36
|
[](https://github.com/pre-commit/pre-commit)
|
|
27
37
|
|
|
28
38
|
## What?
|
|
@@ -37,10 +47,10 @@ The effects of time removal can be seen from both sides:
|
|
|
37
47
|
This hides the code overhead and network latencies from the time measurements,
|
|
38
48
|
making the loop time sharply and predictably advancing in configured steps.
|
|
39
49
|
|
|
40
|
-
* From the **observer's (i.e. your) point of view,**
|
|
50
|
+
* From the **observer's (i.e. your personal) point of view,**
|
|
41
51
|
all activities of the event loop, such as sleeps, events/conditions waits,
|
|
42
52
|
timeouts, "later" callbacks, happen in near-zero amount of the real time
|
|
43
|
-
(
|
|
53
|
+
(due to the usual code execution overhead).
|
|
44
54
|
This speeds up the execution of tests without breaking the tests' time-based
|
|
45
55
|
design, even if they are designed to run in seconds or minutes.
|
|
46
56
|
|
|
@@ -77,19 +87,19 @@ at the same time:
|
|
|
77
87
|
* One is for the coroutine-under-test which moves between states
|
|
78
88
|
in the background.
|
|
79
89
|
* Another one is for the test itself, which controls the flow
|
|
80
|
-
of that coroutine-under-test: it
|
|
90
|
+
of that coroutine-under-test: it schedules events, injects data, etc.
|
|
81
91
|
|
|
82
92
|
In textbook cases with simple coroutines that are more like regular functions,
|
|
83
93
|
it is possible to design a test so that it runs straight to the end in one hop
|
|
84
94
|
— with all the preconditions set and data prepared in advance in the test setup.
|
|
85
95
|
|
|
86
|
-
However, in real-world cases, the tests often must verify that
|
|
96
|
+
However, in the real-world cases, the tests often must verify that
|
|
87
97
|
the coroutine stops at some point, waits for a condition for some limited time,
|
|
88
98
|
and then passes or fails.
|
|
89
99
|
|
|
90
100
|
The problem is often "solved" by mocking the low-level coroutines of sleep/wait
|
|
91
101
|
that we expect the coroutine-under-test to call. But this violates the main
|
|
92
|
-
principle of good unit-tests: test the promise, not the implementation
|
|
102
|
+
principle of good unit-tests: **test the promise, not the implementation.**
|
|
93
103
|
Mocking and checking the low-level coroutines is based on the assumptions
|
|
94
104
|
of how the coroutine is implemented internally, which can change over time.
|
|
95
105
|
Good tests do not change on refactoring if the protocol remains the same.
|
|
@@ -97,8 +107,8 @@ Good tests do not change on refactoring if the protocol remains the same.
|
|
|
97
107
|
Another (straightforward) approach is to not mock the low-level routines, but
|
|
98
108
|
to spend the real-world time, just in short bursts as hard-coded in the test.
|
|
99
109
|
Not only it makes the whole test-suite slower, it also brings the execution
|
|
100
|
-
time close to the values where the code overhead
|
|
101
|
-
|
|
110
|
+
time close to the values where the code overhead or measurement errors affect
|
|
111
|
+
the timing, which makes it difficult to assert on the coroutine's pure time.
|
|
102
112
|
|
|
103
113
|
|
|
104
114
|
## Solution
|
|
@@ -250,13 +260,26 @@ async def test_me():
|
|
|
250
260
|
`start` (`float` or `None`, or a no-argument callable that returns the same)
|
|
251
261
|
is the initial time of the event loop.
|
|
252
262
|
|
|
253
|
-
If it is callable, it is invoked once per event loop to get the value:
|
|
263
|
+
If it is a callable, it is invoked once per event loop to get the value:
|
|
254
264
|
e.g. `start=time.monotonic` to align with the true time,
|
|
255
265
|
or `start=lambda: random.random() * 100` to add some unpredictability.
|
|
256
266
|
|
|
257
267
|
`None` is treated the same as `0.0`.
|
|
258
268
|
|
|
259
|
-
The default is `0.0`.
|
|
269
|
+
The default is `0.0`. For reusable event loops, the default is to keep
|
|
270
|
+
the time untouched, which means `0.0` or the explicit value for the first test,
|
|
271
|
+
but then an ever-increasing value for the 2nd, 3rd, and further tests.
|
|
272
|
+
|
|
273
|
+
Note: pytest-asyncio 1.0.0+ introduced event loops with higher scopes,
|
|
274
|
+
e.g. class-, module-, packages-, session-scoped event loops used in tests.
|
|
275
|
+
Such event loops are reused, so their time continues growing through many tests.
|
|
276
|
+
However, if the test is explicitly configured with the start time,
|
|
277
|
+
that time is enforced to the event loop when the test function starts —
|
|
278
|
+
to satisfy the clearly declared intentions — even if the time moves backwards,
|
|
279
|
+
which goes against the nature of the time itself (monotonically growing).
|
|
280
|
+
This might lead to surprises in time measurements outside of the test,
|
|
281
|
+
e.g. in fixtures: the code durations can become negative, or the events can
|
|
282
|
+
happen (falsely) before they are scheduled (loop-clock-wise). Be careful.
|
|
260
283
|
|
|
261
284
|
|
|
262
285
|
### The end of time
|
|
@@ -273,6 +296,8 @@ e.g. with `asyncio.sleep(0)`, simple `await` statements, etc.
|
|
|
273
296
|
|
|
274
297
|
If set to `None`, there is no end of time, and the event loop runs
|
|
275
298
|
as long as needed. Note: `0` means ending the time immediately on start.
|
|
299
|
+
Be careful with the explicit ending time in higher-scoped event loops
|
|
300
|
+
of pytest-asyncio>=1.0.0, since they time increases through many tests.
|
|
276
301
|
|
|
277
302
|
If it is a callable, it is called once per event loop to get the value:
|
|
278
303
|
e.g. `end=lambda: time.monotonic() + 10`.
|
|
@@ -530,6 +555,63 @@ For example, with the resolution `0.001`, the time
|
|
|
530
555
|
everything smaller than `0.001` becomes `0` and probably misbehaves._
|
|
531
556
|
|
|
532
557
|
|
|
558
|
+
### Time magic coverage
|
|
559
|
+
|
|
560
|
+
The time compaction magic is enabled only for the duration of the test,
|
|
561
|
+
i.e. the test function — but not the fixtures.
|
|
562
|
+
The fixtures run in the real (wall-clock) time.
|
|
563
|
+
|
|
564
|
+
The options (including the force starting time) are applied at the test function
|
|
565
|
+
starting moment, not when it is setting up the fixtures (even function-scoped).
|
|
566
|
+
|
|
567
|
+
This is caused by a new concept of multiple co-existing event loops
|
|
568
|
+
in pytest-asyncio>=1.0.0:
|
|
569
|
+
|
|
570
|
+
- It is unclear which options to apply to higher-scoped fixtures
|
|
571
|
+
used by many tests, which themselves use higher-scoped event loops —
|
|
572
|
+
especially in selective partial runs. Technically, it is the 1st test,
|
|
573
|
+
with the options of 2nd and further tests simply ignored.
|
|
574
|
+
- It is impossible to guess which event loop will be the running loop
|
|
575
|
+
in the test until we reach the test itself, i.e. we do not know this
|
|
576
|
+
when setting up the fixtures, even function-scoped fixtures.
|
|
577
|
+
- There is no way to cover the fixture teardown (no hook in pytest),
|
|
578
|
+
only for the fixture setup and post-teardown cleanup.
|
|
579
|
+
|
|
580
|
+
As such, this functionality (covering of function-scoped fixtures)
|
|
581
|
+
was abandoned — since it was never promised, tested, or documented —
|
|
582
|
+
plus an assumption that it was never used by anyone (it should not be).
|
|
583
|
+
It was rather a side effect of the previous implemention,
|
|
584
|
+
which is not available or possible anymore.
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
### pytest-asyncio>=1.0.0
|
|
588
|
+
|
|
589
|
+
As it is said above, pytest-asyncio>=1.0.0 introduced several co-existing
|
|
590
|
+
event loops of different scopes. The time compaction in these event loops
|
|
591
|
+
is NOT activated. Only the running loop of the test function is activated.
|
|
592
|
+
|
|
593
|
+
Configuring and activating multiple co-existing event loops brings a few
|
|
594
|
+
conceptual challenges, which require a good sample case to look into,
|
|
595
|
+
and some time to think.
|
|
596
|
+
|
|
597
|
+
Would you need time compaction in your fixtures of higher scopes,
|
|
598
|
+
do it explicitly:
|
|
599
|
+
|
|
600
|
+
```python
|
|
601
|
+
import asyncio
|
|
602
|
+
import pytest
|
|
603
|
+
|
|
604
|
+
@pytest.fixture
|
|
605
|
+
async def fixt():
|
|
606
|
+
loop = asyncio.get_running_loop()
|
|
607
|
+
loop.setup_looptime(start=123, end=456)
|
|
608
|
+
with loop.looptime_enabled():
|
|
609
|
+
await do_things()
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
There is #11 to add a feature to do this automatically, but it is not yet done.
|
|
613
|
+
|
|
614
|
+
|
|
533
615
|
## Extras
|
|
534
616
|
|
|
535
617
|
### Chronometers
|
|
@@ -623,6 +705,8 @@ the loop time also includes the time of all fixtures setups.
|
|
|
623
705
|
Do you use a custom event loop? No problem! Create a test-specific descendant
|
|
624
706
|
with the provided mixin — and it will work the same as the default event loop.
|
|
625
707
|
|
|
708
|
+
For `pytest-asyncio<1.0.0`:
|
|
709
|
+
|
|
626
710
|
```python
|
|
627
711
|
import looptime
|
|
628
712
|
import pytest
|
|
@@ -638,6 +722,29 @@ def event_loop():
|
|
|
638
722
|
return LooptimeCustomEventLoop()
|
|
639
723
|
```
|
|
640
724
|
|
|
725
|
+
For `pytest-asyncio>=1.0.0`:
|
|
726
|
+
|
|
727
|
+
```python
|
|
728
|
+
import asyncio
|
|
729
|
+
import looptime
|
|
730
|
+
import pytest
|
|
731
|
+
from wherever import CustomEventLoop
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
class LooptimeCustomEventLoop(looptime.LoopTimeEventLoop, CustomEventLoop):
|
|
735
|
+
pass
|
|
736
|
+
|
|
737
|
+
|
|
738
|
+
class LooptimeCustomEventLoopPolicy(asyncio.DefaultEventLoopPolicy):
|
|
739
|
+
def new_event_loop(self):
|
|
740
|
+
return LooptimeCustomEventLoop()
|
|
741
|
+
|
|
742
|
+
|
|
743
|
+
@pytest.fixture(scope='session')
|
|
744
|
+
def event_loop_policy():
|
|
745
|
+
return LooptimeCustomEventLoopPolicy()
|
|
746
|
+
```
|
|
747
|
+
|
|
641
748
|
Only selector-based event loops are supported: the event loop must rely on
|
|
642
749
|
`self._selector.select(timeout)` to sleep for `timeout` true-time seconds.
|
|
643
750
|
Everything that inherits from `asyncio.BaseEventLoop` should work.
|
|
@@ -645,6 +752,8 @@ Everything that inherits from `asyncio.BaseEventLoop` should work.
|
|
|
645
752
|
You can also patch almost any event loop class or event loop object
|
|
646
753
|
the same way as `looptime` does that (via some dirty hackery):
|
|
647
754
|
|
|
755
|
+
For `pytest-asyncio<1.0.0`:
|
|
756
|
+
|
|
648
757
|
```python
|
|
649
758
|
import asyncio
|
|
650
759
|
import looptime
|
|
@@ -657,6 +766,25 @@ def event_loop():
|
|
|
657
766
|
return looptime.patch_event_loop(loop)
|
|
658
767
|
```
|
|
659
768
|
|
|
769
|
+
For `pytest-asyncio>=1.0.0`:
|
|
770
|
+
|
|
771
|
+
```python
|
|
772
|
+
import asyncio
|
|
773
|
+
import looptime
|
|
774
|
+
import pytest
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
class LooptimeEventLoopPolicy(asyncio.DefaultEventLoopPolicy):
|
|
778
|
+
def new_event_loop(self):
|
|
779
|
+
loop = super().new_event_loop()
|
|
780
|
+
return looptime.patch_event_loop(loop)
|
|
781
|
+
|
|
782
|
+
|
|
783
|
+
@pytest.fixture(scope='session')
|
|
784
|
+
def event_loop_policy():
|
|
785
|
+
return LooptimeEventLoopPolicy()
|
|
786
|
+
```
|
|
787
|
+
|
|
660
788
|
`looptime.make_event_loop_class(cls)` constructs a new class that inherits
|
|
661
789
|
from the referenced class and the specialised event loop class mentioned above.
|
|
662
790
|
The resulting classes are cached, so it can be safely called multiple times.
|
|
@@ -668,5 +796,3 @@ constructed one. For those who care, it is an equivalent of the following hack
|
|
|
668
796
|
```python
|
|
669
797
|
loop.__class__ = looptime.make_event_loop_class(loop.__class__)
|
|
670
798
|
```
|
|
671
|
-
|
|
672
|
-
|