async-solipsism 0.6__tar.gz → 0.8__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.
Files changed (27) hide show
  1. {async-solipsism-0.6 → async_solipsism-0.8}/.github/workflows/test.yml +2 -1
  2. {async-solipsism-0.6 → async_solipsism-0.8}/.pre-commit-config.yaml +1 -1
  3. {async-solipsism-0.6/src/async_solipsism.egg-info → async_solipsism-0.8}/PKG-INFO +28 -4
  4. async-solipsism-0.6/PKG-INFO → async_solipsism-0.8/README.md +24 -20
  5. {async-solipsism-0.6 → async_solipsism-0.8}/pyproject.toml +1 -1
  6. async_solipsism-0.8/pytest.ini +3 -0
  7. {async-solipsism-0.6 → async_solipsism-0.8}/requirements.txt +13 -16
  8. {async-solipsism-0.6 → async_solipsism-0.8}/src/async_solipsism/__init__.py +2 -2
  9. {async-solipsism-0.6 → async_solipsism-0.8}/src/async_solipsism/loop.py +41 -4
  10. {async-solipsism-0.6 → async_solipsism-0.8}/src/async_solipsism/socket.py +13 -2
  11. async-solipsism-0.6/README.md → async_solipsism-0.8/src/async_solipsism.egg-info/PKG-INFO +44 -1
  12. {async-solipsism-0.6 → async_solipsism-0.8}/src/async_solipsism.egg-info/SOURCES.txt +0 -1
  13. async-solipsism-0.6/pytest.ini +0 -2
  14. async-solipsism-0.6/requirements.in +0 -4
  15. {async-solipsism-0.6 → async_solipsism-0.8}/.flake8 +0 -0
  16. {async-solipsism-0.6 → async_solipsism-0.8}/.gitignore +0 -0
  17. {async-solipsism-0.6 → async_solipsism-0.8}/LICENSE +0 -0
  18. {async-solipsism-0.6 → async_solipsism-0.8}/setup.cfg +0 -0
  19. {async-solipsism-0.6 → async_solipsism-0.8}/setup.py +0 -0
  20. {async-solipsism-0.6 → async_solipsism-0.8}/src/async_solipsism/clock.py +0 -0
  21. {async-solipsism-0.6 → async_solipsism-0.8}/src/async_solipsism/exceptions.py +0 -0
  22. {async-solipsism-0.6 → async_solipsism-0.8}/src/async_solipsism/selector.py +0 -0
  23. {async-solipsism-0.6 → async_solipsism-0.8}/src/async_solipsism.egg-info/dependency_links.txt +0 -0
  24. {async-solipsism-0.6 → async_solipsism-0.8}/src/async_solipsism.egg-info/requires.txt +0 -0
  25. {async-solipsism-0.6 → async_solipsism-0.8}/src/async_solipsism.egg-info/top_level.txt +0 -0
  26. {async-solipsism-0.6 → async_solipsism-0.8}/test/test_loop.py +0 -0
  27. {async-solipsism-0.6 → async_solipsism-0.8}/test/test_socket.py +0 -0
@@ -20,13 +20,14 @@ jobs:
20
20
  runs-on: ubuntu-22.04
21
21
  strategy:
22
22
  matrix:
23
- python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
23
+ python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
24
24
  steps:
25
25
  - uses: actions/checkout@v4
26
26
  - name: Set up Python ${{ matrix.python-version }}
27
27
  uses: actions/setup-python@v5
28
28
  with:
29
29
  python-version: ${{ matrix.python-version }}
30
+ allow-prereleases: true
30
31
  - name: Install dependencies
31
32
  run: pip install -r requirements.txt
32
33
  - name: Install package
@@ -1,6 +1,6 @@
1
1
  repos:
2
2
  - repo: https://github.com/pycqa/flake8
3
- rev: "7.0.0"
3
+ rev: "7.1.1"
4
4
  hooks:
5
5
  - id: flake8
