portal 3.4.3__tar.gz → 3.5.0__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 (35) hide show
  1. {portal-3.4.3/portal.egg-info → portal-3.5.0}/PKG-INFO +1 -1
  2. {portal-3.4.3 → portal-3.5.0}/portal/__init__.py +1 -1
  3. {portal-3.4.3 → portal-3.5.0}/portal/client_socket.py +32 -25
  4. {portal-3.4.3 → portal-3.5.0}/portal/server_socket.py +20 -17
  5. {portal-3.4.3 → portal-3.5.0/portal.egg-info}/PKG-INFO +1 -1
  6. {portal-3.4.3 → portal-3.5.0}/LICENSE +0 -0
  7. {portal-3.4.3 → portal-3.5.0}/MANIFEST.in +0 -0
  8. {portal-3.4.3 → portal-3.5.0}/README.md +0 -0
  9. {portal-3.4.3 → portal-3.5.0}/portal/batching.py +0 -0
  10. {portal-3.4.3 → portal-3.5.0}/portal/buffers.py +0 -0
  11. {portal-3.4.3 → portal-3.5.0}/portal/client.py +0 -0
  12. {portal-3.4.3 → portal-3.5.0}/portal/contextlib.py +0 -0
  13. {portal-3.4.3 → portal-3.5.0}/portal/packlib.py +0 -0
  14. {portal-3.4.3 → portal-3.5.0}/portal/poollib.py +0 -0
  15. {portal-3.4.3 → portal-3.5.0}/portal/process.py +0 -0
  16. {portal-3.4.3 → portal-3.5.0}/portal/server.py +0 -0
  17. {portal-3.4.3 → portal-3.5.0}/portal/sharray.py +0 -0
  18. {portal-3.4.3 → portal-3.5.0}/portal/thread.py +0 -0
  19. {portal-3.4.3 → portal-3.5.0}/portal/utils.py +0 -0
  20. {portal-3.4.3 → portal-3.5.0}/portal.egg-info/SOURCES.txt +0 -0
  21. {portal-3.4.3 → portal-3.5.0}/portal.egg-info/dependency_links.txt +0 -0
  22. {portal-3.4.3 → portal-3.5.0}/portal.egg-info/requires.txt +0 -0
  23. {portal-3.4.3 → portal-3.5.0}/portal.egg-info/top_level.txt +0 -0
  24. {portal-3.4.3 → portal-3.5.0}/pyproject.toml +0 -0
  25. {portal-3.4.3 → portal-3.5.0}/requirements.txt +0 -0
  26. {portal-3.4.3 → portal-3.5.0}/setup.cfg +0 -0
  27. {portal-3.4.3 → portal-3.5.0}/setup.py +0 -0
  28. {portal-3.4.3 → portal-3.5.0}/tests/test_batching.py +0 -0
  29. {portal-3.4.3 → portal-3.5.0}/tests/test_client.py +0 -0
  30. {portal-3.4.3 → portal-3.5.0}/tests/test_errfile.py +0 -0
  31. {portal-3.4.3 → portal-3.5.0}/tests/test_pack.py +0 -0
  32. {portal-3.4.3 → portal-3.5.0}/tests/test_process.py +0 -0
  33. {portal-3.4.3 → portal-3.5.0}/tests/test_server.py +0 -0
  34. {portal-3.4.3 → portal-3.5.0}/tests/test_socket.py +0 -0
  35. {portal-3.4.3 → portal-3.5.0}/tests/test_thread.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: portal
3
- Version: 3.4.3
3
+ Version: 3.5.0
4
4
  Summary: Fast and reliable distributed systems in Python
5
5
  Home-page: http://github.com/danijar/portal
6
6
  Author: Danijar Hafner
@@ -1,4 +1,4 @@
1
- __version__ = '3.4.3'
1
+ __version__ = '3.5.0'
2
2
 
3
3
  import multiprocessing as mp
4
4
  try:
@@ -1,5 +1,6 @@
1
1
  import collections
2
2
  import dataclasses
3
+ import os
3
4
  import queue
4
5
  import select
5
6
  import socket
@@ -30,7 +31,6 @@ class Options:
30
31
  logging: bool = True
31
32
  logging_color: str = 'yellow'
32
33
  connect_wait: float = 0.1
33
- loop_sleep: float = 0.0
34
34
 
35
35
 
36
36
  class ClientSocket:
@@ -52,6 +52,7 @@ class ClientSocket:
52
52
  self.wantconn = threading.Event()
