parsl 2024.12.23__py3-none-any.whl → 2025.1.6__py3-none-any.whl

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.
parsl/jobs/strategy.py CHANGED
@@ -298,8 +298,8 @@ class Strategy:
298
298
  # Scale in for htex
299
299
  if isinstance(executor, HighThroughputExecutor):
300
300
  if active_blocks > min_blocks:
301
- excess_slots = math.ceil(active_slots - (active_tasks * parallelism))
302
- excess_blocks = math.ceil(float(excess_slots) / (tasks_per_node * nodes_per_block))
301
+ excess_slots = math.floor(active_slots - (active_tasks * parallelism))
302
+ excess_blocks = math.floor(float(excess_slots) / (tasks_per_node * nodes_per_block))
303
303
  excess_blocks = min(excess_blocks, active_blocks - min_blocks)
304
304
  logger.debug(f"Requesting scaling in by {excess_blocks} blocks with idle time {self.max_idletime}s")
305
305
  executor.scale_in_facade(excess_blocks, max_idletime=self.max_idletime)
@@ -78,7 +78,7 @@ class Database:
78
78
 
79
79
  def _get_mapper(self, table_obj: Table) -> Mapper:
80
80
  all_mappers: Set[Mapper] = set()
81
- for mapper_registry in mapperlib._all_registries(): # type: ignore[attr-defined]
81
+ for mapper_registry in mapperlib._all_registries():
82
82
  all_mappers.update(mapper_registry.mappers)