6
6
  - repo: https://github.com/jazzband/pip-tools
@@ -1,10 +1,10 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: async-solipsism
3
- Version: 0.6
3
+ Version: 0.8
4
4
  Summary: Asyncio event loop that doesn't interact with the outside world
5
5
  Author-email: Bruce Merry <bmerry@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/bmerry/async-solipsism
7
- Classifier: Development Status :: 3 - Alpha
7
+ Classifier: Development Status :: 5 - Production/Stable
8
8
  Classifier: Framework :: AsyncIO
9
9
  Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
10
10
  Classifier: Topic :: Software Development :: Testing :: Mocking
@@ -16,6 +16,7 @@ Requires-Dist: pre-commit; extra == "test"
16
16
  Requires-Dist: pytest; extra == "test"
17
17
  Requires-Dist: pytest-asyncio>=0.23; extra == "test"
18
18
  Requires-Dist: pytest-mock; extra == "test"
19
+ Dynamic: license-file
19
20
 
20
21
  # A solipsist event loop
21
22
 
@@ -130,7 +131,14 @@ def event_loop():
130
131
  loop.close()
131
132
  ```
132
133
 
133
- ### Integration with pytest-aiohttp
134
+ ### Integration with aiohappyeyeballs
135
+
136
+ Unfortunately `aiohappyeyeballs.start_connection` doesn't work out of the box
137
+ because it relies on creating a real socket. I've created a replacement
138
+ `async_solipsism.aiohappyeyeballs_start_connection` that can be used to
139
+ replace it. See the aiohttp section below for example code.
140
+
141
+ ### Integration with aiohttp
134
142
 
135
143
  A little extra work is required to work with aiohttp's test utilities, but it
136
144
  is possible. The example below requires at least aiohttp 3.8.0.
@@ -146,6 +154,12 @@ def event_loop_policy():
146
154
  return async_solipsism.EventLoopPolicy()
147
155
 
148
156
 
157
+ @pytest.fixture(autouse=True)
158
+ def mock_start_connection(monkeypatch):
159
+ monkeypatch.setattr("aiohappyeyeballs.start_connection",
160
+ async_solipsism.aiohappyeyeballs_start_connection)
161
+
162
+
149
163
  def socket_factory(host, port, family):
150
164
  return async_solipsism.ListenSocket((host, port))
151
165
 
@@ -193,6 +207,16 @@ Calling functions that are not supported will generally raise
193
207
 
194
208
  ## Changelog
195
209
 
210
+ ### 0.8
211
+
212
+ - Support `socket.sendmsg`.
213
+ - Support `socket.getpeername` for listening sockets (raises OSError).
214
+ - Make `EventLoop.time` work even after the event loop is closed.
215
+
216
+ ### 0.7
217
+
218
+ - Add a replacement function for `aiohappyeyeballs.start_connection`.
219
+
196
220
  ### 0.6
197
221
 
198
222
  - Drop support for Python 3.6 and 3.7, which have reached end of life.
@@ -1,22 +1,3 @@
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
-
20
1
  # A solipsist event loop
21
2
 
22
3
  async-solipsism provides a Python asyncio event loop that does not interact with
@@ -130,7 +111,14 @@ def event_loop():
130
111
  loop.close()
131
112
  ```
132
113
 
133
- ### Integration with pytest-aiohttp
114
+ ### Integration with aiohappyeyeballs
115
+
116
+ Unfortunately `aiohappyeyeballs.start_connection` doesn't work out of the box
117
+ because it relies on creating a real socket. I've created a replacement
118
+ `async_solipsism.aiohappyeyeballs_start_connection` that can be used to
119
+ replace it. See the aiohttp section below for example code.
120
+
121
+ ### Integration with aiohttp
134
122
 
135
123
  A little extra work is required to work with aiohttp's test utilities, but it
136
124
  is possible. The example below requires at least aiohttp 3.8.0.
@@ -146,6 +134,12 @@ def event_loop_policy():
146
134
  return async_solipsism.EventLoopPolicy()
