zprocess 2.27.0__tar.gz → 2.27.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 (54) hide show
  1. {zprocess-2.27.0 → zprocess-2.27.1}/.github/workflows/release.yml +11 -11
  2. {zprocess-2.27.0/zprocess.egg-info → zprocess-2.27.1}/PKG-INFO +1 -1
  3. {zprocess-2.27.0 → zprocess-2.27.1}/tests/test_zlock_server.py +110 -0
  4. {zprocess-2.27.0 → zprocess-2.27.1}/tests/test_zprocess.py +90 -13
  5. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/clientserver.py +6 -5
  6. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/process_tree.py +1 -1
  7. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/zlock/__init__.py +3 -4
  8. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/zlock/server.py +1 -1
  9. {zprocess-2.27.0 → zprocess-2.27.1/zprocess.egg-info}/PKG-INFO +1 -1
  10. {zprocess-2.27.0 → zprocess-2.27.1}/.github/workflows/release-vars.sh +0 -0
  11. {zprocess-2.27.0 → zprocess-2.27.1}/.gitignore +0 -0
  12. {zprocess-2.27.0 → zprocess-2.27.1}/LICENSE.txt +0 -0
  13. {zprocess-2.27.0 → zprocess-2.27.1}/README.md +0 -0
  14. {zprocess-2.27.0 → zprocess-2.27.1}/doc/Makefile +0 -0
  15. {zprocess-2.27.0 → zprocess-2.27.1}/doc/conf.py +0 -0
  16. {zprocess-2.27.0 → zprocess-2.27.1}/doc/index.rst +0 -0
  17. {zprocess-2.27.0 → zprocess-2.27.1}/doc/requirements.txt +0 -0
  18. {zprocess-2.27.0 → zprocess-2.27.1}/pyproject.toml +0 -0
  19. {zprocess-2.27.0 → zprocess-2.27.1}/pytest.ini +0 -0
  20. {zprocess-2.27.0 → zprocess-2.27.1}/requirements.txt +0 -0
  21. {zprocess-2.27.0 → zprocess-2.27.1}/run_tests.py +0 -0
  22. {zprocess-2.27.0 → zprocess-2.27.1}/setup.cfg +0 -0
  23. {zprocess-2.27.0 → zprocess-2.27.1}/tests/GUI_output_redirection.py +0 -0
  24. {zprocess-2.27.0 → zprocess-2.27.1}/tests/output_redirection.py +0 -0
  25. {zprocess-2.27.0 → zprocess-2.27.1}/tests/test_remote.py +0 -0
  26. {zprocess-2.27.0 → zprocess-2.27.1}/tests/test_zlog_server.py +0 -0
  27. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/__init__.py +0 -0
  28. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/__version__.py +0 -0
  29. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/cryptoproxy.py +0 -0
  30. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/examples/example_child.py +0 -0
  31. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/examples/example_parent.py +0 -0
  32. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/examples/processclass_example.py +0 -0
  33. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/examples/processclass_remote_example.py +0 -0
  34. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/locking/__init__.py +0 -0
  35. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/locking/__main__.py +0 -0
  36. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/makesecret.py +0 -0
  37. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/process_class_wrapper.py +0 -0
  38. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/remote/__init__.py +0 -0
  39. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/remote/__main__.py +0 -0
  40. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/remote/curses_server.py +0 -0
  41. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/remote/server.py +0 -0
  42. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/security.py +0 -0
  43. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/socks/__init__.py +0 -0
  44. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/socks/server.py +0 -0
  45. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/tasks.py +0 -0
  46. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/utils.py +0 -0
  47. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/zlock/__main__.py +0 -0
  48. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/zlog/__init__.py +0 -0
  49. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/zlog/__main__.py +0 -0
  50. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess/zlog/server.py +0 -0
  51. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess.egg-info/SOURCES.txt +0 -0
  52. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess.egg-info/dependency_links.txt +0 -0
  53. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess.egg-info/requires.txt +0 -0
  54. {zprocess-2.27.0 → zprocess-2.27.1}/zprocess.egg-info/top_level.txt +0 -0
@@ -34,7 +34,7 @@ jobs:
34
34
 
35
35
  steps:
36
36
  - name: Checkout
37
- uses: actions/checkout@v4
37
+ uses: actions/checkout@v6
38
38
  with:
39
39
  fetch-depth: 0
