lsst-ctrl-bps-htcondor 29.2025.1500__tar.gz → 29.2025.1700__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 (33) hide show
  1. {lsst_ctrl_bps_htcondor-29.2025.1500/python/lsst_ctrl_bps_htcondor.egg-info → lsst_ctrl_bps_htcondor-29.2025.1700}/PKG-INFO +1 -1
  2. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/python/lsst/ctrl/bps/htcondor/lssthtc.py +19 -4
  3. lsst_ctrl_bps_htcondor-29.2025.1700/python/lsst/ctrl/bps/htcondor/version.py +2 -0
  4. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700/python/lsst_ctrl_bps_htcondor.egg-info}/PKG-INFO +1 -1
  5. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/tests/test_lssthtc.py +89 -1
  6. lsst_ctrl_bps_htcondor-29.2025.1500/python/lsst/ctrl/bps/htcondor/version.py +0 -2
  7. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/COPYRIGHT +0 -0
  8. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/LICENSE +0 -0
  9. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/MANIFEST.in +0 -0
  10. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/README.rst +0 -0
  11. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/bsd_license.txt +0 -0
  12. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/doc/lsst.ctrl.bps.htcondor/CHANGES.rst +0 -0
  13. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/doc/lsst.ctrl.bps.htcondor/index.rst +0 -0
  14. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/doc/lsst.ctrl.bps.htcondor/userguide.rst +0 -0
  15. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/gpl-v3.0.txt +0 -0
  16. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/pyproject.toml +0 -0
  17. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/python/lsst/ctrl/bps/htcondor/__init__.py +0 -0
  18. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/python/lsst/ctrl/bps/htcondor/etc/__init__.py +0 -0
  19. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/python/lsst/ctrl/bps/htcondor/etc/htcondor_defaults.yaml +0 -0
  20. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/python/lsst/ctrl/bps/htcondor/final_post.sh +0 -0
  21. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/python/lsst/ctrl/bps/htcondor/handlers.py +0 -0
  22. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/python/lsst/ctrl/bps/htcondor/htcondor_config.py +0 -0
  23. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/python/lsst/ctrl/bps/htcondor/htcondor_service.py +0 -0
  24. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/python/lsst/ctrl/bps/htcondor/provisioner.py +0 -0
  25. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/python/lsst_ctrl_bps_htcondor.egg-info/SOURCES.txt +0 -0
  26. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/python/lsst_ctrl_bps_htcondor.egg-info/dependency_links.txt +0 -0
  27. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/python/lsst_ctrl_bps_htcondor.egg-info/requires.txt +0 -0
  28. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/python/lsst_ctrl_bps_htcondor.egg-info/top_level.txt +0 -0
  29. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/python/lsst_ctrl_bps_htcondor.egg-info/zip-safe +0 -0
  30. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/setup.cfg +0 -0
  31. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/tests/test_handlers.py +0 -0
  32. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/tests/test_htcondor_service.py +0 -0
  33. {lsst_ctrl_bps_htcondor-29.2025.1500 → lsst_ctrl_bps_htcondor-29.2025.1700}/tests/test_provisioner.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lsst-ctrl-bps-htcondor
3
- Version: 29.2025.1500
3
+ Version: 29.2025.1700
4
4
  Summary: HTCondor plugin for lsst-ctrl-bps.
5
5
  Author-email: Rubin Observatory Data Management <dm-admin@lists.lsst.org>
6
6
  License: BSD 3-Clause License
@@ -703,7 +703,7 @@ def htc_submit_dag(sub):
703
703
  return schedd_dag_info
704
704
 
705
705
 