147
135
 
148
136
 
137
+ @pytest.fixture(autouse=True)
138
+ def mock_start_connection(monkeypatch):
139
+ monkeypatch.setattr("aiohappyeyeballs.start_connection",
140
+ async_solipsism.aiohappyeyeballs_start_connection)
141
+
142
+
149
143
  def socket_factory(host, port, family):
150
144
  return async_solipsism.ListenSocket((host, port))
151
145
 
@@ -193,6 +187,16 @@ Calling functions that are not supported will generally raise
193
187
 
194
188
  ## Changelog
195
189
 
190
+ ### 0.8
191
+
192
+ - Support `socket.sendmsg`.
193
+ - Support `socket.getpeername` for listening sockets (raises OSError).
194
+ - Make `EventLoop.time` work even after the event loop is closed.
195
+
196
+ ### 0.7
197
+
198
+ - Add a replacement function for `aiohappyeyeballs.start_connection`.
199
+
196
200
  ### 0.6
197
201
 
198
202
  - Drop support for Python 3.6 and 3.7, which have reached end of life.
@@ -11,7 +11,7 @@ authors = [
11
11
  ]
12
12
  requires-python = ">=3.8"
13
13
  classifiers = [
14
- "Development Status :: 3 - Alpha",
14
+ "Development Status :: 5 - Production/Stable",
15
15
  "Framework :: AsyncIO",
16
16
  "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
17
17
  "Topic :: Software Development :: Testing :: Mocking",
@@ -0,0 +1,3 @@
1
+ [pytest]
2
+ asyncio_default_fixture_loop_scope=function
3
+ asyncio_mode=auto
@@ -6,41 +6,38 @@
6
6
  #
7
7
  cfgv==3.4.0
8
8
  # via pre-commit
9
- distlib==0.3.8
9
+ distlib==0.3.9
10
10
  # via virtualenv
11
- exceptiongroup==1.2.0
11
+ exceptiongroup==1.2.2
12
12
  # via pytest
13
- filelock==3.13.1
13
+ filelock==3.16.1
14
14
  # via virtualenv
15
- identify==2.5.35
15
+ identify==2.6.1
16
16
  # via pre-commit
17
17
  iniconfig==2.0.0
18
18
  # via pytest
19
- nodeenv==1.8.0
19
+ nodeenv==1.9.1
20
20
  # via pre-commit
21
- packaging==24.0
21
+ packaging==24.2
22
22
  # via pytest
23
- platformdirs==4.2.0
23
+ platformdirs==4.3.6
24
24
  # via virtualenv
25
- pluggy==1.4.0
25
+ pluggy==1.5.0
26
26
  # via pytest
27
27
  pre-commit==3.5.0
28
28
  # via async-solipsism (pyproject.toml)
29
- pytest==8.1.1
29
+ pytest==8.3.4
30
30
  # via
31
31
  # async-solipsism (pyproject.toml)
32
32
  # pytest-asyncio
33
33
  # pytest-mock
34
- pytest-asyncio==0.23.6
34
+ pytest-asyncio==0.24.0
35
35
  # via async-solipsism (pyproject.toml)
36
36
  pytest-mock==3.14.0
37
37
  # via async-solipsism (pyproject.toml)
38
- pyyaml==6.0.1
38
+ pyyaml==6.0.2
39
39
  # via pre-commit
40
- tomli==2.0.1
40
+ tomli==2.2.1
41
41
  # via pytest
42
- virtualenv==20.25.1
42
+ virtualenv==20.29.0
43
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-2022, 2024 Bruce Merry
1
+ # Copyright 2020-2022, 2024-2025 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.6'
22
+ __version__ = '0.8'
@@ -1,4 +1,4 @@
1
- # Copyright 2020, 2022 Bruce Merry
1
+ # Copyright 2020, 2022, 2024-2025 Bruce Merry
2
2
  #
3
3
  # This file is part of async-solipsism.
4
4
  #
@@ -24,20 +24,22 @@ from . import selector, socket as _socket
24
24
  from .exceptions import SolipsismError
25
25
 
26
26
 
27
- __all__ = ('EventLoop', 'EventLoopPolicy', 'stream_pairs')
27
+ __all__ = ('EventLoop', 'EventLoopPolicy', 'aiohappyeyeballs_start_connection', 'stream_pairs')
28
28
 
29
29
 
30
30
  class EventLoop(asyncio.selector_events.BaseSelectorEventLoop):
31
31
  def __init__(self):
32
32
  super().__init__(selector=selector.Selector())
33
- self._selector = selector.Selector()
34
33
  self._clock_resolution = self._selector.clock.resolution
35
34
  # Map from (host, port) pair to ListenSocket
36
35
  self.__listening_sockets = {}
37
36
  self.__next_port = 1
37
+ # Copy this so that time() works even when after the event loop is
38
+ # closed (which clears _selector).
39
+ self.__clock = self._selector.clock
38
40
 
39
41
  def time(self):
40
- return self._selector.clock.time()
42
+ return self.__clock.time()
41
43
 
42
44
  def call_soon_threadsafe(self, callback, *args, context=None):
43
45
  if self._thread_id == threading.get_ident():
@@ -133,6 +135,27 @@ class EventLoop(asyncio.selector_events.BaseSelectorEventLoop):
133
135
  **kwargs
134
136
  )