40
40
 
@@ -43,7 +43,7 @@ jobs:
43
43
  run: git tag -d $(git tag --points-at HEAD)
44
44
 
45
45
  - name: Install Python
46
- uses: actions/setup-python@v5
46
+ uses: actions/setup-python@v6
47
47
  with:
48
48
  python-version: '3.x'
49
49
 
@@ -222,7 +222,7 @@ jobs:
222
222
 
223
223
  steps:
224
224
  - name: Checkout
225
- uses: actions/checkout@v4
225
+ uses: actions/checkout@v6
226
226
  with:
227
227
  fetch-depth: 0
228
228
 
@@ -231,7 +231,7 @@ jobs:
231
231
  run: git tag -d $(git tag --points-at HEAD)
232
232
 
233
233
  - name: Install Python
234
- uses: actions/setup-python@v5
234
+ uses: actions/setup-python@v6
235
235
  with:
236
236
  python-version: ${{ env.DEFAULT_PYTHON }}
237
237
 
@@ -252,7 +252,7 @@ jobs:
252
252
 
253
253
  - name: Upload artifact
254
254
  if: env.PURE == 'false' || strategy.job-index == 0
255
- uses: actions/upload-artifact@v4
255
+ uses: actions/upload-artifact@v6
256
256
  with:
257
257
  name: dist-${{ matrix.os }}
258
258
  path: ./dist
@@ -278,7 +278,7 @@ jobs:
278
278
  setuptools-conda build $CONDA_BUILD_ARGS .
279
279
 
280
280
  - name: Upload artifact
281
- uses: actions/upload-artifact@v4
281
+ uses: actions/upload-artifact@v6
282
282
  with:
283
283
  name: conda_packages-${{ matrix.os }}
284
284
  path: ./conda_packages
@@ -298,7 +298,7 @@ jobs:
298
298
 
299
299
  steps:
300
300
  - name: Download Artifact
301
- uses: actions/download-artifact@v4
301
+ uses: actions/download-artifact@v8
302
302
  with:
303
303
  pattern: dist*
304
304
  path: ./dist
@@ -332,7 +332,7 @@ jobs:
332
332
 
333
333
  steps:
334
334
  - name: Download Artifact
335
- uses: actions/download-artifact@v4
335
+ uses: actions/download-artifact@v8
336
336
  with:
337
337
  pattern: dist*
338
338
  path: ./dist
@@ -362,7 +362,7 @@ jobs:
362
362
 
363
363
  steps:
364
364
  - name: Download Artifact
365
- uses: actions/download-artifact@v4
365
+ uses: actions/download-artifact@v8
366
366
  with:
367
367
  pattern: dist*
368
368
  path: ./dist
@@ -383,7 +383,7 @@ jobs:
383
383
 
384
384
  steps:
385
385
  - name: Download Artifact
386
- uses: actions/download-artifact@v4
386
+ uses: actions/download-artifact@v8
387
387
  with:
388
388
  pattern: conda_packages-*
389
389
  path: ./conda_packages
@@ -422,7 +422,7 @@ jobs:
422
422
 
423
423
  steps:
424
424
  - name: Download Artifact
425
- uses: actions/download-artifact@v4
425
+ uses: actions/download-artifact@v8
426
426
  with:
427
427
  pattern: conda_packages-*
428
428
  path: ./conda_packages
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zprocess
3
- Version: 2.27.0
3
+ Version: 2.27.1
4
4
  Summary: A set of utilities for multiprocessing using zeromq
5
5
  Author-email: Christopher Billington <chrisjbillington@gmail.com>
6
6
  License-Expression: BSD-2-Clause
@@ -1,6 +1,7 @@
1
1
  import sys
2
2
  from pathlib import Path
3
3
  import time
4
+ import threading
4
5
  import zmq
5
6
  import unittest
6
7
 
@@ -28,6 +29,8 @@ from zprocess.zlock.server import (
28
29
  ERR_TIMEOUT_INVALID,
29
30
  ERR_READ_ONLY_WRONG,
30
31
  )
32
+ import zprocess.zlock as zlock
33
+ from zprocess.zlock import ZLockClient
31
34
  import zprocess.zlock.server
32
35
 
33
36
 
@@ -679,8 +682,115 @@ class ImplementationUnitTests(unittest.TestCase):
679
682
  with self.assertRaises(NotWaiting):
