portal 3.4.4__tar.gz → 3.5.1__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.4/portal.egg-info → portal-3.5.1}/PKG-INFO +1 -1
  2. {portal-3.4.4 → portal-3.5.1}/portal/__init__.py +1 -1
  3. {portal-3.4.4 → portal-3.5.1}/portal/client_socket.py +43 -35
  4. {portal-3.4.4 → portal-3.5.1}/portal/server_socket.py +20 -19
  5. {portal-3.4.4 → portal-3.5.1/portal.egg-info}/PKG-INFO +1 -1
  6. {portal-3.4.4 → portal-3.5.1}/LICENSE +0 -0
  7. {portal-3.4.4 → portal-3.5.1}/MANIFEST.in +0 -0
  8. {portal-3.4.4 → portal-3.5.1}/README.md +0 -0
  9. {portal-3.4.4 → portal-3.5.1}/portal/batching.py +0 -0
  10. {portal-3.4.4 → portal-3.5.1}/portal/buffers.py +0 -0
  11. {portal-3.4.4 → portal-3.5.1}/portal/client.py +0 -0
  12. {portal-3.4.4 → portal-3.5.1}/portal/contextlib.py +0 -0
  13. {portal-3.4.4 → portal-3.5.1}/portal/packlib.py +0 -0
  14. {portal-3.4.4 → portal-3.5.1}/portal/poollib.py +0 -0
  15. {portal-3.4.4 → portal-3.5.1}/portal/process.py +0 -0
  16. {portal-3.4.4 → portal-3.5.1}/portal/server.py +0 -0
  17. {portal-3.4.4 → portal-3.5.1}/portal/sharray.py +0 -0
  18. {portal-3.4.4 → portal-3.5.1}/portal/thread.py +0 -0
  19. {portal-3.4.4 → portal-3.5.1}/portal/utils.py +0 -0
  20. {portal-3.4.4 → portal-3.5.1}/portal.egg-info/SOURCES.txt +0 -0
  21. {portal-3.4.4 → portal-3.5.1}/portal.egg-info/dependency_links.txt +0 -0
  22. {portal-3.4.4 → portal-3.5.1}/portal.egg-info/requires.txt +0 -0
  23. {portal-3.4.4 → portal-3.5.1}/portal.egg-info/top_level.txt +0 -0
  24. {portal-3.4.4 → portal-3.5.1}/pyproject.toml +0 -0
  25. {portal-3.4.4 → portal-3.5.1}/requirements.txt +0 -0
  26. {portal-3.4.4 → portal-3.5.1}/setup.cfg +0 -0
  27. {portal-3.4.4 → portal-3.5.1}/setup.py +0 -0
  28. {portal-3.4.4 → portal-3.5.1}/tests/test_batching.py +0 -0
  29. {portal-3.4.4 → portal-3.5.1}/tests/test_client.py +0 -0
  30. {portal-3.4.4 → portal-3.5.1}/tests/test_errfile.py +0 -0
  31. {portal-3.4.4 → portal-3.5.1}/tests/test_pack.py +0 -0
  32. {portal-3.4.4 → portal-3.5.1}/tests/test_process.py +0 -0
  33. {portal-3.4.4 → portal-3.5.1}/tests/test_server.py +0 -0
  34. {portal-3.4.4 → portal-3.5.1}/tests/test_socket.py +0 -0
  35. {portal-3.4.4 → portal-3.5.1}/tests/test_thread.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: portal
3
- Version: 3.4.4
3
+ Version: 3.5.1
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.4'
1
+ __version__ = '3.5.1'
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
- idle_sleep: float = 0.0001
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,36 +136,37 @@ class ClientSocket:
130
136
 
131
137
  try:
132
138
 