135
137
 
138
+ async def aiohappyeyeballs_start_connection(self,
139
+ addr_infos,
140
+ *,
141
+ local_addr_infos=None,
142
+ happy_eyeballs_delay=None,
143
+ interleave=None):
144
+ for addr_info in addr_infos:
145
+ addr = addr_info[4]
146
+ addr = _socket._normalise_ipv6_sockaddr(addr)
147
+ if addr in self.__listening_sockets:
148
+ listener = self.__listening_sockets[addr]
149
+ if local_addr_infos:
150
+ local_addr_info = local_addr_infos[0]
151
+ else:
152
+ port = self.__next_port
153
+ self.__next_port += 1
154
+ local_addr_info = ('::1', port, 0, 0)
155
+ sock = await listener.make_connection(local_addr_info)
156
+ return sock
157
+ raise ConnectionRefusedError('No socket listening on requested addresses')
158
+
136
159
  async def connect_read_pipe(self, protocol_factory, pipe):
137
160
  raise SolipsismError("connect_read_pipe is not supported")
138
161
 
@@ -207,3 +230,17 @@ async def stream_pairs(capacity=None):
207
230
  streams1 = await asyncio.open_connection(sock=sock1)
208
231
  streams2 = await asyncio.open_connection(sock=sock2)
209
232
  return streams1, streams2
233
+
234
+
235
+ async def aiohappyeyeballs_start_connection(addr_infos,
236
+ *,
237
+ local_addr_infos=None,
238
+ happy_eyeballs_delay=None,
239
+ interleave=None,
240
+ loop=None):
241
+ if loop is None:
242
+ loop = asyncio.get_running_loop()
243
+ return await loop.aiohappyeyeballs_start_connection(addr_infos,
244
+ local_addr_infos=local_addr_infos,
245
+ happy_eyeballs_delay=happy_eyeballs_delay,
246
+ interleave=interleave)
@@ -1,4 +1,4 @@
1
- # Copyright 2020, 2024 Bruce Merry
1
+ # Copyright 2020, 2024-2025 Bruce Merry
2
2
  #
3
3
  # This file is part of async-solipsism.
4
4
  #
@@ -120,7 +120,7 @@ class _SocketBase:
120
120
 
121
121
  def setblocking(self, flag):
122
122
  if flag:
123
- raise SolipsismError('Socket only support non-blocking operation')
123
+ raise SolipsismError('Socket only supports non-blocking operation')
124
124
 
125
125
  def setsockopt(self, level, optname, value, optlen=None):