706
- def htc_create_submit_from_dag(dag_filename, submit_options=None):
706
+ def htc_create_submit_from_dag(dag_filename: str, submit_options: dict[str, Any]) -> htcondor.Submit:
707
707
  """Create a DAGMan job submit description.
708
708
 
709
709
  Parameters
@@ -730,11 +730,26 @@ def htc_create_submit_from_dag(dag_filename, submit_options=None):
730
730
  var_name = "_CONDOR_DAGMAN_MANAGER_JOB_APPEND_GETENV"
731
731
  if var_name not in os.environ:
732
732
  os.environ[var_name] = "True"
733
- do_recurse = submit_options.get("do_recurse", None)
734
- if do_recurse:
733
+
734
+ if "do_recurse" in submit_options:
735
735
  var_name = "_CONDOR_DAGMAN_GENERATE_SUBDAG_SUBMITS"
736
736
  if var_name not in os.environ:
737
- os.environ[var_name] = str(do_recurse)
737
+ os.environ[var_name] = str(submit_options["do_recurse"])
738
+
739
+ # Config and environment variables do not seem to override -MaxIdle
740
+ # on the .dag.condor.sub's command line (broken in some 24.0.x versions).
741
+ # Explicitly forward them as a submit_option if either exists.
742
+ # Note: auto generated subdag submit files are still the -MaxIdle=1000
743
+ # in the broken versions.
744
+ if "MaxIdle" not in submit_options:
745
+ max_jobs_idle: int | None = None
746
+ config_var_name = "DAGMAN_MAX_JOBS_IDLE"
747
+ if f"_CONDOR_{config_var_name}" in os.environ:
748
+ max_jobs_idle = int(os.environ[f"_CONDOR_{config_var_name}"])
749
+ elif config_var_name in htcondor.param:
750
+ max_jobs_idle = htcondor.param[config_var_name]
751
+ if max_jobs_idle:
752
+ submit_options["MaxIdle"] = max_jobs_idle
738
753
 
739
754
  return htcondor.Submit.from_dag(dag_filename, submit_options)
740
755
 
@@ -0,0 +1,2 @@
1
+ __all__ = ["__version__"]
2
+ __version__ = "29.2025.1700"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lsst-ctrl-bps-htcondor
3
- Version: 29.2025.1500
3
+ Version: 29.2025.1700
4
4
  Summary: HTCondor plugin for lsst-ctrl-bps.
5
5
  Author-email: Rubin Observatory Data Management <dm-admin@lists.lsst.org>
6
6
  License: BSD 3-Clause License
@@ -30,10 +30,11 @@ import io
30
30
  import logging
31
31
  import os
32
32
  import pathlib
33
+ import stat
33
34
  import sys
34
35
  import tempfile
35
36
  import unittest
36
- from shutil import copy2, copytree, ignore_patterns, rmtree
37
+ from shutil import copy2, copytree, ignore_patterns, rmtree, which
37
38
 
38
39
  import htcondor
39
40
 
@@ -1139,5 +1140,92 @@ class HtcWriteCondorFileTestCase(unittest.TestCase):
1139
1140
  _ = lssthtc.htc_create_submit_from_file(filename)
1140
1141
 
1141
1142
 
1143
+ class HtcCreateSubmitFromDagTestCase(unittest.TestCase):
1144
+ """Test htc_create_submit_from_dag function."""
1145
+
1146
+ @classmethod
1147
+ def setUpClass(cls):
1148
+ cls.bindir = None
1149
+ # htcondor.Submit.from_dag requires condor_dagman executable in path.
1150
+ if not which("condor_dagman"): # pragma: no cover
1151
+ cls.bindir = tempfile.TemporaryDirectory()
1152
+ fake_dagman_exec = pathlib.Path(cls.bindir.name) / "condor_dagman"
1153
+ with open(fake_dagman_exec, "w") as fh:
1154
+ print("#!/bin/bash", file=fh)
1155
+ print("echo fake_condor_dagman $@", file=fh)
1156
+ print("exit 0", file=fh)
1157
+ fake_dagman_exec.chmod(fake_dagman_exec.stat().st_mode | stat.S_IEXEC)
1158
+ os.environ["PATH"] = f"{os.environ['PATH']}:{cls.bindir.name}"
1159
+
1160
+ @classmethod
1161
+ def tearDownClass(cls):
1162
+ if cls.bindir:
1163
+ cls.bindir.cleanup()
1164
+
1165
+ @unittest.mock.patch.dict(os.environ, {"_CONDOR_DAGMAN_MAX_JOBS_IDLE": "42"})
1166
+ def testMaxIdleEnvVar(self):
1167
+ with temporaryDirectory() as tmp_dir:
1168
+ copy2(f"{TESTDIR}/data/tiny_success/tiny_success.dag", tmp_dir)
1169
+ dag_filename = pathlib.Path(tmp_dir) / "tiny_success.dag"
1170
+ submit = lssthtc.htc_create_submit_from_dag(str(dag_filename), {})
1171
+ self.assertIn("-MaxIdle 42", submit["arguments"])
1172
+ self.assertEqual("true", os.environ["_CONDOR_DAGMAN_MANAGER_JOB_APPEND_GETENV"].lower())
1173
+
1174
+ @unittest.mock.patch.dict(os.environ, {})
1175
+ def testMaxIdleGiven(self):
1176
+ with temporaryDirectory() as tmp_dir:
1177
+ copy2(f"{TESTDIR}/data/tiny_success/tiny_success.dag", tmp_dir)
1178
+ dag_filename = pathlib.Path(tmp_dir) / "tiny_success.dag"
1179
+ submit = lssthtc.htc_create_submit_from_dag(str(dag_filename), {"MaxIdle": 37})
1180
+ self.assertIn("-MaxIdle 37", submit["arguments"])
1181
+ self.assertEqual("true", os.environ["_CONDOR_DAGMAN_MANAGER_JOB_APPEND_GETENV"].lower())
1182
+
1183
+ @unittest.mock.patch.dict(os.environ, {})
1184
+ def testNoMaxJobsIdle(self):
1185
+ """Note: Since the produced arguments differ depending on
1186
+ HTCondor version when no MaxIdle passed to from_dag, not
1187
+ checking arguments string here. Instead just making sure
1188
+ lssthtc code doesn't pass MaxIdle value to from_dag.
1189
+ """
1190
+ with temporaryDirectory() as tmp_dir:
1191
+ copy2(f"{TESTDIR}/data/tiny_success/tiny_success.dag", tmp_dir)
1192
+ dag_filename = pathlib.Path(tmp_dir) / "tiny_success.dag"
1193
+ with unittest.mock.patch("htcondor.Submit.from_dag") as submit_mock:
1194
+ with unittest.mock.patch("htcondor.param") as mock_param:
1195
+ mock_param.__contains__.return_value = False
1196
+ _ = lssthtc.htc_create_submit_from_dag(str(dag_filename), {})
1197
+ self.assertEqual("true", os.environ["_CONDOR_DAGMAN_MANAGER_JOB_APPEND_GETENV"].lower())
1198
+ submit_mock.assert_called_once_with(str(dag_filename), {})
1199
+
1200
+ @unittest.mock.patch.dict(os.environ, {})
1201
+ def testDoRecurseGivenWithNoEnv(self):
1202
+ with temporaryDirectory() as tmp_dir:
1203
+ copy2(f"{TESTDIR}/data/tiny_success/tiny_success.dag", tmp_dir)
1204
+ dag_filename = pathlib.Path(tmp_dir) / "tiny_success.dag"
1205
+ submit = lssthtc.htc_create_submit_from_dag(str(dag_filename), {"do_recurse": True})
1206
+ self.assertIn("-do_recurse", submit["arguments"])
1207
+ self.assertEqual("true", os.environ["_CONDOR_DAGMAN_GENERATE_SUBDAG_SUBMITS"].lower())
1208
+ self.assertEqual("true", os.environ["_CONDOR_DAGMAN_MANAGER_JOB_APPEND_GETENV"].lower())
1209
+
1210
+ @unittest.mock.patch.dict(os.environ, {"_CONDOR_DAGMAN_GENERATE_SUBDAG_SUBMITS": "False"})
1211
+ def testDoRecurseGivenWithEnv(self):
1212
+ with temporaryDirectory() as tmp_dir:
1213
+ copy2(f"{TESTDIR}/data/tiny_success/tiny_success.dag", tmp_dir)
1214
+ dag_filename = pathlib.Path(tmp_dir) / "tiny_success.dag"
1215
+ submit = lssthtc.htc_create_submit_from_dag(str(dag_filename), {"do_recurse": True})
1216
+ self.assertIn("-do_recurse", submit["arguments"])
1217
+ self.assertEqual("true", os.environ["_CONDOR_DAGMAN_MANAGER_JOB_APPEND_GETENV"].lower())
1218
+ self.assertEqual("false", os.environ["_CONDOR_DAGMAN_GENERATE_SUBDAG_SUBMITS"].lower())
1219
+
1220
+ @unittest.mock.patch.dict(os.environ, {"_CONDOR_DAGMAN_MANAGER_JOB_APPEND_GETENV": "*_DIR"})
1221
+ def testGetEnvOverridden(self):
1222
+ with temporaryDirectory() as tmp_dir:
1223
+ copy2(f"{TESTDIR}/data/tiny_success/tiny_success.dag", tmp_dir)
1224
+ dag_filename = pathlib.Path(tmp_dir) / "tiny_success.dag"
1225
+ submit = lssthtc.htc_create_submit_from_dag(str(dag_filename), {"do_recurse": True})
1226
+ self.assertIn("-do_recurse", submit["arguments"])
1227
+ self.assertEqual(os.environ["_CONDOR_DAGMAN_MANAGER_JOB_APPEND_GETENV"], "*_DIR")
1228
+
1229
+
1142
1230
  if __name__ == "__main__":
1143
1231
  unittest.main()
@@ -1,2 +0,0 @@
1
- __all__ = ["__version__"]
2
- __version__ = "29.2025.1500"