async-solipsism 0.4__tar.gz → 0.6__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.
- async-solipsism-0.6/.github/workflows/test.yml +35 -0
- {async-solipsism-0.4 → async-solipsism-0.6}/.gitignore +1 -0
- async-solipsism-0.6/.pre-commit-config.yaml +12 -0
- {async-solipsism-0.4/src/async_solipsism.egg-info → async-solipsism-0.6}/PKG-INFO +48 -19
- async-solipsism-0.4/PKG-INFO → async-solipsism-0.6/README.md +39 -29
- async-solipsism-0.6/pyproject.toml +35 -0
- async-solipsism-0.6/requirements.in +4 -0
- async-solipsism-0.6/requirements.txt +46 -0
- async-solipsism-0.6/setup.cfg +4 -0
- {async-solipsism-0.4 → async-solipsism-0.6}/src/async_solipsism/__init__.py +2 -2
- {async-solipsism-0.4 → async-solipsism-0.6}/src/async_solipsism/loop.py +14 -3
- {async-solipsism-0.4 → async-solipsism-0.6}/src/async_solipsism/socket.py +27 -13
- async-solipsism-0.4/README.md → async-solipsism-0.6/src/async_solipsism.egg-info/PKG-INFO +58 -13
- {async-solipsism-0.4 → async-solipsism-0.6}/src/async_solipsism.egg-info/SOURCES.txt +2 -1
- async-solipsism-0.6/src/async_solipsism.egg-info/requires.txt +6 -0
- {async-solipsism-0.4 → async-solipsism-0.6}/test/test_loop.py +51 -17
- {async-solipsism-0.4 → async-solipsism-0.6}/test/test_socket.py +41 -1
- async-solipsism-0.4/.github/workflows/test.yml +0 -25
- async-solipsism-0.4/pyproject.toml +0 -2
- async-solipsism-0.4/requirements.txt +0 -4
- async-solipsism-0.4/setup.cfg +0 -34
- async-solipsism-0.4/src/async_solipsism.egg-info/requires.txt +0 -5
- {async-solipsism-0.4 → async-solipsism-0.6}/.flake8 +0 -0
- {async-solipsism-0.4 → async-solipsism-0.6}/LICENSE +0 -0
- {async-solipsism-0.4 → async-solipsism-0.6}/pytest.ini +0 -0
- {async-solipsism-0.4 → async-solipsism-0.6}/setup.py +0 -0
- {async-solipsism-0.4 → async-solipsism-0.6}/src/async_solipsism/clock.py +0 -0
- {async-solipsism-0.4 → async-solipsism-0.6}/src/async_solipsism/exceptions.py +0 -0
- {async-solipsism-0.4 → async-solipsism-0.6}/src/async_solipsism/selector.py +0 -0
- {async-solipsism-0.4 → async-solipsism-0.6}/src/async_solipsism.egg-info/dependency_links.txt +0 -0
- {async-solipsism-0.4 → async-solipsism-0.6}/src/async_solipsism.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: Run tests
|
|
2
|
+
|
|
3
|
+
on: [push, pull_request]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
lint:
|
|
7
|
+
runs-on: ubuntu-22.04
|
|
8
|
+
steps:
|
|
9
|
+
- uses: actions/checkout@v4
|
|
10
|
+
- name: Set up Python 3.8
|
|
11
|
+
uses: actions/setup-python@v5
|
|
12
|
+
with:
|
|
13
|
+
python-version: "3.8"
|
|
14
|
+
- name: Install dependencies
|
|
15
|
+
run: pip install -r requirements.txt
|
|
16
|
+
- name: Run pre-commit
|
|
17
|
+
run: pre-commit run --all-files
|
|
18
|
+
|
|
19
|
+
build:
|
|
20
|
+
runs-on: ubuntu-22.04
|
|
21
|
+
strategy:
|
|
22
|
+
matrix:
|
|
23
|
+
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
27
|
+
uses: actions/setup-python@v5
|
|
28
|
+
with:
|
|
29
|
+
python-version: ${{ matrix.python-version }}
|
|
30
|
+
- name: Install dependencies
|
|
31
|
+
run: pip install -r requirements.txt
|
|
32
|
+
- name: Install package
|
|
33
|
+
run: pip install .
|
|
34
|
+
- name: Run pytest
|
|
35
|
+
run: pytest
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pycqa/flake8
|
|
3
|
+
rev: "7.0.0"
|
|
4
|
+
hooks:
|
|
5
|
+
- id: flake8
|
|
6
|
+
- repo: https://github.com/jazzband/pip-tools
|
|
7
|
+
rev: 7.4.1
|
|
8
|
+
hooks:
|
|
9
|
+
- id: pip-compile
|
|
10
|
+
language_version: python3.8
|
|
11
|
+
args: ["--output-file=requirements.txt", "--extra=test", "--strip-extras", "pyproject.toml"]
|
|
12
|
+
files: '^(?:pyproject\.toml|requirements\.txt)$'
|
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: async-solipsism
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6
|
|
4
4
|
Summary: Asyncio event loop that doesn't interact with the outside world
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Author-email: bmerry@gmail.com
|
|
5
|
+
Author-email: Bruce Merry <bmerry@gmail.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/bmerry/async-solipsism
|
|
8
7
|
Classifier: Development Status :: 3 - Alpha
|
|
9
8
|
Classifier: Framework :: AsyncIO
|
|
10
9
|
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
11
10
|
Classifier: Topic :: Software Development :: Testing :: Mocking
|
|
12
|
-
Requires-Python: >=3.
|
|
11
|
+
Requires-Python: >=3.8
|
|
13
12
|
Description-Content-Type: text/markdown
|
|
14
|
-
Provides-Extra: test
|
|
15
13
|
License-File: LICENSE
|
|
14
|
+
Provides-Extra: test
|
|
15
|
+
Requires-Dist: pre-commit; extra == "test"
|
|
16
|
+
Requires-Dist: pytest; extra == "test"
|
|
17
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "test"
|
|
18
|
+
Requires-Dist: pytest-mock; extra == "test"
|
|
16
19
|
|
|
17
20
|
# A solipsist event loop
|
|
18
21
|
|
|
19
|
-
async-solipsism
|
|
22
|
+
async-solipsism provides a Python asyncio event loop that does not interact with
|
|
20
23
|
the outside world at all. This is ideal for writing unit tests that intend to
|
|
21
24
|
mock out real-world interactions. It makes for tests that are reliable
|
|
22
25
|
(unaffected by network outages), reproducible (not affected by random timing
|
|
@@ -100,11 +103,24 @@ The host and port are associated with the event loop, and are remembered until
|
|
|
100
103
|
the server is closed. Attempting to connect after closing the server, or to an
|
|
101
104
|
address that hasn't been registered, will raise a `ConnectionRefusedError`.
|
|
102
105
|
|
|
106
|
+
If you don't want the bother of picking non-colliding ports, you can pass a
|
|
107
|
+
port number of 0, and async-solipsism will bind the first port number above
|
|
108
|
+
60000 that is unused.
|
|
109
|
+
|
|
103
110
|
### Integration with pytest-asyncio
|
|
104
111
|
|
|
105
112
|
async-solipsism and pytest-asyncio complement each other well: just write a
|
|
106
|
-
custom `
|
|
107
|
-
override the default provided by pytest-asyncio:
|
|
113
|
+
custom `event_loop_policy` fixture in your test file or `conftest.py` and it
|
|
114
|
+
will override the default provided by pytest-asyncio:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
@pytest.fixture
|
|
118
|
+
def event_loop_policy():
|
|
119
|
+
return async_solipsism.EventLoopPolicy()
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The above is for pytest-asyncio 0.23+. If you're using an older version, then
|
|
123
|
+
instead you should do this:
|
|
108
124
|
|
|
109
125
|
```python
|
|
110
126
|
@pytest.fixture
|
|
@@ -126,14 +142,12 @@ from aiohttp import web, test_utils
|
|
|
126
142
|
|
|
127
143
|
|
|
128
144
|
@pytest.fixture
|
|
129
|
-
def
|
|
130
|
-
|
|
131
|
-
yield loop
|
|
132
|
-
loop.close()
|
|
145
|
+
def event_loop_policy():
|
|
146
|
+
return async_solipsism.EventLoopPolicy()
|
|
133
147
|
|
|
134
148
|
|
|
135
149
|
def socket_factory(host, port, family):
|
|
136
|
-
return async_solipsism.ListenSocket((host, port
|
|
150
|
+
return async_solipsism.ListenSocket((host, port))
|
|
137
151
|
|
|
138
152
|
|
|
139
153
|
async def test_integration():
|
|
@@ -150,10 +164,6 @@ the socket factory, although it may be possible to do by monkeypatching. In
|
|
|
150
164
|
practice you will probably want to define your own fixtures for the client
|
|
151
165
|
and server.
|
|
152
166
|
|
|
153
|
-
If you need to run more than one test server concurrently, you'll need to
|
|
154
|
-
extend the socket factory to assign them each a different port
|
|
155
|
-
(async-solipsism does not currently handle mapping port 0 to an unused port).
|
|
156
|
-
|
|
157
167
|
## Limitations
|
|
158
168
|
|
|
159
169
|
The requirement to have no interaction with the outside world naturally
|
|
@@ -183,12 +193,31 @@ Calling functions that are not supported will generally raise
|
|
|
183
193
|
|
|
184
194
|
## Changelog
|
|
185
195
|
|
|
196
|
+
### 0.6
|
|
197
|
+
|
|
198
|
+
- Drop support for Python 3.6 and 3.7, which have reached end of life.
|
|
199
|
+
- Add `EventLoopPolicy` to simplify integration with pytest-asyncio 0.23+,
|
|
200
|
+
and use it for the internal tests.
|
|
201
|
+
- Make `Socket.write` and `Socket.recv_into` accept arbitrary buffer-protocol
|
|
202
|
+
objects.
|
|
203
|
+
- Various packaging and developer experience improvements:
|
|
204
|
+
- Put the packaging metadata in pyproject.toml, removing setup.cfg.
|
|
205
|
+
- Pin versions for CI in requirements.txt using pip-compile.
|
|
206
|
+
- Use pre-commit to enforce flake8 and pip-compile.
|
|
207
|
+
- Remove wheel from `build-system.requires` (on setuptools advice).
|
|
208
|
+
- Test against Python 3.11 and 3.12.
|
|
209
|
+
- Run CI workflow against pull requests.
|
|
210
|
+
|
|
211
|
+
### 0.5
|
|
212
|
+
|
|
213
|
+
- Map port 0 to an unused port.
|
|
214
|
+
|
|
186
215
|
### 0.4
|
|
187
216
|
|
|
188
217
|
- Allow `call_soon_threadsafe` from the same thread.
|
|
189
218
|
- Don't warn when `SO_KEEPALIVE` is set on a socket.
|
|
190
219
|
- Update instructions for use with aiohttp.
|
|
191
|
-
- Add a pyproject.toml
|
|
220
|
+
- Add a pyproject.toml.
|
|
192
221
|
|
|
193
222
|
### 0.3
|
|
194
223
|
|
|
@@ -1,22 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: async-solipsism
|
|
3
|
-
Version: 0.4
|
|
4
|
-
Summary: Asyncio event loop that doesn't interact with the outside world
|
|
5
|
-
Home-page: https://github.com/bmerry/async-solipsism
|
|
6
|
-
Author: Bruce Merry
|
|
7
|
-
Author-email: bmerry@gmail.com
|
|
8
|
-
Classifier: Development Status :: 3 - Alpha
|
|
9
|
-
Classifier: Framework :: AsyncIO
|
|
10
|
-
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
11
|
-
Classifier: Topic :: Software Development :: Testing :: Mocking
|
|
12
|
-
Requires-Python: >=3.6
|
|
13
|
-
Description-Content-Type: text/markdown
|
|
14
|
-
Provides-Extra: test
|
|
15
|
-
License-File: LICENSE
|
|
16
|
-
|
|
17
1
|
# A solipsist event loop
|
|
18
2
|
|
|
19
|
-
async-solipsism
|
|
3
|
+
async-solipsism provides a Python asyncio event loop that does not interact with
|
|
20
4
|
the outside world at all. This is ideal for writing unit tests that intend to
|
|
21
5
|
mock out real-world interactions. It makes for tests that are reliable
|
|
22
6
|
(unaffected by network outages), reproducible (not affected by random timing
|
|
@@ -100,11 +84,24 @@ The host and port are associated with the event loop, and are remembered until
|
|
|
100
84
|
the server is closed. Attempting to connect after closing the server, or to an
|
|
101
85
|
address that hasn't been registered, will raise a `ConnectionRefusedError`.
|
|
102
86
|
|
|
87
|
+
If you don't want the bother of picking non-colliding ports, you can pass a
|
|
88
|
+
port number of 0, and async-solipsism will bind the first port number above
|
|
89
|
+
60000 that is unused.
|
|
90
|
+
|
|
103
91
|
### Integration with pytest-asyncio
|
|
104
92
|
|
|
105
93
|
async-solipsism and pytest-asyncio complement each other well: just write a
|
|
106
|
-
custom `
|
|
107
|
-
override the default provided by pytest-asyncio:
|
|
94
|
+
custom `event_loop_policy` fixture in your test file or `conftest.py` and it
|
|
95
|
+
will override the default provided by pytest-asyncio:
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
@pytest.fixture
|
|
99
|
+
def event_loop_policy():
|
|
100
|
+
return async_solipsism.EventLoopPolicy()
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The above is for pytest-asyncio 0.23+. If you're using an older version, then
|
|
104
|
+
instead you should do this:
|
|
108
105
|
|
|
109
106
|
```python
|
|
110
107
|
@pytest.fixture
|
|
@@ -126,14 +123,12 @@ from aiohttp import web, test_utils
|
|
|
126
123
|
|
|
127
124
|
|
|
128
125
|
@pytest.fixture
|
|
129
|
-
def
|
|
130
|
-
|
|
131
|
-
yield loop
|
|
132
|
-
loop.close()
|
|
126
|
+
def event_loop_policy():
|
|
127
|
+
return async_solipsism.EventLoopPolicy()
|
|
133
128
|
|
|
134
129
|
|
|
135
130
|
def socket_factory(host, port, family):
|
|
136
|
-
return async_solipsism.ListenSocket((host, port
|
|
131
|
+
return async_solipsism.ListenSocket((host, port))
|
|
137
132
|
|
|
138
133
|
|
|
139
134
|
async def test_integration():
|
|
@@ -150,10 +145,6 @@ the socket factory, although it may be possible to do by monkeypatching. In
|
|
|
150
145
|
practice you will probably want to define your own fixtures for the client
|
|
151
146
|
and server.
|
|
152
147
|
|
|
153
|
-
If you need to run more than one test server concurrently, you'll need to
|
|
154
|
-
extend the socket factory to assign them each a different port
|
|
155
|
-
(async-solipsism does not currently handle mapping port 0 to an unused port).
|
|
156
|
-
|
|
157
148
|
## Limitations
|
|
158
149
|
|
|
159
150
|
The requirement to have no interaction with the outside world naturally
|
|
@@ -183,12 +174,31 @@ Calling functions that are not supported will generally raise
|
|
|
183
174
|
|
|
184
175
|
## Changelog
|
|
185
176
|
|
|
177
|
+
### 0.6
|
|
178
|
+
|
|
179
|
+
- Drop support for Python 3.6 and 3.7, which have reached end of life.
|
|
180
|
+
- Add `EventLoopPolicy` to simplify integration with pytest-asyncio 0.23+,
|
|
181
|
+
and use it for the internal tests.
|
|
182
|
+
- Make `Socket.write` and `Socket.recv_into` accept arbitrary buffer-protocol
|
|
183
|
+
objects.
|
|
184
|
+
- Various packaging and developer experience improvements:
|
|
185
|
+
- Put the packaging metadata in pyproject.toml, removing setup.cfg.
|
|
186
|
+
- Pin versions for CI in requirements.txt using pip-compile.
|
|
187
|
+
- Use pre-commit to enforce flake8 and pip-compile.
|
|
188
|
+
- Remove wheel from `build-system.requires` (on setuptools advice).
|
|
189
|
+
- Test against Python 3.11 and 3.12.
|
|
190
|
+
- Run CI workflow against pull requests.
|
|
191
|
+
|
|
192
|
+
### 0.5
|
|
193
|
+
|
|
194
|
+
- Map port 0 to an unused port.
|
|
195
|
+
|
|
186
196
|
### 0.4
|
|
187
197
|
|
|
188
198
|
- Allow `call_soon_threadsafe` from the same thread.
|
|
189
199
|
- Don't warn when `SO_KEEPALIVE` is set on a socket.
|
|
190
200
|
- Update instructions for use with aiohttp.
|
|
191
|
-
- Add a pyproject.toml
|
|
201
|
+
- Add a pyproject.toml.
|
|
192
202
|
|
|
193
203
|
### 0.3
|
|
194
204
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=64", "setuptools-scm>=8.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "async-solipsism"
|
|
7
|
+
dynamic = ["version", "readme"]
|
|
8
|
+
description = "Asyncio event loop that doesn't interact with the outside world"
|
|
9
|
+
authors = [
|
|
10
|
+
{name = "Bruce Merry", email = "bmerry@gmail.com"}
|
|
11
|
+
]
|
|
12
|
+
requires-python = ">=3.8"
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 3 - Alpha",
|
|
15
|
+
"Framework :: AsyncIO",
|
|
16
|
+
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
|
|
17
|
+
"Topic :: Software Development :: Testing :: Mocking",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.urls]
|
|
21
|
+
Homepage = "https://github.com/bmerry/async-solipsism"
|
|
22
|
+
|
|
23
|
+
[project.optional-dependencies]
|
|
24
|
+
test = [
|
|
25
|
+
"pre-commit",
|
|
26
|
+
"pytest",
|
|
27
|
+
"pytest-asyncio>=0.23",
|
|
28
|
+
"pytest-mock",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[tool.setuptools_scm]
|
|
32
|
+
|
|
33
|
+
[tool.setuptools.dynamic]
|
|
34
|
+
version = {attr = "async_solipsism.__version__"}
|
|
35
|
+
readme = {file = ["README.md"], content-type = "text/markdown"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#
|
|
2
|
+
# This file is autogenerated by pip-compile with Python 3.8
|
|
3
|
+
# by the following command:
|
|
4
|
+
#
|
|
5
|
+
# pip-compile --extra=test --output-file=requirements.txt --strip-extras pyproject.toml
|
|
6
|
+
#
|
|
7
|
+
cfgv==3.4.0
|
|
8
|
+
# via pre-commit
|
|
9
|
+
distlib==0.3.8
|
|
10
|
+
# via virtualenv
|
|
11
|
+
exceptiongroup==1.2.0
|
|
12
|
+
# via pytest
|
|
13
|
+
filelock==3.13.1
|
|
14
|
+
# via virtualenv
|
|
15
|
+
identify==2.5.35
|
|
16
|
+
# via pre-commit
|
|
17
|
+
iniconfig==2.0.0
|
|
18
|
+
# via pytest
|
|
19
|
+
nodeenv==1.8.0
|
|
20
|
+
# via pre-commit
|
|
21
|
+
packaging==24.0
|
|
22
|
+
# via pytest
|
|
23
|
+
platformdirs==4.2.0
|
|
24
|
+
# via virtualenv
|
|
25
|
+
pluggy==1.4.0
|
|
26
|
+
# via pytest
|
|
27
|
+
pre-commit==3.5.0
|
|
28
|
+
# via async-solipsism (pyproject.toml)
|
|
29
|
+
pytest==8.1.1
|
|
30
|
+
# via
|
|
31
|
+
# async-solipsism (pyproject.toml)
|
|
32
|
+
# pytest-asyncio
|
|
33
|
+
# pytest-mock
|
|
34
|
+
pytest-asyncio==0.23.6
|
|
35
|
+
# via async-solipsism (pyproject.toml)
|
|
36
|
+
pytest-mock==3.14.0
|
|
37
|
+
# via async-solipsism (pyproject.toml)
|
|
38
|
+
pyyaml==6.0.1
|
|
39
|
+
# via pre-commit
|
|
40
|
+
tomli==2.0.1
|
|
41
|
+
# via pytest
|
|
42
|
+
virtualenv==20.25.1
|
|
43
|
+
# via pre-commit
|
|
44
|
+
|
|
45
|
+
# The following packages are considered to be unsafe in a requirements file:
|
|
46
|
+
# setuptools
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2020-
|
|
1
|
+
# Copyright 2020-2022, 2024 Bruce Merry
|
|
2
2
|
#
|
|
3
3
|
# This file is part of async-solipsism.
|
|
4
4
|
#
|
|
@@ -19,4 +19,4 @@ from .exceptions import * # noqa
|
|
|
19
19
|
from .socket import * # noqa
|
|
20
20
|
from .loop import * # noqa
|
|
21
21
|
|
|
22
|
-
__version__ = '0.
|
|
22
|
+
__version__ = '0.6'
|
|
@@ -24,7 +24,7 @@ from . import selector, socket as _socket
|
|
|
24
24
|
from .exceptions import SolipsismError
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
__all__ = ('EventLoop', 'stream_pairs')
|
|
27
|
+
__all__ = ('EventLoop', 'EventLoopPolicy', 'stream_pairs')
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
class EventLoop(asyncio.selector_events.BaseSelectorEventLoop):
|
|
@@ -113,8 +113,14 @@ class EventLoop(asyncio.selector_events.BaseSelectorEventLoop):
|
|
|
113
113
|
# TODO: what if host is actually a list?
|
|
114
114
|
addr = (host, port, 0, 0)
|
|
115
115
|
sock = _socket.ListenSocket(addr)
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
addr = sock.getsockname()
|
|
117
|
+
if addr[1] == 0: # Port 0: pick a free port
|
|
118
|
+
addr_list = list(addr)
|
|
119
|
+
addr_list[1] = 60000
|
|
120
|
+
while tuple(addr_list) in self.__listening_sockets:
|
|
121
|
+
addr_list[1] += 1
|
|
122
|
+
addr = tuple(addr_list)
|
|
123
|
+
sock._sockname = addr
|
|
118
124
|
if addr in self.__listening_sockets:
|
|
119
125
|
raise SolipsismError("Reuse of listening addresses is not supported")
|
|
120
126
|
self.__listening_sockets[addr] = sock
|
|
@@ -191,6 +197,11 @@ class EventLoop(asyncio.selector_events.BaseSelectorEventLoop):
|
|
|
191
197
|
super()._stop_serving(sock)
|
|
192
198
|
|
|
193
199
|
|
|
200
|
+
class EventLoopPolicy(asyncio.DefaultEventLoopPolicy):
|
|
201
|
+
def new_event_loop(self) -> EventLoop:
|
|
202
|
+
return EventLoop()
|
|
203
|
+
|
|
204
|
+
|
|
194
205
|
async def stream_pairs(capacity=None):
|
|
195
206
|
sock1, sock2 = _socket.socketpair(capacity=capacity)
|
|
196
207
|
streams1 = await asyncio.open_connection(sock=sock1)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2020 Bruce Merry
|
|
1
|
+
# Copyright 2020, 2024 Bruce Merry
|
|
2
2
|
#
|
|
3
3
|
# This file is part of async-solipsism.
|
|
4
4
|
#
|
|
@@ -30,6 +30,11 @@ DEFAULT_CAPACITY = 65536
|
|
|
30
30
|
__all__ = ('Socket', 'ListenSocket', 'Queue', 'socketpair')
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
def _view(bytes):
|
|
34
|
+
"""Get a 1D byte-typed memoryview of an object implementing the buffer protocol."""
|
|
35
|
+
return memoryview(bytes).cast("B")
|
|
36
|
+
|
|
37
|
+
|
|
33
38
|
class Queue:
|
|
34
39
|
def __init__(self, capacity=None):
|
|
35
40
|
self.capacity = capacity or DEFAULT_CAPACITY
|
|
@@ -50,11 +55,14 @@ class Queue:
|
|
|
50
55
|
raise BrokenPipeError(errno.EPIPE, 'Broken pipe')
|
|
51
56
|
if len(self) >= self.capacity:
|
|
52
57
|
return None
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
n
|
|
56
|
-
|
|
57
|
-
|
|
58
|
+
with _view(data) as view:
|
|
59
|
+
n = len(view)
|
|
60
|
+
if len(self) + n > self.capacity:
|
|
61
|
+
n = self.capacity - len(self)
|
|
62
|
+
with view[:n] as prefix:
|
|
63
|
+
self._buffer += prefix
|
|
64
|
+
else:
|
|
65
|
+
self._buffer += view
|
|
58
66
|
return n
|
|
59
67
|
|
|
60
68
|
def read(self, size=-1):
|
|
@@ -68,7 +76,8 @@ class Queue:
|
|
|
68
76
|
self._buffer = bytearray()
|
|
69
77
|
else:
|
|
70
78
|
n = min(size, len(self._buffer))
|
|
71
|
-
|
|
79
|
+
with memoryview(self._buffer) as view, view[:n] as prefix:
|
|
80
|
+
ret = bytes(prefix)
|
|
72
81
|
self._buffer = self._buffer[n:]
|
|
73
82
|
return ret
|
|
74
83
|
|
|
@@ -93,7 +102,7 @@ class SocketFd:
|
|
|
93
102
|
return hash(self.socket)
|
|
94
103
|
|
|
95
104
|
def __eq__(self, other):
|
|
96
|
-
if type(other)
|
|
105
|
+
if type(other) is SocketFd:
|
|
97
106
|
return self.socket is other.socket
|
|
98
107
|
return NotImplemented
|
|
99
108
|
|
|
@@ -164,11 +173,16 @@ class Socket(_SocketBase):
|
|
|
164
173
|
|
|
165
174
|
def recv_into(self, buffer, nbytes=0, flags=0):
|
|
166
175
|
# TODO: implement more efficiently?
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
176
|
+
with _view(buffer) as view:
|
|
177
|
+
if not nbytes:
|
|
178
|
+
nbytes = len(view)
|
|
179
|
+
if nbytes < 0:
|
|
180
|
+
raise ValueError("negative buffersize in recv_into")
|
|
181
|
+
if nbytes > len(view):
|
|
182
|
+
raise ValueError("buffer too small for requested bytes")
|
|
183
|
+
data = self.recv(nbytes)
|
|
184
|
+
view[:len(data)] = data
|
|
185
|
+
return len(data)
|
|
172
186
|
|
|
173
187
|
def send(self, bytes, flags=0):
|
|
174
188
|
self._check_closed()
|
|
@@ -1,6 +1,25 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: async-solipsism
|
|
3
|
+
Version: 0.6
|
|
4
|
+
Summary: Asyncio event loop that doesn't interact with the outside world
|
|
5
|
+
Author-email: Bruce Merry <bmerry@gmail.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/bmerry/async-solipsism
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Framework :: AsyncIO
|
|
9
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
10
|
+
Classifier: Topic :: Software Development :: Testing :: Mocking
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Provides-Extra: test
|
|
15
|
+
Requires-Dist: pre-commit; extra == "test"
|
|
16
|
+
Requires-Dist: pytest; extra == "test"
|
|
17
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "test"
|
|
18
|
+
Requires-Dist: pytest-mock; extra == "test"
|
|
19
|
+
|
|
1
20
|
# A solipsist event loop
|
|
2
21
|
|
|
3
|
-
async-solipsism
|
|
22
|
+
async-solipsism provides a Python asyncio event loop that does not interact with
|
|
4
23
|
the outside world at all. This is ideal for writing unit tests that intend to
|
|
5
24
|
mock out real-world interactions. It makes for tests that are reliable
|
|
6
25
|
(unaffected by network outages), reproducible (not affected by random timing
|
|
@@ -84,11 +103,24 @@ The host and port are associated with the event loop, and are remembered until
|
|
|
84
103
|
the server is closed. Attempting to connect after closing the server, or to an
|
|
85
104
|
address that hasn't been registered, will raise a `ConnectionRefusedError`.
|
|
86
105
|
|
|
106
|
+
If you don't want the bother of picking non-colliding ports, you can pass a
|
|
107
|
+
port number of 0, and async-solipsism will bind the first port number above
|
|
108
|
+
60000 that is unused.
|
|
109
|
+
|
|
87
110
|
### Integration with pytest-asyncio
|
|
88
111
|
|
|
89
112
|
async-solipsism and pytest-asyncio complement each other well: just write a
|
|
90
|
-
custom `
|
|
91
|
-
override the default provided by pytest-asyncio:
|
|
113
|
+
custom `event_loop_policy` fixture in your test file or `conftest.py` and it
|
|
114
|
+
will override the default provided by pytest-asyncio:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
@pytest.fixture
|
|
118
|
+
def event_loop_policy():
|
|
119
|
+
return async_solipsism.EventLoopPolicy()
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The above is for pytest-asyncio 0.23+. If you're using an older version, then
|
|
123
|
+
instead you should do this:
|
|
92
124
|
|
|
93
125
|
```python
|
|
94
126
|
@pytest.fixture
|
|
@@ -110,14 +142,12 @@ from aiohttp import web, test_utils
|
|
|
110
142
|
|
|
111
143
|
|
|
112
144
|
@pytest.fixture
|
|
113
|
-
def
|
|
114
|
-
|
|
115
|
-
yield loop
|
|
116
|
-
loop.close()
|
|
145
|
+
def event_loop_policy():
|
|
146
|
+
return async_solipsism.EventLoopPolicy()
|
|
117
147
|
|
|
118
148
|
|
|
119
149
|
def socket_factory(host, port, family):
|
|
120
|
-
return async_solipsism.ListenSocket((host, port
|
|
150
|
+
return async_solipsism.ListenSocket((host, port))
|
|
121
151
|
|
|
122
152
|
|
|
123
153
|
async def test_integration():
|
|
@@ -134,10 +164,6 @@ the socket factory, although it may be possible to do by monkeypatching. In
|
|
|
134
164
|
practice you will probably want to define your own fixtures for the client
|
|
135
165
|
and server.
|
|
136
166
|
|
|
137
|
-
If you need to run more than one test server concurrently, you'll need to
|
|
138
|
-
extend the socket factory to assign them each a different port
|
|
139
|
-
(async-solipsism does not currently handle mapping port 0 to an unused port).
|
|
140
|
-
|
|
141
167
|
## Limitations
|
|
142
168
|
|
|
143
169
|
The requirement to have no interaction with the outside world naturally
|
|
@@ -167,12 +193,31 @@ Calling functions that are not supported will generally raise
|
|
|
167
193
|
|
|
168
194
|
## Changelog
|
|
169
195
|
|
|
196
|
+
### 0.6
|
|
197
|
+
|
|
198
|
+
- Drop support for Python 3.6 and 3.7, which have reached end of life.
|
|
199
|
+
- Add `EventLoopPolicy` to simplify integration with pytest-asyncio 0.23+,
|
|
200
|
+
and use it for the internal tests.
|
|
201
|
+
- Make `Socket.write` and `Socket.recv_into` accept arbitrary buffer-protocol
|
|
202
|
+
objects.
|
|
203
|
+
- Various packaging and developer experience improvements:
|
|
204
|
+
- Put the packaging metadata in pyproject.toml, removing setup.cfg.
|
|
205
|
+
- Pin versions for CI in requirements.txt using pip-compile.
|
|
206
|
+
- Use pre-commit to enforce flake8 and pip-compile.
|
|
207
|
+
- Remove wheel from `build-system.requires` (on setuptools advice).
|
|
208
|
+
- Test against Python 3.11 and 3.12.
|
|
209
|
+
- Run CI workflow against pull requests.
|
|
210
|
+
|
|
211
|
+
### 0.5
|
|
212
|
+
|
|
213
|
+
- Map port 0 to an unused port.
|
|
214
|
+
|
|
170
215
|
### 0.4
|
|
171
216
|
|
|
172
217
|
- Allow `call_soon_threadsafe` from the same thread.
|
|
173
218
|
- Don't warn when `SO_KEEPALIVE` is set on a socket.
|
|
174
219
|
- Update instructions for use with aiohttp.
|
|
175
|
-
- Add a pyproject.toml
|
|
220
|
+
- Add a pyproject.toml.
|
|
176
221
|
|
|
177
222
|
### 0.3
|
|
178
223
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2020 Bruce Merry
|
|
1
|
+
# Copyright 2020, 2022 Bruce Merry
|
|
2
2
|
#
|
|
3
3
|
# This file is part of async-solipsism.
|
|
4
4
|
#
|
|
@@ -25,13 +25,12 @@ import async_solipsism
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
@pytest.fixture
|
|
28
|
-
def
|
|
29
|
-
|
|
30
|
-
yield loop
|
|
31
|
-
loop.close()
|
|
28
|
+
def event_loop_policy():
|
|
29
|
+
return async_solipsism.EventLoopPolicy()
|
|
32
30
|
|
|
33
31
|
|
|
34
|
-
async def test_sleep(
|
|
32
|
+
async def test_sleep():
|
|
33
|
+
event_loop = asyncio.get_running_loop()
|
|
35
34
|
assert event_loop.time() == 0.0
|
|
36
35
|
await asyncio.sleep(2)
|
|
37
36
|
assert event_loop.time() == 2.0
|
|
@@ -53,7 +52,9 @@ def test_sleep_forever(event_loop):
|
|
|
53
52
|
]
|
|
54
53
|
)
|
|
55
54
|
@pytest.mark.parametrize('delay', [False, True])
|
|
56
|
-
async def test_delayed_sock_recv(method, delay
|
|
55
|
+
async def test_delayed_sock_recv(method, delay):
|
|
56
|
+
event_loop = asyncio.get_running_loop()
|
|
57
|
+
|
|
57
58
|
async def delayed_write(wsock):
|
|
58
59
|
await asyncio.sleep(1)
|
|
59
60
|
wsock.send(b'Hello')
|
|
@@ -76,7 +77,9 @@ async def test_delayed_sock_recv(method, delay, event_loop):
|
|
|
76
77
|
|
|
77
78
|
|
|
78
79
|
@pytest.mark.parametrize('size', [10, 10**7])
|
|
79
|
-
async def test_sock_sendall(size
|
|
80
|
+
async def test_sock_sendall(size):
|
|
81
|
+
event_loop = asyncio.get_running_loop()
|
|
82
|
+
|
|
80
83
|
async def delayed_read(rsock):
|
|
81
84
|
n = 0
|
|
82
85
|
while True:
|
|
@@ -99,7 +102,8 @@ async def test_sock_sendall(size, event_loop):
|
|
|
99
102
|
assert n == size
|
|
100
103
|
|
|
101
104
|
|
|
102
|
-
async def test_connect_existing(
|
|
105
|
+
async def test_connect_existing(mocker):
|
|
106
|
+
event_loop = asyncio.get_running_loop()
|
|
103
107
|
sock1, sock2 = async_solipsism.socketpair()
|
|
104
108
|
transport1, protocol1 = await event_loop.connect_accepted_socket(
|
|
105
109
|
mocker.MagicMock, sock1)
|
|
@@ -128,7 +132,9 @@ async def test_stream():
|
|
|
128
132
|
|
|
129
133
|
|
|
130
134
|
@pytest.mark.parametrize('manual_socket', [False, True])
|
|
131
|
-
async def test_server(
|
|
135
|
+
async def test_server(manual_socket):
|
|
136
|
+
event_loop = asyncio.get_running_loop()
|
|
137
|
+
|
|
132
138
|
def callback(reader, writer):
|
|
133
139
|
server_conn.set_result((reader, writer))
|
|
134
140
|
|
|
@@ -156,7 +162,24 @@ async def test_server(event_loop, manual_socket):
|
|
|
156
162
|
await server.wait_closed()
|
|
157
163
|
|
|
158
164
|
|
|
159
|
-
async def
|
|
165
|
+
async def test_unused_port():
|
|
166
|
+
def callback(reader, writer):
|
|
167
|
+
pass
|
|
168
|
+
|
|
169
|
+
listen_socket1 = async_solipsism.ListenSocket(('localhost', 0))
|
|
170
|
+
listen_socket2 = async_solipsism.ListenSocket(('localhost', 0))
|
|
171
|
+
server1 = await asyncio.start_server(callback, sock=listen_socket1)
|
|
172
|
+
server2 = await asyncio.start_server(callback, sock=listen_socket2)
|
|
173
|
+
assert listen_socket1.getsockname()[1] == 60000
|
|
174
|
+
assert listen_socket2.getsockname()[1] == 60001
|
|
175
|
+
server1.close()
|
|
176
|
+
server2.close()
|
|
177
|
+
await server1.wait_closed()
|
|
178
|
+
await server2.wait_closed()
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
async def test_close_server():
|
|
182
|
+
event_loop = asyncio.get_running_loop()
|
|
160
183
|
server = await asyncio.start_server(lambda reader, writer: None, 'test.invalid', 1234)
|
|
161
184
|
server.close()
|
|
162
185
|
await server.wait_closed()
|
|
@@ -164,18 +187,21 @@ async def test_close_server(event_loop):
|
|
|
164
187
|
await event_loop.create_connection('test.invalid', 1234)
|
|
165
188
|
|
|
166
189
|
|
|
167
|
-
async def test_create_connection_no_listener(
|
|
190
|
+
async def test_create_connection_no_listener():
|
|
191
|
+
event_loop = asyncio.get_running_loop()
|
|
168
192
|
with pytest.raises(ConnectionRefusedError):
|
|
169
193
|
await event_loop.create_connection('test.invalid', 1234)
|
|
170
194
|
|
|
171
195
|
|
|
172
|
-
async def test_run_in_executor_implicit(
|
|
196
|
+
async def test_run_in_executor_implicit():
|
|
197
|
+
event_loop = asyncio.get_running_loop()
|
|
173
198
|
thread_id = await event_loop.run_in_executor(None, threading.get_ident)
|
|
174
199
|
assert isinstance(thread_id, int)
|
|
175
200
|
assert thread_id != threading.get_ident()
|
|
176
201
|
|
|
177
202
|
|
|
178
|
-
async def test_run_in_executor_explicit(
|
|
203
|
+
async def test_run_in_executor_explicit():
|
|
204
|
+
event_loop = asyncio.get_running_loop()
|
|
179
205
|
my_executor = concurrent.futures.ThreadPoolExecutor(1)
|
|
180
206
|
expected_thread_id = my_executor.submit(threading.get_ident).result()
|
|
181
207
|
assert isinstance(expected_thread_id, int)
|
|
@@ -185,7 +211,8 @@ async def test_run_in_executor_explicit(event_loop):
|
|
|
185
211
|
|
|
186
212
|
|
|
187
213
|
@pytest.mark.skipif("sys.version_info < (3, 7)")
|
|
188
|
-
async def test_sendfile(
|
|
214
|
+
async def test_sendfile(tmp_path):
|
|
215
|
+
event_loop = asyncio.get_running_loop()
|
|
189
216
|
tmp_file = tmp_path / 'test_sendfile.txt'
|
|
190
217
|
tmp_file.write_bytes(b'Hello world\n')
|
|
191
218
|
((reader1, writer1), (reader2, writer2)) = await async_solipsism.stream_pairs()
|
|
@@ -193,16 +220,23 @@ async def test_sendfile(event_loop, tmp_path):
|
|
|
193
220
|
await event_loop.sendfile(writer1.transport, f)
|
|
194
221
|
line = await reader2.readline()
|
|
195
222
|
assert line == b'Hello world\n'
|
|
223
|
+
writer1.close()
|
|
224
|
+
writer2.close()
|
|
225
|
+
await writer1.wait_closed()
|
|
226
|
+
await writer2.wait_closed()
|
|
196
227
|
|
|
197
228
|
|
|
198
|
-
async def test_call_soon_threadsafe(
|
|
229
|
+
async def test_call_soon_threadsafe():
|
|
230
|
+
event_loop = asyncio.get_running_loop()
|
|
199
231
|
future = event_loop.create_future()
|
|
200
232
|
event_loop.call_soon_threadsafe(future.set_result, 3)
|
|
201
233
|
result = await future
|
|
202
234
|
assert result == 3
|
|
203
235
|
|
|
204
236
|
|
|
205
|
-
async def test_call_soon_threadsafe_wrong_thread(
|
|
237
|
+
async def test_call_soon_threadsafe_wrong_thread():
|
|
238
|
+
event_loop = asyncio.get_running_loop()
|
|
239
|
+
|
|
206
240
|
def thread_func():
|
|
207
241
|
event_loop.call_soon_threadsafe(lambda: None)
|
|
208
242
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2020 Bruce Merry
|
|
1
|
+
# Copyright 2020, 2024 Bruce Merry
|
|
2
2
|
#
|
|
3
3
|
# This file is part of async-solipsism.
|
|
4
4
|
#
|
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
# You should have received a copy of the GNU General Public License
|
|
16
16
|
# along with async-solipsism. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
|
|
18
|
+
import array
|
|
18
19
|
import socket
|
|
20
|
+
import struct
|
|
19
21
|
|
|
20
22
|
import pytest
|
|
21
23
|
|
|
@@ -69,3 +71,41 @@ def test_use_after_shutdown(end):
|
|
|
69
71
|
assert sock0.recv(1) == b''
|
|
70
72
|
with pytest.raises(BrokenPipeError):
|
|
71
73
|
sock1.send(b'hello')
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def test_recv_into_array():
|
|
77
|
+
sock1, sock2 = async_solipsism.socketpair()
|
|
78
|
+
data = array.array("I", [1, 2, 3])
|
|
79
|
+
sock1.send(b"\xDE\xAD\xBE\xEF")
|
|
80
|
+
nbytes = sock2.recv_into(data)
|
|
81
|
+
assert nbytes == 4
|
|
82
|
+
assert data[0] == struct.unpack("I", b"\xDE\xAD\xBE\xEF")[0]
|
|
83
|
+
assert data[1] == 2
|
|
84
|
+
assert data[2] == 3
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_recv_into_array_partial():
|
|
88
|
+
sock1, sock2 = async_solipsism.socketpair()
|
|
89
|
+
data = array.array("I", [1, 2, 3])
|
|
90
|
+
sock1.send(b"\xDE\xAD\xBE\xEF\xCA\xFE")
|
|
91
|
+
nbytes = sock2.recv_into(data, 4)
|
|
92
|
+
assert nbytes == 4
|
|
93
|
+
assert data[0] == struct.unpack("I", b"\xDE\xAD\xBE\xEF")[0]
|
|
94
|
+
assert data[1] == 2
|
|
95
|
+
assert data[2] == 3
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_write_array():
|
|
99
|
+
sock1, sock2 = async_solipsism.socketpair(capacity=100)
|
|
100
|
+
data = array.array("I", range(64))
|
|
101
|
+
nbytes = sock1.send(data)
|
|
102
|
+
assert nbytes == 100
|
|
103
|
+
received = sock2.recv(-1)
|
|
104
|
+
assert len(received) == 100
|
|
105
|
+
assert memoryview(received) == memoryview(data).cast("B")[:100]
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@pytest.mark.parametrize("nbytes", [-1, 5])
|
|
109
|
+
def test_recv_into_bad_nbytes(sock, nbytes):
|
|
110
|
+
with pytest.raises(ValueError):
|
|
111
|
+
sock.recv_into(bytearray(4), nbytes=nbytes)
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
name: Run tests
|
|
2
|
-
|
|
3
|
-
on: [push]
|
|
4
|
-
|
|
5
|
-
jobs:
|
|
6
|
-
build:
|
|
7
|
-
runs-on: ubuntu-latest
|
|
8
|
-
strategy:
|
|
9
|
-
matrix:
|
|
10
|
-
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
|
|
11
|
-
|
|
12
|
-
steps:
|
|
13
|
-
- uses: actions/checkout@v2
|
|
14
|
-
- name: Set up Python ${{ matrix.python-version }}
|
|
15
|
-
uses: actions/setup-python@v2
|
|
16
|
-
with:
|
|
17
|
-
python-version: ${{ matrix.python-version }}
|
|
18
|
-
- name: Install dependencies
|
|
19
|
-
run: pip install -r requirements.txt
|
|
20
|
-
- name: Run flake8
|
|
21
|
-
run: flake8
|
|
22
|
-
- name: Install package
|
|
23
|
-
run: pip install .
|
|
24
|
-
- name: Run pytest
|
|
25
|
-
run: pytest
|
async-solipsism-0.4/setup.cfg
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
[metadata]
|
|
2
|
-
name = async-solipsism
|
|
3
|
-
version = attr: src.async_solipsism.__version__
|
|
4
|
-
description = Asyncio event loop that doesn't interact with the outside world
|
|
5
|
-
long_description = file:README.md
|
|
6
|
-
long_description_content_type = text/markdown
|
|
7
|
-
author = Bruce Merry
|
|
8
|
-
author_email = bmerry@gmail.com
|
|
9
|
-
url = https://github.com/bmerry/async-solipsism
|
|
10
|
-
classifiers =
|
|
11
|
-
Development Status :: 3 - Alpha
|
|
12
|
-
Framework :: AsyncIO
|
|
13
|
-
License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
14
|
-
Topic :: Software Development :: Testing :: Mocking
|
|
15
|
-
|
|
16
|
-
[options]
|
|
17
|
-
package_dir =
|
|
18
|
-
= src
|
|
19
|
-
packages = find:
|
|
20
|
-
python_requires = >= 3.6
|
|
21
|
-
|
|
22
|
-
[options.packages.find]
|
|
23
|
-
where = src
|
|
24
|
-
|
|
25
|
-
[options.extras_require]
|
|
26
|
-
test =
|
|
27
|
-
pytest
|
|
28
|
-
pytest-asyncio
|
|
29
|
-
pytest-mock
|
|
30
|
-
|
|
31
|
-
[egg_info]
|
|
32
|
-
tag_build =
|
|
33
|
-
tag_date = 0
|
|
34
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{async-solipsism-0.4 → async-solipsism-0.6}/src/async_solipsism.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|