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.
- {portal-3.2.0/portal.egg-info → portal-3.2.1}/PKG-INFO +1 -1
- {portal-3.2.0 → portal-3.2.1}/portal/__init__.py +1 -1
- {portal-3.2.0 → portal-3.2.1}/portal/process.py +6 -5
- {portal-3.2.0 → portal-3.2.1}/portal/server.py +3 -0
- {portal-3.2.0 → portal-3.2.1}/portal/thread.py +0 -1
- {portal-3.2.0 → portal-3.2.1/portal.egg-info}/PKG-INFO +1 -1
- {portal-3.2.0 → portal-3.2.1}/tests/test_process.py +22 -5
- {portal-3.2.0 → portal-3.2.1}/tests/test_server.py +23 -2
- {portal-3.2.0 → portal-3.2.1}/tests/test_thread.py +1 -1
- {portal-3.2.0 → portal-3.2.1}/LICENSE +0 -0
- {portal-3.2.0 → portal-3.2.1}/MANIFEST.in +0 -0
- {portal-3.2.0 → portal-3.2.1}/README.md +0 -0
- {portal-3.2.0 → portal-3.2.1}/portal/batching.py +0 -0
- {portal-3.2.0 → portal-3.2.1}/portal/buffers.py +0 -0
- {portal-3.2.0 → portal-3.2.1}/portal/client.py +0 -0
- {portal-3.2.0 → portal-3.2.1}/portal/client_socket.py +0 -0
- {portal-3.2.0 → portal-3.2.1}/portal/contextlib.py +0 -0
- {portal-3.2.0 → portal-3.2.1}/portal/packlib.py +0 -0
- {portal-3.2.0 → portal-3.2.1}/portal/poollib.py +0 -0
- {portal-3.2.0 → portal-3.2.1}/portal/server_socket.py +0 -0
- {portal-3.2.0 → portal-3.2.1}/portal/sharray.py +0 -0
- {portal-3.2.0 → portal-3.2.1}/portal/utils.py +0 -0
- {portal-3.2.0 → portal-3.2.1}/portal.egg-info/SOURCES.txt +0 -0
- {portal-3.2.0 → portal-3.2.1}/portal.egg-info/dependency_links.txt +0 -0
- {portal-3.2.0 → portal-3.2.1}/portal.egg-info/requires.txt +0 -0
- {portal-3.2.0 → portal-3.2.1}/portal.egg-info/top_level.txt +0 -0
- {portal-3.2.0 → portal-3.2.1}/pyproject.toml +0 -0
- {portal-3.2.0 → portal-3.2.1}/requirements.txt +0 -0
- {portal-3.2.0 → portal-3.2.1}/setup.cfg +0 -0
- {portal-3.2.0 → portal-3.2.1}/setup.py +0 -0
- {portal-3.2.0 → portal-3.2.1}/tests/test_batching.py +0 -0
- {portal-3.2.0 → portal-3.2.1}/tests/test_client.py +0 -0
- {portal-3.2.0 → portal-3.2.1}/tests/test_errfile.py +0 -0
- {portal-3.2.0 → portal-3.2.1}/tests/test_pack.py +0 -0
- {portal-3.2.0 → portal-3.2.1}/tests/test_socket.py +0 -0
@@ -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,
|
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)
|
@@ -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
|
-
|
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
|
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
|
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
|
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()
|
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
|