portal 3.2.0__tar.gz → 3.2.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.2.0/portal.egg-info → portal-3.2.1}/PKG-INFO +1 -1
  2. {portal-3.2.0 → portal-3.2.1}/portal/__init__.py +1 -1
  3. {portal-3.2.0 → portal-3.2.1}/portal/process.py +6 -5
  4. {portal-3.2.0 → portal-3.2.1}/portal/server.py +3 -0
  5. {portal-3.2.0 → portal-3.2.1}/portal/thread.py +0 -1
  6. {portal-3.2.0 → portal-3.2.1/portal.egg-info}/PKG-INFO +1 -1
  7. {portal-3.2.0 → portal-3.2.1}/tests/test_process.py +22 -5
  8. {portal-3.2.0 → portal-3.2.1}/tests/test_server.py +23 -2
  9. {portal-3.2.0 → portal-3.2.1}/tests/test_thread.py +1 -1
  10. {portal-3.2.0 → portal-3.2.1}/LICENSE +0 -0
  11. {portal-3.2.0 → portal-3.2.1}/MANIFEST.in +0 -0
  12. {portal-3.2.0 → portal-3.2.1}/README.md +0 -0
  13. {portal-3.2.0 → portal-3.2.1}/portal/batching.py +0 -0
  14. {portal-3.2.0 → portal-3.2.1}/portal/buffers.py +0 -0
  15. {portal-3.2.0 → portal-3.2.1}/portal/client.py +0 -0
  16. {portal-3.2.0 → portal-3.2.1}/portal/client_socket.py +0 -0
  17. {portal-3.2.0 → portal-3.2.1}/portal/contextlib.py +0 -0
  18. {portal-3.2.0 → portal-3.2.1}/portal/packlib.py +0 -0
  19. {portal-3.2.0 → portal-3.2.1}/portal/poollib.py +0 -0
  20. {portal-3.2.0 → portal-3.2.1}/portal/server_socket.py +0 -0
  21. {portal-3.2.0 → portal-3.2.1}/portal/sharray.py +0 -0
  22. {portal-3.2.0 → portal-3.2.1}/portal/utils.py +0 -0
  23. {portal-3.2.0 → portal-3.2.1}/portal.egg-info/SOURCES.txt +0 -0
  24. {portal-3.2.0 → portal-3.2.1}/portal.egg-info/dependency_links.txt +0 -0
  25. {portal-3.2.0 → portal-3.2.1}/portal.egg-info/requires.txt +0 -0
  26. {portal-3.2.0 → portal-3.2.1}/portal.egg-info/top_level.txt +0 -0
  27. {portal-3.2.0 → portal-3.2.1}/pyproject.toml +0 -0
  28. {portal-3.2.0 → portal-3.2.1}/requirements.txt +0 -0
  29. {portal-3.2.0 → portal-3.2.1}/setup.cfg +0 -0
  30. {portal-3.2.0 → portal-3.2.1}/setup.py +0 -0
  31. {portal-3.2.0 → portal-3.2.1}/tests/test_batching.py +0 -0
  32. {portal-3.2.0 → portal-3.2.1}/tests/test_client.py +0 -0
  33. {portal-3.2.0 → portal-3.2.1}/tests/test_errfile.py +0 -0
  34. {portal-3.2.0 → portal-3.2.1}/tests/test_pack.py +0 -0
  35. {portal-3.2.0 → portal-3.2.1}/tests/test_socket.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: portal
3
- Version: 3.2.0
3
+ Version: 3.2.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.2.0'
1
+ __version__ = '3.2.1'
2
2
 
3
3
  import multiprocessing as mp
4
4
  try:
@@ -1,6 +1,5 @@
1
1
  import atexit
2
2
  import errno
3
- import os
4
3
  import traceback
5
4
 
6
5
  import cloudpickle
@@ -37,8 +36,10 @@ class Process:
37
36
  name = name or getattr(fn, '__name__', 'process')
38
37
  fn = cloudpickle.dumps(fn)
39
38
  options = contextlib.context.options()
39
+ self.ready = contextlib.context.mp.Barrier(2)
40
40
  self.process = contextlib.context.mp.Process(
41
- target=self._wrapper, name=name, args=(options, name, fn, args))
41
+ target=self._wrapper, name=name,
42
+ args=(options, self.ready, name, fn, args))
42
43
  self.started = False
43
44
  self.killed = False
44
45
  self.thepid = None
@@ -73,6 +74,7 @@ class Process:
73
74
  assert not self.started
74
75
  self.started = True
75
76
  self.process.start()
77
+ self.ready.wait()
76
78
  self.thepid = self.process.pid
77
79
  assert self.thepid is not None
78
80
  return self
@@ -83,8 +85,6 @@ class Process:
83
85
  return self
84
86
 
85
87
  def kill(self, timeout=1):
86
- # Cannot early exit if process is not running, because it may just be
87
- # starting up.
88
88
  assert self.started
89
89
  try:
90
90
  children = list(psutil.Process(self.pid).children(recursive=True))
@@ -108,9 +108,10 @@ class Process:
108
108
  return 'Process(' + ', '.join(attrs) + ')'
109
109
 
110
110
  @staticmethod
111
- def _wrapper(options, name, fn, args):
111
+ def _wrapper(options, ready, name, fn, args):
112
112
  exitcode = 0
113
113
  try:
114
+ ready.wait()
114
115
  contextlib.setup(**options)
115
116
  fn = cloudpickle.loads(fn)
116
117
  exitcode = fn(*args)
@@ -148,4 +148,7 @@ class Server:
148
148
  data = message.encode('utf-8')
149
149
  self.socket.send(addr, reqnum, status, data)
150
150
  if self.errors:
151
+ # Wait until the error is delivered to the client and then raise.
152
+ self.socket.shutdown()
153
+ self.socket.close()
151
154
  raise RuntimeError(message)
@@ -21,7 +21,6 @@ class Thread:
21
21
  """
22
22
 
23
23
  def __init__(self, fn, *args, name=None, start=False):
24
- global TIDS
25
24
  self.fn = fn
26
25
  self.excode = None
27
26
  name = name or getattr(fn, '__name__', 'thread')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: portal
3
- Version: 3.2.0
3
+ Version: 3.2.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
@@ -16,35 +16,43 @@ class TestProcess:
16
16
  assert worker.exitcode == 42
17
17
 
18
18
  def test_error(self):
19
+
19
20
  def fn():
20
21
  raise KeyError('foo')
22
+
21
23
  worker = portal.Process(fn, start=True)
22
24
  worker.join()
23
25
  assert not worker.running
24
26
  assert worker.exitcode == 1
25
27
 
26
28
  def test_error_with_children(self):
29
+
27
30
  def hang():
28
31
  while True:
29
32
  time.sleep(0.1)
33
+
30
34
  def fn():
31
35
  portal.Process(hang, start=True)
32
36
  portal.Thread(hang, start=True)
33
37
  time.sleep(0.1)
34
38
  raise KeyError('foo')
39
+
35
40
  worker = portal.Process(fn, start=True)
36
41
  worker.join()
37
42
  assert not worker.running
38
43
  assert worker.exitcode == 1
39
44
 
40
- def test_kill(self):
45
+ @pytest.mark.parametrize('repeat', range(5))
46
+ def test_kill_basic(self, repeat):
47
+
41
48
  def fn():
42
49
  while True:
43
50
  time.sleep(0.1)
51
+
44
52
  worker = portal.Process(fn, start=True)
45
53
  worker.kill()
46
54
  assert not worker.running
47
- assert worker.exitcode < 0
55
+ assert abs(worker.exitcode) >= 1
48
56
 
49
57
  @pytest.mark.parametrize('repeat', range(5))
50
58
  def test_kill_with_subproc(self, repeat):
@@ -52,14 +60,18 @@ class TestProcess:
52
60
  queue = portal.context.mp.Queue()
53
61
 
54
62
  def outer(ready, queue):
55
- queue.put(os.getpid())
56
63
  portal.Process(inner, ready, queue, start=True)
64
+ queue.put(os.getpid())
65
+ queue.close()
66
+ queue.join_thread()
57
67
  ready.wait()
58
68
  while True:
59
69
  time.sleep(0.1)
60
70
 
61
71
  def inner(ready, queue):
62
72
  queue.put(os.getpid())
73
+ queue.close()
74
+ queue.join_thread()
63
75
  ready.wait()
64
76
  while True:
65
77
  time.sleep(0.1)
@@ -68,7 +80,7 @@ class TestProcess:
68
80
  ready.wait()
69
81
  worker.kill()
70
82
  assert not worker.running
71
- assert worker.exitcode < 0
83
+ assert abs(worker.exitcode) >= 1
72
84
  assert not portal.proc_alive(queue.get())
73
85
  assert not portal.proc_alive(queue.get())
74
86
 
@@ -87,20 +99,25 @@ class TestProcess:
87
99
  ready.wait()
88
100
  worker.kill()
89
101
  assert not worker.running
90
- assert worker.exitcode < 0
102
+ assert abs(worker.exitcode) >= 1
91
103
 
92
104
  def test_initfn(self):
105
+
93
106
  def init():
94
107
  portal.foo = 42
108
+
95
109
  portal.initfn(init)
96
110
  ready = portal.context.mp.Event()
97
111
  assert portal.foo == 42
112
+
98
113
  def outer(ready):
99
114
  assert portal.foo == 42
100
115
  portal.Process(inner, ready, start=True).join()
116
+
101
117
  def inner(ready):
102
118
  assert portal.foo == 42
103
119
  ready.set()
120
+
104
121
  portal.Process(outer, ready, start=True).join()
105
122
  ready.wait()
106
123
  assert ready.is_set()
@@ -91,6 +91,27 @@ class TestServer:
91
91
  def test_server_errors(self, Server):
92
92
  port = portal.free_port()
93
93
 
94
+ server = Server(port, errors=False)
95
+ def fn(x):
96
+ if x == 2:
97
+ raise ValueError(x)
98
+ return x
99
+ server.bind('fn', fn)
100
+ server.start(block=False)
101
+
102
+ client = portal.Client('localhost', port)
103
+ assert client.fn(1).result() == 1
104
+ with pytest.raises(RuntimeError):
105
+ client.fn(2).result()
106
+ assert client.fn(3).result() == 3
107
+
108
+ client.close()
109
+ server.close()
110
+
111
+ @pytest.mark.parametrize('Server', SERVERS)
112
+ def test_server_errors_raise(self, Server):
113
+ port = portal.free_port()
114
+
94
115
  def server(port):
95
116
  server = Server(port, errors=True)
96
117
  def fn(x):
@@ -108,8 +129,8 @@ class TestServer:
108
129
  client = portal.Client('localhost', port)
109
130
  assert client.fn(1).result() == 1
110
131
  assert server.running
111
- with pytest.raises(RuntimeError):
112
- client.fn(2).result()
132
+ with pytest.raises((RuntimeError, TimeoutError)):
133
+ client.fn(2).result(timeout=3)
113
134
 
114
135
  client.close()
115
136
  server.join()
@@ -91,4 +91,4 @@ class TestThread:
91
91
  assert not worker.running
92
92
  assert not proc[0].running
93
93
  assert worker.exitcode == 2
94
- assert proc[0].exitcode < 0
94
+ assert abs(proc[0].exitcode) >= 1
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