680
683
  lock.give_up(b'client_foo')
681
684
 
685
+ def test_zlock_client_uses_instance_default_timeout(self):
686
+ class FakeSocket(object):
687
+ def __init__(self):
688
+ self.messages = []
689
+
690
+ def send_multipart(self, message, flags=0):
691
+ self.messages.append(message)
692
+
693
+ def recv(self):
694
+ return b'ok'
695
+
696
+ def close(self, linger=False):
697
+ pass
698
+
699
+ class FakePoller(object):
700
+ def poll(self, timeout):
701
+ return [object()]
702
+
703
+ client = ZLockClient(default_timeout=12.5)
704
+ client.local = threading.local()
705
+
706
+ def fake_new_socket(timeout=None):
707
+ client.local.sock = FakeSocket()
708
+ client.local.poller = FakePoller()
709
+ client.local.client_id = b'client'
710
+
711
+ client._new_socket = fake_new_socket
712
+ client.acquire('key_foo')
713
+
714
+ self.assertEqual(client.local.sock.messages[-1][3], b'12.5')
715
+
716
+ def test_set_client_thread_name_requires_default_client(self):
717
+ with monkeypatch(zlock, '_default_zlock_client', None):
718
+ with self.assertRaisesRegex(RuntimeError, 'ZLockClient'):
719
+ zlock.set_client_thread_name('worker')
720
+
721
+ def test_set_client_thread_name_delegates_to_default_client(self):
722
+ client = ZLockClient()
723
+ with monkeypatch(zlock, '_default_zlock_client', client):
724
+ zlock.set_client_thread_name('worker')
725
+
726
+ self.assertEqual(client.thread_name.name, 'worker')
727
+ self.assertIn(b'worker-', client.local.client_id)
728
+
729
+ def test_set_client_process_name_avoids_double_separator(self):
730
+ client = ZLockClient()
731
+ with monkeypatch(zlock, '_default_zlock_client', client):
732
+ zlock.set_client_process_name('worker')
733
+
734
+ process_identifier = client._make_client_id().split(':')[1]
735
+ self.assertTrue(process_identifier.startswith('worker-'))
736
+ self.assertNotIn('worker--', process_identifier)
737
+
682
738
 
683
739
  class OtherTests(unittest.TestCase):
740
+ def test_default_bind_address(self):
741
+ class FakeSocket(object):
742
+ def __init__(self):
743
+ self.logger = None
744
+
745
+ def bind_to_random_port(self, addr):
746
+ self.bound_addr = addr
747
+ return 7339
748
+
749
+ def bind(self, endpoint):
750
+ self.endpoint = endpoint
751
+
752
+ def close(self):
753
+ pass
754
+
755
+ class FakeContext(object):
756
+ def __init__(self):
757
+ self.sockets = []
758
+
759
+ def socket(self, *args, **kwargs):
760
+ sock = FakeSocket()
761
+ self.sockets.append(sock)
762
+ return sock
763
+
764
+ class FakePoller(object):
765
+ def register(self, *args, **kwargs):
766
+ pass
767
+
768
+ class FakeAllowInterrupt(object):
769
+ def __init__(self, handler):
770
+ self.handler = handler
771
+
772
+ def __enter__(self):
773
+ return self
774
+
775
+ def __exit__(self, *args):
776
+ return False
777
+
778
+ class FakeLogger(object):
779
+ def info(self, *args, **kwargs):
780
+ pass
781
+
782
+ fake_context = FakeContext()
783
+ server = ZMQLockServer(silent=True)
784
+
785
+ with monkeypatch(zprocess.zlock.server.SecureContext, 'instance', lambda *args, **kwargs: fake_context):
786
+ with monkeypatch(zprocess.zlock.server.zmq, 'Poller', FakePoller):
787
+ with monkeypatch(zprocess.zlock.server, 'allow_interrupt', FakeAllowInterrupt):
788
+ with monkeypatch(zprocess.zlock.server, 'setup_logging', lambda *args, **kwargs: FakeLogger()):
789
+ with monkeypatch(ZMQLockServer, '_mainloop', lambda self: None):
790
+ server.run()
791
+
792
+ self.assertEqual(fake_context.sockets[0].bound_addr, 'tcp://*')
793
+
684
794
  def test_bind_to_port(self):
685
795
  # Run the server on a random port on localhost:
686
796
  import random
@@ -1,9 +1,11 @@
1
1
  import unittest
2
2
  from pathlib import Path
3
- import sys
4
- import os
5
- import time
6
- import threading
3
+ import sys
4
+ import os
5
+ import time
6
+ import threading
7
+ import subprocess
8
+ from unittest.mock import patch
7
9
 
8
10
  import zmq
9
11
  import pytest
@@ -144,7 +146,7 @@ class TestProcess(Process):
144
146
 
145
147
 
146
148
 
147
- class ProcessClassTests(unittest.TestCase):
149
+ class ProcessClassTests(unittest.TestCase):
148
150
 
149
151
  def setUp(self):
150
152
  """Create a subprocess with output redirection to a zmq port"""
@@ -201,9 +203,26 @@ class ProcessClassTests(unittest.TestCase):
201
203
  # And no more...
202
204
  self.assertEqual(self.redirection_sock.poll(100), 0)
203
205
 
204
- def tearDown(self):
205
- self.process.terminate()
206
- self.redirection_sock.close()
206
+ def tearDown(self):
207
+ self.process.terminate()
208
+ self.redirection_sock.close()
209
+
210
+
211
+ class ProcessTerminateTests(unittest.TestCase):
212
+ def test_terminate_ignores_local_wait_timeout(self):
213
+ class DummyChild(object):
214
+ def terminate(self, **kwargs):
215
+ pass
216
+
217
+ def wait(self, timeout=None, **kwargs):
218
+ raise subprocess.TimeoutExpired(cmd='dummy', timeout=timeout)
219
+
220
+ process = Process.__new__(Process)
221
+ process.child = DummyChild()
222
+ process.interrupt_startup = lambda reason=None: None
223
+
224
+ # Should not leak subprocess.TimeoutExpired for a local child wait timeout:
225
+ process.terminate(wait_timeout=0.1)
207
226
 
208
227
 
209
228
  class HeartbeatClientTestProcess(Process):
@@ -389,11 +408,69 @@ class TaskTests(unittest.TestCase):
389
408
  self.assertEqual(queue, [task3])
390
409
 
391
410
 
392
- class ClientServerTests(unittest.TestCase):
393
-
394
- def test_rep_server(self):
395
- class MyServer(ZMQServer):
396
- def handler(self, data):
411
+ class ClientServerTests(unittest.TestCase):
412
+
413
+ def test_invalid_dtype_server_raises_valueerror(self):
414
+ class InvalidServer(clientserver._ZMQServer):
415
+ def setup_auth(self, context):
416
+ return None
417
+
418
+ class FakeSocket(object):
419
+ def setsockopt(self, *args, **kwargs):
420
+ pass
421
+
422
+ def bind(self, *args, **kwargs):
423
+ pass
424
+
425
+ def close(self, *args, **kwargs):
426
+ pass
427
+
428
+ class FakeContext(object):
429
+ def socket(self, *args, **kwargs):
430
+ return FakeSocket()
431
+
432
+ class FakePoller(object):
433
+ def register(self, *args, **kwargs):
434
+ pass
435
+
436
+ with patch.object(clientserver.zmq, 'Context', FakeContext):
437
+ with patch.object(clientserver.zmq, 'Poller', FakePoller):
438
+ with self.assertRaisesRegex(ValueError, "invalid dtype invalid"):
439
+ InvalidServer(
440
+ port=1, dtype='invalid', bind_address='tcp://127.0.0.1'
441
+ )
442
+
443
+ def test_invalid_dtype_sender_raises_valueerror(self):
444
+ class FakeSocket(object):
445
+ def setsockopt(self, *args, **kwargs):
446
+ pass
447
+
448
+ def connect(self, *args, **kwargs):
449
+ pass
450
+
451
+ def close(self, *args, **kwargs):
452
+ pass
453
+
454
+ class FakeContext(object):
455
+ def socket(self, *args, **kwargs):
456
+ return FakeSocket()
457
+
458
+ class FakePoller(object):
459
+ def register(self, *args, **kwargs):
460
+ pass
461
+
462
+ sender = clientserver._Sender(
463
+ dtype='invalid', interruptor=clientserver.Interruptor()
464
+ )
465
+
466
+ with patch.object(clientserver.SecureContext, 'instance', return_value=FakeContext()):
467
+ with patch.object(clientserver.zmq, 'Poller', FakePoller):
468
+ with self.assertRaisesRegex(ValueError, "invalid dtype invalid"):
469
+ sender.new_socket('localhost', 1)
470
+
471
+ def test_rep_server(self):
472
+ class MyServer(ZMQServer):
473
+ def handler(self, data):
397
474
  if data == 'error':