53
53
  self.sendq = collections.deque()
54
54
  self.recvq = queue.Queue()
55
+ self.get_signal, self.set_signal = os.pipe()
55
56
 
56
57
  self.running = True
57
58
  self.thread = thread.Thread(self._loop, name=f'{name}Loop')
@@ -76,6 +77,7 @@ class ClientSocket:
76
77
  self.require_connection(timeout)
77
78
  maxsize = self.options.max_msg_size
78
79
  self.sendq.append(buffers.SendBuffer(*data, maxsize=maxsize))
80
+ os.write(self.set_signal, bytes(1))
79
81
 
80
82
  def recv(self, timeout=None):
81
83
  assert self.running
@@ -98,6 +100,8 @@ class ClientSocket:
98
100
  self.running = False
99
101
  self.thread.join(timeout)
100
102
  self.thread.kill()
103
+ os.close(self.get_signal)
104
+ os.close(self.set_signal)
101
105
 
102
106
  def require_connection(self, timeout):
103
107
  if self.connected:
@@ -111,7 +115,9 @@ class ClientSocket:
111
115
  recvbuf = buffers.RecvBuffer(maxsize=self.options.max_msg_size)
112
116
  sock = None
113
117
  poll = select.poll()
118
+ poll.register(self.get_signal, select.POLLIN)
114
119
  isconn = False # Local mirror of self.isconn without the lock.
120
+ writing = False
115
121
 
116
122
  while self.running or (self.sendq and isconn):
117
123
 
@@ -121,7 +127,7 @@ class ClientSocket:
121
127
  sock = self._connect()
122
128
  if not sock:
123
129
  break
124
- poll.register(sock, select.POLLIN | select.POLLOUT)
130
+ poll.register(sock, select.POLLIN)
125
131
  self.isconn.set()
126
132
  isconn = True
127
133
  if not self.options.autoconn:
@@ -130,33 +136,37 @@ class ClientSocket:
130
136
 
131
137
  try:
132
138
 
133
- # TODO: According to the py-spy profiler, the GIL is held during
134
- # polling. Is there a way to avoid that?
135
- pairs = poll.poll(0.2)
136
- if not pairs:
137
- continue
138
- _, mask = pairs[0]
139
-
140
- if mask & select.POLLIN:
141
- try:
142
- recvbuf.recv(sock)
143
- if recvbuf.done():
144
- if self.recvq.qsize() > self.options.max_recv_queue:
145
- raise RuntimeError('Too many incoming messages enqueued')
146
- msg = recvbuf.result()
147
- self.recvq.put(msg)
148
- [x(msg) for x in self.callbacks_recv]
149
- recvbuf = buffers.RecvBuffer(maxsize=self.options.max_msg_size)
150
- except BlockingIOError:
151
- pass
139
+ if not writing:
140
+ fds = [fd for fd, _ in poll.poll(0.2)]
141
+ if self.get_signal in fds:
142
+ writing = True
143
+ os.read(self.get_signal, 1)
152
144
 
153
- if self.sendq and mask & select.POLLOUT:
145
+ try:
146
+ recvbuf.recv(sock)
147
+ if recvbuf.done():
148
+ if self.recvq.qsize() > self.options.max_recv_queue:
149
+ raise RuntimeError('Too many incoming messages enqueued')
150
+ msg = recvbuf.result()
151
+ self.recvq.put(msg)
152
+ [x(msg) for x in self.callbacks_recv]
153
+ recvbuf = buffers.RecvBuffer(maxsize=self.options.max_msg_size)
154
+ except BlockingIOError:
155
+ pass
156
+
157
+ if self.sendq:
154
158
  try:
155
159
  self.sendq[0].send(sock)
156
160
  if self.sendq[0].done():
157
161
  self.sendq.popleft()
162
+ if not self.sendq:
163
+ writing = False
158
164
  except BlockingIOError:
159
165
  pass
166
+ except ConnectionResetError:
167
+ # The server is gone but we may have buffered messages left to
168
+ # read, so we keep the socket open until recv() fails.
169
+ pass
160
170
 
161
171
  except OSError as e:
162
172
  detail = f'{type(e).__name__}'
@@ -176,9 +186,6 @@ class ClientSocket:
176
186
  [x() for x in self.callbacks_disc]
177
187
  continue
178
188
 
179
- if self.options.loop_sleep:
180
- time.sleep(self.options.loop_sleep)
181
-
182
189
  if sock:
183
190
  sock.close()