126
126
  key = (level, optname)
@@ -188,6 +188,14 @@ class Socket(_SocketBase):
188
188
  self._check_closed()
189
189
  return self._write_queue.write(bytes)
190
190
 
191
+ def sendmsg(self, buffers, ancdata=None, flags=0, address=None, /):
192
+ self._check_closed()
193
+ if ancdata is not None:
194
+ raise SolipsismError('ancilliary data is not supported in sendmsg')
195
+ if address is not None:
196
+ raise SolipsismError('address is not supported in sendmsg')
197
+ return self._write_queue.write(b"".join(buffers))
198
+
191
199
  def read_ready(self):
192
200
  return self._read_queue is None or self._read_queue.read_ready()
193
201
 
@@ -217,6 +225,9 @@ class ListenSocket(_SocketBase):
217
225
  def getsockname(self):
218
226
  return self._sockname
219
227
 
228
+ def getpeername(self):
229
+ raise OSError(errno.ENOTCONN, "Transport endpoint is not connected")
230
+
220
231
  def listen(self, backlog=None):
221
232
  pass
222
233
 
@@ -1,3 +1,23 @@
1
+ Metadata-Version: 2.4
2
+ Name: async-solipsism
3
+ Version: 0.8
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 :: 5 - Production/Stable
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
+ Dynamic: license-file
20
+
1
21
  # A solipsist event loop
2
22
 
3
23
  async-solipsism provides a Python asyncio event loop that does not interact with
@@ -111,7 +131,14 @@ def event_loop():
111
131
  loop.close()
112
132
  ```
113
133
 
114
- ### Integration with pytest-aiohttp
134
+ ### Integration with aiohappyeyeballs
135
+
136
+ Unfortunately `aiohappyeyeballs.start_connection` doesn't work out of the box
137
+ because it relies on creating a real socket. I've created a replacement
138
+ `async_solipsism.aiohappyeyeballs_start_connection` that can be used to
139
+ replace it. See the aiohttp section below for example code.
140
+
141
+ ### Integration with aiohttp
115
142
 
116
143
  A little extra work is required to work with aiohttp's test utilities, but it
117
144
  is possible. The example below requires at least aiohttp 3.8.0.
@@ -127,6 +154,12 @@ def event_loop_policy():
127
154
  return async_solipsism.EventLoopPolicy()
128
155
 
129
156
 
157
+ @pytest.fixture(autouse=True)
158
+ def mock_start_connection(monkeypatch):
159
+ monkeypatch.setattr("aiohappyeyeballs.start_connection",
160
+ async_solipsism.aiohappyeyeballs_start_connection)
161
+
162
+
130
163
  def socket_factory(host, port, family):
131
164
  return async_solipsism.ListenSocket((host, port))
132
165
 
@@ -174,6 +207,16 @@ Calling functions that are not supported will generally raise
174
207
 
175
208
  ## Changelog
176
209
 
210
+ ### 0.8
211
+
212
+ - Support `socket.sendmsg`.
213
+ - Support `socket.getpeername` for listening sockets (raises OSError).
214
+ - Make `EventLoop.time` work even after the event loop is closed.
215
+
216
+ ### 0.7
217
+
218
+ - Add a replacement function for `aiohappyeyeballs.start_connection`.
219
+
177
220
  ### 0.6
178
221
 
179
222
  - Drop support for Python 3.6 and 3.7, which have reached end of life.
@@ -5,7 +5,6 @@ LICENSE
5
5
  README.md
6
6
  pyproject.toml
7
7
  pytest.ini
8
- requirements.in
9
8
  requirements.txt
10
9
  setup.py
11
10
  .github/workflows/test.yml
@@ -1,2 +0,0 @@
1
- [pytest]
2
- asyncio_mode=auto
@@ -1,4 +0,0 @@
1
- pre-commit
2
- pytest
3
- pytest-asyncio>=0.23
4
- pytest-mock
File without changes
File without changes
File without changes
File without changes
File without changes