398
475
  raise TestError
399
476
  return data
@@ -129,8 +129,10 @@ class ZMQServer(object):
129
129
  protocol=zprocess.PICKLE_PROTOCOL)
130
130
  self.recv = self.sock.recv_pyobj
131
131
  else:
132
- msg = ("invalid dtype %s, must be 'raw', 'string', " +
133
- "'multipart' or 'pyobj'" % str(self.dtype))
132
+ msg = (
133
+ "invalid dtype %s, must be 'raw', 'string', "
134
+ "'multipart' or 'pyobj'"
135
+ ) % str(self.dtype)
134
136
  raise ValueError(msg)
135
137
 
136
138
  self.mainloop_thread = threading.Thread(target=self.mainloop)
@@ -318,8 +320,8 @@ class _Sender(object):
318
320
  else:
319
321
  msg = (
320
322
  "invalid dtype %s, must be 'raw', 'string', "
321
- + "'multipart' or 'pyobj'" % str(self.dtype)
322
- )
323
+ "'multipart' or 'pyobj'"
324
+ ) % str(self.dtype)
323
325
  raise ValueError(msg)
324
326
  except:
325
327
  # Didn't work, don't keep it:
@@ -474,4 +476,3 @@ zmq_push_raw = _default_client.push_raw
474
476
  __all__ = ['ZMQServer', 'ZMQClient',
475
477
  'zmq_get', 'zmq_get_multipart', 'zmq_get_string', 'zmq_get_raw',
476
478
  'zmq_push', 'zmq_push_multipart', 'zmq_push_string', 'zmq_push_raw']
477
-
@@ -1129,7 +1129,7 @@ class Process(object):
1129
1129
  try:
1130
1130
  self.child.terminate(**kwargs)
1131
1131
  self.child.wait(timeout=wait_timeout, **kwargs)
1132
- except (OSError, TimeoutError):
1132
+ except (OSError, TimeoutError, subprocess.TimeoutExpired):
1133
1133
  # process is already dead, or cannot contact remote server
1134
1134
  pass
1135
1135
 
@@ -121,7 +121,7 @@ class ZLockClient(object):
121
121
 
122
122
  def acquire(self, key, timeout=None, read_only=False):
123
123
  if timeout is None:
124
- timeout = DEFAULT_TIMEOUT
124
+ timeout = self.default_timeout
125
125
  timeout = str(timeout).encode('utf8')
126
126
  if not hasattr(self.local, 'sock'):
127
127
  self._new_socket()
@@ -289,7 +289,6 @@ def set_default_timeout(timeout):
289
289
 
290
290
  def set_client_process_name(name):
291
291
  """Deprecated. Instantiate a ZLockClient and call its method instead"""
292
- name += '-'
293
292
  global _process_name
294
293
  if _default_zlock_client is not None:
295
294
  _default_zlock_client.set_process_name(name)
@@ -299,8 +298,8 @@ def set_client_process_name(name):
299
298
 
300
299
  def set_client_thread_name(name):
301
300
  """Deprecated. Instantiate a ZLockClient and call its method instead"""
302
- if _default_zlock_client is not None:
303
- raise RuntimeError("Can't set thread name before instantiating ZLogClient")
301
+ if _default_zlock_client is None:
302
+ raise RuntimeError("Can't set thread name before instantiating ZLockClient")
304
303
  _default_zlock_client.set_thread_name(name)
305
304
 
306
305
 
@@ -421,7 +421,7 @@ class ZMQLockServer(object):
421
421
  def __init__(
422
422
  self,
423
423
  port=None,
424
- bind_address='tcp:/*',
424
+ bind_address='tcp://*',
425
425
  silent=False,
426
426
  shared_secret=None,
427
427
  allow_insecure=True,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zprocess
3
- Version: 2.27.0
3
+ Version: 2.27.1
4
4
  Summary: A set of utilities for multiprocessing using zeromq
5
5
  Author-email: Christopher Billington <chrisjbillington@gmail.com>
6
6
  License-Expression: BSD-2-Clause
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