133
- idle = True
134
- pairs = poll.poll(0.2)
135
- if pairs:
136
- _, mask = pairs[0]
137
-
138
- if mask & select.POLLIN:
139
- try:
140
- recvbuf.recv(sock)
141
- idle = False
142
- if recvbuf.done():
143
- if self.recvq.qsize() > self.options.max_recv_queue:
144
- raise RuntimeError('Too many incoming messages enqueued')
145
- msg = recvbuf.result()
146
- self.recvq.put(msg)
147
- [x(msg) for x in self.callbacks_recv]
148
- recvbuf = buffers.RecvBuffer(maxsize=self.options.max_msg_size)
149
- except BlockingIOError:
150
- pass
151
-
152
- if self.sendq and mask & select.POLLOUT:
153
- try:
154
- self.sendq[0].send(sock)
155
- idle = False
156
- if self.sendq[0].done():
157
- self.sendq.popleft()
158
- except BlockingIOError:
159
- pass
160
-
161
- if idle and self.options.idle_sleep:
162
- time.sleep(self.options.idle_sleep)
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)
144
+
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:
158
+ try:
159
+ self.sendq[0].send(sock)
160
+ if self.sendq[0].done():
161
+ self.sendq.popleft()
162
+ if not self.sendq:
163
+ writing = False
164
+ except BlockingIOError:
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
163
170
 
164
171
  except OSError as e:
165
172
  detail = f'{type(e).__name__}'
@@ -231,9 +238,10 @@ class ClientSocket:
231
238
  sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, after)
232
239
  sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, every)
233
240
  sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, fails)
234
- sock.setsockopt(
235
- socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT,
236
- 1000 * (after + every * fails))
241
+ if hasattr(socket, 'TCP_USER_TIMEOUT'): # Linux
242
+ sock.setsockopt(
243
+ socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT,
244
+ 1000 * (after + every * fails))
237
245
  if sys.platform == 'darwin':
238
246
  sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
239
247
  sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPALIVE, every)
@@ -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
- idle_sleep: float = 0.0001
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,38 +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
- idle = True
109
- writeable = []
110
113
  for key, mask in self.sel.select(timeout=0.2):
111
- 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:
112
118
  assert mask & selectors.EVENT_READ
113
119
  self._accept(key.fileobj)
114
- idle = False
115
120
  elif mask & selectors.EVENT_READ and self.reading:
116
121
  self._recv(key.data)
117
- idle = False
118
- elif mask & selectors.EVENT_WRITE:
119
- writeable.append(key.data)
120
- for conn in writeable:
121
- if not conn.sendbufs:
122
- continue
122
+ if not writing:
123
+ continue
124
+ pending = [conn for conn in self.conns.values() if conn.sendbufs]
125
+ for conn in pending:
123
126
  try:
124
127
  conn.sendbufs[0].send(conn.sock)
125
- idle = False
126
128
  if conn.sendbufs[0].done():
127
129
  conn.sendbufs.popleft()
130
+ if not any(conn.sendbufs for conn in pending):
131
+ writing = False
128
132
  except BlockingIOError:
129
133
  pass
130
134
  except ConnectionResetError:
131
- # The client is gone but we may have buffered messages left to
132
- # 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.
133
137
  pass
134
- if idle and self.options.idle_sleep:
135
- time.sleep(self.options.idle_sleep)
136
138
  except Exception as e:
137
139
  self.error = e
138
140
 
@@ -141,8 +143,7 @@ class ServerSocket:
141
143
  self._log(f'Accepted connection from {addr[0]}:{addr[1]}')
142
144
  sock.setblocking(False)
143
145
  conn = Connection(sock, addr)
144
- self.sel.register(
145
- sock, selectors.EVENT_READ | selectors.EVENT_WRITE, data=conn)
146
+ self.sel.register(sock, selectors.EVENT_READ, data=conn)
146
147
  self.conns[addr] = conn
147
148
 
148
149
  def _recv(self, conn):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: portal
3
- Version: 3.4.4
3
+ Version: 3.5.1
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