83
83
  mapper_gen = (
84
84
  mapper for mapper in all_mappers
@@ -67,7 +67,7 @@ def test_app_name(get_app, expected_name, expected_result, tmpd_cwd):
67
67
  with engine.begin() as connection:
68
68
 
69
69
  def count_rows(table: str):
70
- result = connection.execute(f"SELECT COUNT(*) FROM {table}")
70
+ result = connection.execute(sqlalchemy.text(f"SELECT COUNT(*) FROM {table}"))
71
71
  (c, ) = result.first()
72
72
  return c
73
73
 
@@ -81,6 +81,6 @@ def test_app_name(get_app, expected_name, expected_result, tmpd_cwd):
81
81
  assert count_rows("try") == 1
82
82
 
83
83
  # ... and has the expected name.
84
- result = connection.execute("SELECT task_func_name FROM task")
84
+ result = connection.execute(sqlalchemy.text("SELECT task_func_name FROM task"))
85
85
  (c, ) = result.first()
86
86
  assert c == expected_name
@@ -106,7 +106,7 @@ def test_stdstream_to_monitoring(stdx, expected_stdx, stream, tmpd_cwd, caplog):
106
106
  with engine.begin() as connection:
107
107
 
108
108
  def count_rows(table: str):
109
- result = connection.execute(f"SELECT COUNT(*) FROM {table}")
109
+ result = connection.execute(sqlalchemy.text(f"SELECT COUNT(*) FROM {table}"))
110
110
  (c, ) = result.first()
111
111
  return c
112
112
 
@@ -120,7 +120,7 @@ def test_stdstream_to_monitoring(stdx, expected_stdx, stream, tmpd_cwd, caplog):
120
120
  assert count_rows("try") == 1
121
121
 
122
122
  # ... and has the expected name.
123
- result = connection.execute(f"SELECT task_{stream} FROM task")
123
+ result = connection.execute(sqlalchemy.text(f"SELECT task_{stream} FROM task"))
124
124
  (c, ) = result.first()
125
125
 
126
126
  if isinstance(expected_stdx, str):
@@ -0,0 +1,103 @@
1
+ import math
2
+ from unittest.mock import MagicMock
3
+
4
+ import pytest
5
+
6
+ from parsl.executors.high_throughput.executor import HighThroughputExecutor
7
+ from parsl.jobs.states import JobState, JobStatus
8
+ from parsl.jobs.strategy import Strategy
9
+
10
+
11
+ # the parameterize tuple consists of:
12
+ # Input:
13
+ # * number of tasks to mock the load as
14
+ # * number of workers per node
15
+ # Expected output:
16
+ # * the number of blocks we should expect to be launched
17
+ # in this situation
18
+ #
19
+ # This test will configure an executor, then run strategize
20
+ # a few times, asserting that it converges to the correct
21
+ # number of blocks without oscillating.
22
+ @pytest.mark.local
23
+ @pytest.mark.parametrize("ns", [(14, 48, 1), # values from issue #3696
24
+
25
+ (1, 1, 1), # one task needs one block
26
+
27
+ (100, 1, 20), # many one-task blocks, hitting hard-coded max blocks
28
+
29
+ (47, 48, 1), # some edge cases around #3696 values
30
+ (48, 48, 1), # "
31
+ (49, 48, 2), # "
32
+ (149, 50, 3)]) # "
33
+ def test_htex_strategy_does_not_oscillate(ns):
34
+ """Check for oscillations in htex scaling.
35
+ In issue 3696, with a large number of workers per block
36
+ and a smaller number of active tasks, the htex scaling
37
+ strategy oscillates between 0 and 1 active block, rather
38
+ than converging to 1 active block.
39
+ """
40
+
41
+ n_tasks, n_workers, n_blocks = ns
42
+
43
+ s = Strategy(strategy='htex_auto_scale', max_idletime=0)
44
+
45
+ provider = MagicMock()
46
+ executor = MagicMock(spec=HighThroughputExecutor)
47
+
48
+ statuses = {}
49
+
50
+ executor.provider = provider
51
+ executor.outstanding = n_tasks
52
+ executor.status_facade = statuses
53
+ executor.workers_per_node = n_workers
54
+
55
+ provider.parallelism = 1
56
+ provider.init_blocks = 0
57
+ provider.min_blocks = 0
58
+ provider.max_blocks = 20
59
+ provider.nodes_per_block = 1
60
+
61
+ def scale_out(n):
62
+ for _ in range(n):
63
+ statuses[len(statuses)] = JobStatus(state=JobState.PENDING)
64
+
65
+ executor.scale_out_facade.side_effect = scale_out
66
+
67
+ def scale_in(n, max_idletime=None):
68
+ # find n PENDING jobs and set them to CANCELLED
69
+ for k in statuses:
70
+ if n == 0:
71
+ return
72
+ if statuses[k].state == JobState.PENDING:
73
+ statuses[k].state = JobState.CANCELLED
74
+ n -= 1
75
+
76
+ executor.scale_in_facade.side_effect = scale_in
77
+
78
+ s.add_executors([executor])
79
+
80
+ # In issue #3696, this first strategise does initial and load based
81
+ # scale outs, because n_tasks > n_workers*0
82
+ s.strategize([executor])
83
+
84
+ executor.scale_out_facade.assert_called()
85
+ assert len(statuses) == n_blocks, "Should have launched n_blocks"
86
+ assert len([k for k in statuses if statuses[k].state == JobState.PENDING]) == n_blocks
87
+ # there might be several calls to scale_out_facade inside strategy,
88
+ # but the end effect should be that exactly one block is scaled out.
89
+
90
+ executor.scale_in_facade.assert_not_called()
91
+
92
+ # In issue #3696, this second strategize does a scale in, because n_tasks < n_workers*1
93
+ s.strategize([executor])
94
+
95
+ # assert that there should still be n_blocks pending blocks
96
+ assert len([k for k in statuses if statuses[k].state == JobState.PENDING]) == n_blocks
97
+ # this assert fails due to issue #3696
98
+
99
+ # Now check scale in happens with 0 load
100
+ executor.outstanding = 0
101
+ s.strategize([executor])
102
+ executor.scale_in_facade.assert_called()
103
+ assert len([k for k in statuses if statuses[k].state == JobState.PENDING]) == 0
parsl/version.py CHANGED
@@ -3,4 +3,4 @@
3
3
  Year.Month.Day[alpha/beta/..]
4
4
  Alphas will be numbered like this -> 2024.12.10a0
5
5
  """
6
- VERSION = '2024.12.23'
6
+ VERSION = '2025.01.06'
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: parsl
3
- Version: 2024.12.23
3
+ Version: 2025.1.6
4
4
  Summary: Simple data dependent workflows in Python
5
5
  Home-page: https://github.com/Parsl/parsl
6
- Download-URL: https://github.com/Parsl/parsl/archive/2024.12.23.tar.gz
6
+ Download-URL: https://github.com/Parsl/parsl/archive/2025.01.06.tar.gz
7
7
  Author: The Parsl Team
8
8
  Author-email: parsl@googlegroups.com
9
9
  License: Apache 2.0
@@ -28,9 +28,9 @@ Requires-Dist: psutil>=5.5.1
28
28
  Requires-Dist: setproctitle
29
29
  Requires-Dist: filelock<4,>=3.13
30
30
  Provides-Extra: all
31
- Requires-Dist: sqlalchemy<2,>=1.4; extra == "all"
32
- Requires-Dist: pydot; extra == "all"
33
- Requires-Dist: networkx<2.6,>=2.5; extra == "all"
31
+ Requires-Dist: sqlalchemy<2.1,>=2; extra == "all"
32
+ Requires-Dist: pydot>=1.4.2; extra == "all"
33
+ Requires-Dist: networkx<3.3,>=3.2; extra == "all"
34
34
  Requires-Dist: Flask>=1.0.2; extra == "all"
35
35
  Requires-Dist: flask-sqlalchemy; extra == "all"
36
36
  Requires-Dist: pandas<2.2; extra == "all"
@@ -76,15 +76,15 @@ Requires-Dist: python-gssapi; extra == "gssapi"
76
76
  Provides-Extra: kubernetes
77
77
  Requires-Dist: kubernetes; extra == "kubernetes"
78
78
  Provides-Extra: monitoring
79
- Requires-Dist: sqlalchemy<2,>=1.4; extra == "monitoring"
79
+ Requires-Dist: sqlalchemy<2.1,>=2; extra == "monitoring"
80
80
  Provides-Extra: proxystore
81
81
  Requires-Dist: proxystore; extra == "proxystore"
82
82
  Provides-Extra: radical-pilot
83
83
  Requires-Dist: radical.pilot==1.90; extra == "radical-pilot"
84
84
  Requires-Dist: radical.utils==1.90; extra == "radical-pilot"
85
85
  Provides-Extra: visualization
86
- Requires-Dist: pydot; extra == "visualization"
87
- Requires-Dist: networkx<2.6,>=2.5; extra == "visualization"
86
+ Requires-Dist: pydot>=1.4.2; extra == "visualization"
87
+ Requires-Dist: networkx<3.3,>=3.2; extra == "visualization"
88
88
  Requires-Dist: Flask>=1.0.2; extra == "visualization"
89
89
  Requires-Dist: flask-sqlalchemy; extra == "visualization"
90
90
  Requires-Dist: pandas<2.2; extra == "visualization"
@@ -8,7 +8,7 @@ parsl/multiprocessing.py,sha256=MyaEcEq-Qf860u7V98u-PZrPNdtzOZL_NW6EhIJnmfQ,1937
8
8
  parsl/process_loggers.py,sha256=uQ7Gd0W72Jz7rrcYlOMfLsAEhkRltxXJL2MgdduJjEw,1136
9
9
  parsl/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  parsl/utils.py,sha256=5FvHIMao3Ik0Rm2p2ieL1KQcQcYXc5K83Jrx5csi-B4,14301
11
- parsl/version.py,sha256=6l0WNaP0JU1FwTVCPx69g0nu1kKQFf-XcxlNgQVkHuo,131
11
+ parsl/version.py,sha256=lIhZrp6NnBCcbBMgntHRdPaNhpDBWe88rZRBa8C0pSw,131
12
12
  parsl/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  parsl/app/app.py,sha256=0gbM4AH2OtFOLsv07I5nglpElcwMSOi-FzdZZfrk7So,8532
14
14
  parsl/app/bash.py,sha256=jm2AvePlCT9DZR7H_4ANDWxatp5dN_22FUlT_gWhZ-g,5528
@@ -105,13 +105,13 @@ parsl/jobs/error_handlers.py,sha256=BBXwUAMJpBm0HxV1P-I6jv7ZF9wcrhnCfzSTlsd2g4w,
105
105
  parsl/jobs/errors.py,sha256=cpSQXCrlKtuHsQf7usjF-lX8XsDkFnE5kWpmFjiN6OU,178
106
106
  parsl/jobs/job_status_poller.py,sha256=b37JOqDpSesqeSreEh1HzfVTFnD5Aoy6k8JDXkkPDmk,2192
107
107
  parsl/jobs/states.py,sha256=dUM8gC4YVpUjLMARJJ_tDERs6oHsoNheAtG6JWPIJt4,5058
108
- parsl/jobs/strategy.py,sha256=KYcIpjWVKLYbM0TXhue9Zp2a7I1zkbHx4raPFiDlewA,13799
108
+ parsl/jobs/strategy.py,sha256=wQk5q-K0adh1oZFI9Mei2iASLRQ8khup1ebiZD-4xfQ,13801
109
109
  parsl/launchers/__init__.py,sha256=jJeDOWGKJjvpmWTLsj1zSqce_UAhWRc_IO-TzaOAlII,579
110
110
  parsl/launchers/base.py,sha256=CblcvPTJiu-MNLWaRtFe29SZQ0BpTOlaY8CGcHdlHIE,538
111
111
  parsl/launchers/errors.py,sha256=8YMV_CHpBNVa4eXkGE4x5DaFQlZkDCRCHmBktYcY6TA,467
112
112
  parsl/launchers/launchers.py,sha256=cQsNsHuCOL_nQTjPXf0--YsgsDoMoJ77bO1Wt4ncLjs,15134
113
113
  parsl/monitoring/__init__.py,sha256=0ywNz6i0lM1xo_7_BIxhETDGeVd2C_0wwD7qgeaMR4c,83
114
- parsl/monitoring/db_manager.py,sha256=D8lrngFGxbFhyWVkF8JZRTbGxRYmd3SY6_zu8KV0FJs,33330
114
+ parsl/monitoring/db_manager.py,sha256=ra5PqmbUstfDx0o_bkBYI8GIUi461-GV3b4A-Q6DVVE,33300
115
115
  parsl/monitoring/errors.py,sha256=D6jpYzEzp0d6FmVKGqhvjAxr4ztZfJX2s-aXemH9bBU,148
116
116
  parsl/monitoring/message_type.py,sha256=Khn88afNxcOIciKiCK4GLnn90I5BlRTiOL3zK-P07yQ,401
117
117
  parsl/monitoring/monitoring.py,sha256=fkBZU4fWp7qBQUcKYtWjd4d-SsFlJUZNMZacFOh0IoA,12687
@@ -330,14 +330,14 @@ parsl/tests/test_htex/test_resource_spec_validation.py,sha256=VzOk4rjMNiDcEVLb-3
330
330
  parsl/tests/test_htex/test_worker_failure.py,sha256=Uz-RHI-LK78FMjXUvrUFmo4iYfmpDVBUcBxxRb3UG9M,603
331
331
  parsl/tests/test_htex/test_zmq_binding.py,sha256=WNFsCKKfid2uEfem0WLgl1wnBncIabpAv6kmg3imBxk,4001
332
332
  parsl/tests/test_monitoring/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
333
- parsl/tests/test_monitoring/test_app_names.py,sha256=ayyxySGWpKSe9dDw2UeJo1dicxjpALRuLsJfprZV4Eg,2174
333
+ parsl/tests/test_monitoring/test_app_names.py,sha256=A-mOMCVhZDnUyJp32fsTUkHdcyval8o7WPEWacDkbD4,2208
334
334
  parsl/tests/test_monitoring/test_basic.py,sha256=VdF6JHfqsEOIMg-ysIAREgygZIjHWNDVLNVQ7jhWxmQ,4592
335
335
  parsl/tests/test_monitoring/test_db_locks.py,sha256=3s3c1xhKo230ZZIJ3f1Ca4U7LcEdXnanOGVXQyNlk2U,2895
336
336
  parsl/tests/test_monitoring/test_fuzz_zmq.py,sha256=--3-pQUvXXbkr8v_BEJoPvVvNly1oXvrD2nJh6yl_0M,3436
337
337
  parsl/tests/test_monitoring/test_htex_init_blocks_vs_monitoring.py,sha256=_QV8zjBKVF_qBbBnhT0C3X9AmfS7IKLcOnEw_cU6HeM,2622
338
338
  parsl/tests/test_monitoring/test_incomplete_futures.py,sha256=ZnO1sFSwlWUBHX64C_zwfTVRVC_UFNlU4h0POgx6NEo,2005
339
339
  parsl/tests/test_monitoring/test_memoization_representation.py,sha256=dknv2nO7pNZ1jGxWGsC_AW3rs90gjMIeC5d7pIJ75Xc,2645
340
- parsl/tests/test_monitoring/test_stdouterr.py,sha256=9jlzeWU7csjPmcaC09RR6i9U8eTLdUm_R_hTGli0k58,4458
340
+ parsl/tests/test_monitoring/test_stdouterr.py,sha256=AjzD4oumRIe9XMDPU4Cn_9-fwx34Vg8eAq58VfCO_zc,4492
341
341
  parsl/tests/test_monitoring/test_viz_colouring.py,sha256=83Qdmn3gM0j7IL6kPDcuIsp_nl4zj-liPijyIN632SY,592
342
342
  parsl/tests/test_mpi_apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
343
343
  parsl/tests/test_mpi_apps/test_bad_mpi_config.py,sha256=QKvEUSrHIBrvqu2fRj1MAqxsYxDfcrdQ7dzWdOZejuU,1320
@@ -401,6 +401,7 @@ parsl/tests/test_scaling/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
401
401
  parsl/tests/test_scaling/test_block_error_handler.py,sha256=OS1IyiK8gjRFI1VzpmOvEnKsPev2vKmC6Z2Hp5LaHpA,6068
402
402
  parsl/tests/test_scaling/test_regression_1621.py,sha256=e3-bkHR3d8LxA-uY0BugyWgYzksh00I_UbaA-jHOzKY,1872
403
403
  parsl/tests/test_scaling/test_regression_3568_scaledown_vs_MISSING.py,sha256=bjE_NIBoWK6heEz5LN0tzE1977vUA9kVemAYCqcIbzY,2942
404
+ parsl/tests/test_scaling/test_regression_3696_oscillation.py,sha256=7Xc3vgocXXUbUegh9t5OyXlV91lRXDVMUlrOwErYOXA,3621
404
405
  parsl/tests/test_scaling/test_scale_down.py,sha256=vHJOMRUriW6xPtaY8GTKYXd5P0WJkQV6Q1IPui05aLU,2736
405
406
  parsl/tests/test_scaling/test_scale_down_htex_auto_scale.py,sha256=EnVNllKO2AGKkGa6927cLrzvvG6mpNQeFDzVktv6x08,4521
406
407
  parsl/tests/test_scaling/test_scale_down_htex_unregistered.py,sha256=OrdnYmd58n7UfkANPJ7mzha4WSCPdbgJRX1O1Zdu0tI,1954
@@ -449,13 +450,13 @@ parsl/usage_tracking/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
449
450
  parsl/usage_tracking/api.py,sha256=iaCY58Dc5J4UM7_dJzEEs871P1p1HdxBMtNGyVdzc9g,1821
450
451
  parsl/usage_tracking/levels.py,sha256=xbfzYEsd55KiZJ-mzNgPebvOH4rRHum04hROzEf41tU,291
451
452
  parsl/usage_tracking/usage.py,sha256=tcoZ2OUjsQVakG8Uu9_HFuEdzpSHyt4JarSRcLGnSMw,8918
452
- parsl-2024.12.23.data/scripts/exec_parsl_function.py,sha256=YXKVVIa4zXmOtz-0Ca4E_5nQfN_3S2bh2tB75uZZB4w,7774
453
- parsl-2024.12.23.data/scripts/interchange.py,sha256=rUhF_Bwk5NOqLhh-HgP-ei_gclKnPIJJ7uS32p0j-XI,30129
454
- parsl-2024.12.23.data/scripts/parsl_coprocess.py,sha256=zrVjEqQvFOHxsLufPi00xzMONagjVwLZbavPM7bbjK4,5722
455
- parsl-2024.12.23.data/scripts/process_worker_pool.py,sha256=82FoJTye2SysJzPg-N8BpenuHGU7hOI8-Bedq8HV9C0,41851
456
- parsl-2024.12.23.dist-info/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
457
- parsl-2024.12.23.dist-info/METADATA,sha256=19V6tjgV6yT4ycIAUFZS-seRIXUzQwSUvk6rebdbAt0,3848
458
- parsl-2024.12.23.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
459
- parsl-2024.12.23.dist-info/entry_points.txt,sha256=XqnsWDYoEcLbsMcpnYGKLEnSBmaIe1YoM5YsBdJG2tI,176
460
- parsl-2024.12.23.dist-info/top_level.txt,sha256=PIheYoUFQtF2icLsgOykgU-Cjuwr2Oi6On2jo5RYgRM,6
461
- parsl-2024.12.23.dist-info/RECORD,,
453
+ parsl-2025.1.6.data/scripts/exec_parsl_function.py,sha256=YXKVVIa4zXmOtz-0Ca4E_5nQfN_3S2bh2tB75uZZB4w,7774
454
+ parsl-2025.1.6.data/scripts/interchange.py,sha256=rUhF_Bwk5NOqLhh-HgP-ei_gclKnPIJJ7uS32p0j-XI,30129
455
+ parsl-2025.1.6.data/scripts/parsl_coprocess.py,sha256=zrVjEqQvFOHxsLufPi00xzMONagjVwLZbavPM7bbjK4,5722
456
+ parsl-2025.1.6.data/scripts/process_worker_pool.py,sha256=82FoJTye2SysJzPg-N8BpenuHGU7hOI8-Bedq8HV9C0,41851
457
+ parsl-2025.1.6.dist-info/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
458
+ parsl-2025.1.6.dist-info/METADATA,sha256=vd794sFRn6CYChMUdYFIu7M5BZTvn18yg0Ymji3XGjI,3860
459
+ parsl-2025.1.6.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
460
+ parsl-2025.1.6.dist-info/entry_points.txt,sha256=XqnsWDYoEcLbsMcpnYGKLEnSBmaIe1YoM5YsBdJG2tI,176
461
+ parsl-2025.1.6.dist-info/top_level.txt,sha256=PIheYoUFQtF2icLsgOykgU-Cjuwr2Oi6On2jo5RYgRM,6
462
+ parsl-2025.1.6.dist-info/RECORD,,