184
191
 
@@ -1,9 +1,9 @@
1
1
  import collections
2
2
  import dataclasses
3
+ import os
3
4
  import queue
4
5
  import selectors
5
6
  import socket
6
- import time
7
7
 
8
8
  from . import buffers
9
9
  from . import contextlib
@@ -32,7 +32,6 @@ class Options:
32
32
  max_send_queue: int = 4096
33
33
  logging: bool = True
34
34
  logging_color: str = 'blue'
35
- loop_sleep: float = 0.0
36
35
 
37
36
 
38
37
  class ServerSocket:
@@ -56,7 +55,9 @@ class ServerSocket:
56
55
  self.sock.bind(self.addr)
57
56
  self.sock.setblocking(False)
58
57
  self.sock.listen(8192)
58
+ self.get_signal, self.set_signal = os.pipe()
59
59
  self.sel = selectors.DefaultSelector()
60
+ self.sel.register(self.get_signal, selectors.EVENT_READ, data='signal')
60
61
  self.sel.register(self.sock, selectors.EVENT_READ, data=None)
61
62
  self._log(f'Listening at {self.addr[0]}:{self.addr[1]}')
62
63
  self.conns = {}
@@ -89,6 +90,7 @@ class ServerSocket:
89
90
  try:
90
91
  self.conns[addr].sendbufs.append(
91
92
  buffers.SendBuffer(*data, maxsize=maxsize))
93
+ os.write(self.set_signal, bytes(1))
92
94
  except KeyError:
93
95
  self._log('Dropping message to disconnected client')
94
96
 
@@ -101,36 +103,38 @@ class ServerSocket:
101
103
  [conn.sock.close() for conn in self.conns.values()]
102
104
  self.sock.close()
103
105
  self.sel.close()
106
+ os.close(self.get_signal)
107
+ os.close(self.set_signal)
104
108
 
105
109
  def _loop(self):
110
+ writing = False
106
111
  try:
107
112
  while self.running or self._numsending():
108
- writeable = []
109
- # TODO: According to the py-spy profiler, the GIL is held during
110
- # polling. Is there a way to avoid that?
111
113
  for key, mask in self.sel.select(timeout=0.2):
112
- if key.data is None and self.reading:
114
+ if key.data == 'signal':
115
+ writing = True
116
+ os.read(self.get_signal, 1)
117
+ elif key.data is None and self.reading:
113
118
  assert mask & selectors.EVENT_READ
114
119
  self._accept(key.fileobj)
115
120
  elif mask & selectors.EVENT_READ and self.reading:
116
121
  self._recv(key.data)
117
- elif mask & selectors.EVENT_WRITE:
118
- writeable.append(key.data)
119
- for conn in writeable:
120
- if not conn.sendbufs:
121
- continue
122
+ if not writing:
123
+ continue
124
+ pending = [conn for conn in self.conns.values() if conn.sendbufs]
125
+ for conn in pending:
122
126
  try:
123
127
  conn.sendbufs[0].send(conn.sock)
124
128
  if conn.sendbufs[0].done():
125
129
  conn.sendbufs.popleft()
130
+ if not any(conn.sendbufs for conn in pending):
131
+ writing = False
126
132
  except BlockingIOError:
127
133
  pass
128
134
  except ConnectionResetError:
129
- # The client is gone but we may have buffered messages left to
130
- # read, so we keep the socket open until recv() fails.
135
+ # The client is gone but we may have buffered messages left
136
+ # to read, so we keep the socket open until recv() fails.
131
137
  pass
132
- if self.options.loop_sleep:
133
- time.sleep(self.options.loop_sleep)
134
138
  except Exception as e:
135
139
  self.error = e
136
140
 
@@ -139,8 +143,7 @@ class ServerSocket:
139
143
  self._log(f'Accepted connection from {addr[0]}:{addr[1]}')
140
144
  sock.setblocking(False)
141
145
  conn = Connection(sock, addr)
142
- self.sel.register(
143
- sock, selectors.EVENT_READ | selectors.EVENT_WRITE, data=conn)
146
+ self.sel.register(sock, selectors.EVENT_READ, data=conn)
144
147
  self.conns[addr] = conn
145
148
 
146
149
  def _recv(self, conn):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: portal
3
- Version: 3.4.3
3
+ Version: 3.5.0
4
4
  Summary: Fast and reliable distributed systems in Python
5
5
  Home-page: http://github.com/danijar/portal
6
6
  Author: Danijar Hafner
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
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