panda-server-source 0.8.0__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.
Files changed (375) hide show
  1. panda_server_source-0.8.0.dist-info/METADATA +55 -0
  2. panda_server_source-0.8.0.dist-info/RECORD +375 -0
  3. panda_server_source-0.8.0.dist-info/WHEEL +4 -0
  4. panda_server_source-0.8.0.dist-info/licenses/LICENSE.txt +202 -0
  5. pandajedi/__init__.py +0 -0
  6. pandajedi/jedibrokerage/AtlasAnalJobBroker.py +2343 -0
  7. pandajedi/jedibrokerage/AtlasBrokerUtils.py +1292 -0
  8. pandajedi/jedibrokerage/AtlasProdJobBroker.py +2064 -0
  9. pandajedi/jedibrokerage/AtlasProdTaskBroker.py +629 -0
  10. pandajedi/jedibrokerage/GenJobBroker.py +381 -0
  11. pandajedi/jedibrokerage/JobBrokerBase.py +133 -0
  12. pandajedi/jedibrokerage/TaskBrokerBase.py +15 -0
  13. pandajedi/jedibrokerage/__init__.py +0 -0
  14. pandajedi/jediconfig/__init__.py +0 -0
  15. pandajedi/jediconfig/jedi_config.py +40 -0
  16. pandajedi/jedicore/FactoryBase.py +182 -0
  17. pandajedi/jedicore/Interaction.py +433 -0
  18. pandajedi/jedicore/JediDBProxy.py +56 -0
  19. pandajedi/jedicore/JediDBProxyPool.py +37 -0
  20. pandajedi/jedicore/JediDBSchemaInfo.py +17 -0
  21. pandajedi/jedicore/JediException.py +13 -0
  22. pandajedi/jedicore/JediTaskBuffer.py +984 -0
  23. pandajedi/jedicore/JediTaskBufferInterface.py +50 -0
  24. pandajedi/jedicore/MsgWrapper.py +124 -0
  25. pandajedi/jedicore/ProcessUtils.py +34 -0
  26. pandajedi/jedicore/SiteCandidate.py +90 -0
  27. pandajedi/jedicore/ThreadUtils.py +205 -0
  28. pandajedi/jedicore/__init__.py +0 -0
  29. pandajedi/jediddm/AtlasDDMClient.py +1708 -0
  30. pandajedi/jediddm/DDMClientBase.py +16 -0
  31. pandajedi/jediddm/DDMInterface.py +83 -0
  32. pandajedi/jediddm/GenDDMClient.py +17 -0
  33. pandajedi/jediddm/__init__.py +0 -0
  34. pandajedi/jedidog/AtlasAnalWatchDog.py +648 -0
  35. pandajedi/jedidog/AtlasDataCarouselWatchDog.py +189 -0
  36. pandajedi/jedidog/AtlasDataLocalityUpdaterWatchDog.py +215 -0
  37. pandajedi/jedidog/AtlasProdWatchDog.py +443 -0
  38. pandajedi/jedidog/AtlasQueueFillerWatchDog.py +800 -0
  39. pandajedi/jedidog/AtlasTaskWithholderWatchDog.py +245 -0
  40. pandajedi/jedidog/AtlasWorkflowManagerWatchDog.py +63 -0
  41. pandajedi/jedidog/GenWatchDog.py +21 -0
  42. pandajedi/jedidog/JumboWatchDog.py +147 -0
  43. pandajedi/jedidog/TypicalWatchDogBase.py +122 -0
  44. pandajedi/jedidog/WatchDogBase.py +53 -0
  45. pandajedi/jedidog/__init__.py +0 -0
  46. pandajedi/jediexec/reniceJEDI.py +25 -0
  47. pandajedi/jedigen/AtlasTaskGenerator.py +110 -0
  48. pandajedi/jedigen/TaskGeneratorBase.py +15 -0
  49. pandajedi/jedigen/__init__.py +0 -0
  50. pandajedi/jedimsgprocessor/__init__.py +0 -0
  51. pandajedi/jedimsgprocessor/ai_parameter_adjuster_msg_processor +71 -0
  52. pandajedi/jedimsgprocessor/atlas_idds_msg_processor.py +66 -0
  53. pandajedi/jedimsgprocessor/base_msg_processor.py +33 -0
  54. pandajedi/jedimsgprocessor/forwarding_msg_processor.py +29 -0
  55. pandajedi/jedimsgprocessor/hpo_msg_processor.py +93 -0
  56. pandajedi/jedimsgprocessor/jedi_contents_feeder_msg_processor.py +63 -0
  57. pandajedi/jedimsgprocessor/jedi_job_generator_msg_processor.py +185 -0
  58. pandajedi/jedimsgprocessor/jedi_post_processor_msg_processor.py +82 -0
  59. pandajedi/jedimsgprocessor/kafka_msg_processor.py +44 -0
  60. pandajedi/jedimsgprocessor/panda_callback_msg_processor.py +182 -0
  61. pandajedi/jedimsgprocessor/panda_to_jedi_msg_processor.py +74 -0
  62. pandajedi/jedimsgprocessor/processing_msg_processor.py +134 -0
  63. pandajedi/jedimsgprocessor/status_report_msg_processor.py +129 -0
  64. pandajedi/jedimsgprocessor/tape_carousel_msg_processor.py +118 -0
  65. pandajedi/jedimsgprocessor/workflow_manager_msg_processor.py +89 -0
  66. pandajedi/jediorder/ContentsFeeder.py +785 -0
  67. pandajedi/jediorder/JediDaemon.py +27 -0
  68. pandajedi/jediorder/JediKnight.py +55 -0
  69. pandajedi/jediorder/JediMaster.py +238 -0
  70. pandajedi/jediorder/JediMsgProcessor.py +32 -0
  71. pandajedi/jediorder/JobBroker.py +33 -0
  72. pandajedi/jediorder/JobGenerator.py +2763 -0
  73. pandajedi/jediorder/JobSplitter.py +308 -0
  74. pandajedi/jediorder/JobThrottler.py +28 -0
  75. pandajedi/jediorder/PostProcessor.py +166 -0
  76. pandajedi/jediorder/TaskBroker.py +268 -0
  77. pandajedi/jediorder/TaskCommando.py +334 -0
  78. pandajedi/jediorder/TaskGenerator.py +17 -0
  79. pandajedi/jediorder/TaskRefiner.py +611 -0
  80. pandajedi/jediorder/TaskSetupper.py +17 -0
  81. pandajedi/jediorder/WatchDog.py +79 -0
  82. pandajedi/jediorder/__init__.py +0 -0
  83. pandajedi/jedipprocess/AtlasAnalPostProcessor.py +404 -0
  84. pandajedi/jedipprocess/AtlasPostProcessorUtils.py +50 -0
  85. pandajedi/jedipprocess/AtlasProdPostProcessor.py +178 -0
  86. pandajedi/jedipprocess/GenPostProcessor.py +35 -0
  87. pandajedi/jedipprocess/MailTemplates.py +185 -0
  88. pandajedi/jedipprocess/PostProcessorBase.py +269 -0
  89. pandajedi/jedipprocess/__init__.py +0 -0
  90. pandajedi/jedirefine/AtlasAnalTaskRefiner.py +259 -0
  91. pandajedi/jedirefine/AtlasProdTaskRefiner.py +162 -0
  92. pandajedi/jedirefine/GenTaskRefiner.py +61 -0
  93. pandajedi/jedirefine/RefinerUtils.py +154 -0
  94. pandajedi/jedirefine/TaskRefinerBase.py +996 -0
  95. pandajedi/jedirefine/__init__.py +0 -0
  96. pandajedi/jedisetup/AtlasTaskSetupper.py +309 -0
  97. pandajedi/jedisetup/GenTaskSetupper.py +16 -0
  98. pandajedi/jedisetup/SimpleTaskSetupper.py +152 -0
  99. pandajedi/jedisetup/TaskSetupperBase.py +15 -0
  100. pandajedi/jedisetup/__init__.py +0 -0
  101. pandajedi/jeditest/SchemaChecker.py +41 -0
  102. pandajedi/jeditest/addContWoSoTask.py +54 -0
  103. pandajedi/jeditest/addDataCarouselRequest.py +47 -0
  104. pandajedi/jeditest/addEventServiceTask.py +101 -0
  105. pandajedi/jeditest/addHpoTask.py +81 -0
  106. pandajedi/jeditest/addIDDSStageInRequest.py +56 -0
  107. pandajedi/jeditest/addNonAtlasTask.py +71 -0
  108. pandajedi/jeditest/addNonAtlasTaskEnc.py +64 -0
  109. pandajedi/jeditest/addTestTask.py +124 -0
  110. pandajedi/jeditest/addTestTaskParamToDEFT.py +70 -0
  111. pandajedi/jeditest/addTestTaskWithMerger.py +97 -0
  112. pandajedi/jeditest/brokerTest.py +78 -0
  113. pandajedi/jeditest/cancelDataCarouselRequest.py +35 -0
  114. pandajedi/jeditest/contentsFeederTest.py +28 -0
  115. pandajedi/jeditest/contentsFeederTestWithTask.py +18 -0
  116. pandajedi/jeditest/ddmTest.py +4 -0
  117. pandajedi/jeditest/jobGenForOneTask.py +38 -0
  118. pandajedi/jeditest/jobGeneratorTest.py +43 -0
  119. pandajedi/jeditest/jobSplitterTest.py +104 -0
  120. pandajedi/jeditest/jobThrottlerTest.py +22 -0
  121. pandajedi/jeditest/msgProcessorTest.py +9 -0
  122. pandajedi/jeditest/postProcessorTest.py +18 -0
  123. pandajedi/jeditest/resubmitDataCarouselRequest.py +35 -0
  124. pandajedi/jeditest/resubmitTaskParam.py +20 -0
  125. pandajedi/jeditest/taskBrokerTest.py +22 -0
  126. pandajedi/jeditest/taskBrokerTestWithTask.py +38 -0
  127. pandajedi/jeditest/taskBufferTest.py +5 -0
  128. pandajedi/jeditest/taskCommandoTest.py +28 -0
  129. pandajedi/jeditest/taskRefinerTest.py +30 -0
  130. pandajedi/jeditest/testOneTaskToRefine.py +49 -0
  131. pandajedi/jeditest/testOneTaskToTaskBroker.py +42 -0
  132. pandajedi/jeditest/testPandaDDMMap.py +62 -0
  133. pandajedi/jeditest/updateDataCarouselRequestParams.py +50 -0
  134. pandajedi/jeditest/watchDogTest.py +25 -0
  135. pandajedi/jeditest/workQueueTest.py +22 -0
  136. pandajedi/jedithrottle/AtlasAnalJobThrottler.py +19 -0
  137. pandajedi/jedithrottle/AtlasProdJobThrottler.py +18 -0
  138. pandajedi/jedithrottle/GenJobThrottler.py +26 -0
  139. pandajedi/jedithrottle/JobThrottlerBase.py +496 -0
  140. pandajedi/jedithrottle/__init__.py +0 -0
  141. pandaserver/__init__.py +0 -0
  142. pandaserver/api/__init__.py +0 -0
  143. pandaserver/api/v1/__init__.py +0 -0
  144. pandaserver/api/v1/common.py +339 -0
  145. pandaserver/api/v1/credential_management_api.py +372 -0
  146. pandaserver/api/v1/data_carousel_api.py +264 -0
  147. pandaserver/api/v1/event_api.py +269 -0
  148. pandaserver/api/v1/file_server_api.py +942 -0
  149. pandaserver/api/v1/harvester_api.py +445 -0
  150. pandaserver/api/v1/http_client.py +166 -0
  151. pandaserver/api/v1/idds_api.py +180 -0
  152. pandaserver/api/v1/job_api.py +463 -0
  153. pandaserver/api/v1/metaconfig_api.py +122 -0
  154. pandaserver/api/v1/pilot_api.py +895 -0
  155. pandaserver/api/v1/statistics_api.py +207 -0
  156. pandaserver/api/v1/system_api.py +186 -0
  157. pandaserver/api/v1/task_api.py +1520 -0
  158. pandaserver/api/v1/tests/__init__.py +0 -0
  159. pandaserver/api/v1/tests/credential_management_api_tests.py +116 -0
  160. pandaserver/api/v1/tests/data_carousel_api_tests.py +82 -0
  161. pandaserver/api/v1/tests/harvester_api_tests.py +188 -0
  162. pandaserver/api/v1/tests/idds_api_tests.py +45 -0
  163. pandaserver/api/v1/tests/job_api_tests.py +112 -0
  164. pandaserver/api/v1/tests/pilot_api_tests.py +154 -0
  165. pandaserver/api/v1/tests/statistics_api_tests.py +104 -0
  166. pandaserver/api/v1/tests/system_api_tests.py +58 -0
  167. pandaserver/api/v1/tests/task_api_tests.py +590 -0
  168. pandaserver/api/v1/version.py +2 -0
  169. pandaserver/api/v1/workflow_api.py +136 -0
  170. pandaserver/brokerage/SiteMapper.py +406 -0
  171. pandaserver/brokerage/__init__.py +0 -0
  172. pandaserver/brokerage/broker.py +272 -0
  173. pandaserver/config/__init__.py +0 -0
  174. pandaserver/config/config_utils.py +15 -0
  175. pandaserver/config/daemon_config.py +29 -0
  176. pandaserver/config/mcp_config.py +38 -0
  177. pandaserver/config/panda_config.py +233 -0
  178. pandaserver/configurator/Carbon.py +65 -0
  179. pandaserver/configurator/Configurator.py +908 -0
  180. pandaserver/configurator/__init__.py +0 -0
  181. pandaserver/configurator/aux.py +73 -0
  182. pandaserver/daemons/__init__.py +0 -0
  183. pandaserver/daemons/master.py +110 -0
  184. pandaserver/daemons/master_systemd.py +79 -0
  185. pandaserver/daemons/scripts/__init__.py +0 -0
  186. pandaserver/daemons/scripts/add_main.py +264 -0
  187. pandaserver/daemons/scripts/add_sub.py +221 -0
  188. pandaserver/daemons/scripts/cache_pilots.py +47 -0
  189. pandaserver/daemons/scripts/cache_schedconfig.py +286 -0
  190. pandaserver/daemons/scripts/carbon.py +50 -0
  191. pandaserver/daemons/scripts/configurator.py +94 -0
  192. pandaserver/daemons/scripts/copyArchive.py +1387 -0
  193. pandaserver/daemons/scripts/datasetManager.py +1154 -0
  194. pandaserver/daemons/scripts/dummy_test.py +25 -0
  195. pandaserver/daemons/scripts/evpPD2P.py +144 -0
  196. pandaserver/daemons/scripts/hs_scrapers.py +264 -0
  197. pandaserver/daemons/scripts/metric_collector.py +927 -0
  198. pandaserver/daemons/scripts/panda_activeusers_query.py +80 -0
  199. pandaserver/daemons/scripts/pilotStreaming.py +109 -0
  200. pandaserver/daemons/scripts/process_workflow_files_daemon.py +166 -0
  201. pandaserver/daemons/scripts/recover_lost_files_daemon.py +177 -0
  202. pandaserver/daemons/scripts/task_evaluator.py +405 -0
  203. pandaserver/daemons/scripts/tmpwatch.py +70 -0
  204. pandaserver/daemons/scripts/worker_synchronization.py +97 -0
  205. pandaserver/daemons/utils.py +753 -0
  206. pandaserver/dataservice/DataServiceUtils.py +323 -0
  207. pandaserver/dataservice/ErrorCode.py +25 -0
  208. pandaserver/dataservice/RecoverLostFilesCore.py +322 -0
  209. pandaserver/dataservice/__init__.py +0 -0
  210. pandaserver/dataservice/activator.py +79 -0
  211. pandaserver/dataservice/adder_atlas_plugin.py +1106 -0
  212. pandaserver/dataservice/adder_dummy_plugin.py +31 -0
  213. pandaserver/dataservice/adder_gen.py +954 -0
  214. pandaserver/dataservice/adder_plugin_base.py +27 -0
  215. pandaserver/dataservice/adder_result.py +75 -0
  216. pandaserver/dataservice/adder_simple_plugin.py +160 -0
  217. pandaserver/dataservice/closer.py +247 -0
  218. pandaserver/dataservice/closer_atlas_plugin.py +90 -0
  219. pandaserver/dataservice/ddm.py +1381 -0
  220. pandaserver/dataservice/ddm_handler.py +95 -0
  221. pandaserver/dataservice/dyn_data_distributer.py +824 -0
  222. pandaserver/dataservice/event_lookup_client_ei.py +77 -0
  223. pandaserver/dataservice/event_picker.py +268 -0
  224. pandaserver/dataservice/finisher.py +194 -0
  225. pandaserver/dataservice/setupper.py +225 -0
  226. pandaserver/dataservice/setupper_atlas_plugin.py +1851 -0
  227. pandaserver/dataservice/setupper_dummy_plugin.py +47 -0
  228. pandaserver/dataservice/setupper_plugin_base.py +67 -0
  229. pandaserver/jobdispatcher/DispatcherUtils.py +35 -0
  230. pandaserver/jobdispatcher/ErrorCode.py +19 -0
  231. pandaserver/jobdispatcher/JobDispatcher.py +1625 -0
  232. pandaserver/jobdispatcher/Protocol.py +443 -0
  233. pandaserver/jobdispatcher/Watcher.py +186 -0
  234. pandaserver/jobdispatcher/__init__.py +0 -0
  235. pandaserver/pandamcp/__init__.py +0 -0
  236. pandaserver/pandamcp/mcp_main.py +56 -0
  237. pandaserver/pandamcp/mcp_test_client.py +95 -0
  238. pandaserver/pandamcp/mcp_utils.py +74 -0
  239. pandaserver/proxycache/__init__.py +0 -0
  240. pandaserver/proxycache/panda_proxy_cache.py +240 -0
  241. pandaserver/proxycache/token_cache.py +141 -0
  242. pandaserver/server/.gacl +6 -0
  243. pandaserver/server/panda.py +539 -0
  244. pandaserver/srvcore/CoreUtils.py +572 -0
  245. pandaserver/srvcore/MailUtils.py +106 -0
  246. pandaserver/srvcore/__init__.py +0 -0
  247. pandaserver/srvcore/allowed_methods.py +100 -0
  248. pandaserver/srvcore/oidc_utils.py +152 -0
  249. pandaserver/srvcore/panda_request.py +196 -0
  250. pandaserver/srvcore/srv_msg_utils.py +42 -0
  251. pandaserver/taskbuffer/ConBridge.py +513 -0
  252. pandaserver/taskbuffer/DBProxyPool.py +95 -0
  253. pandaserver/taskbuffer/DataCarousel.py +3053 -0
  254. pandaserver/taskbuffer/DatasetSpec.py +137 -0
  255. pandaserver/taskbuffer/DdmSpec.py +201 -0
  256. pandaserver/taskbuffer/ErrorCode.py +87 -0
  257. pandaserver/taskbuffer/EventServiceUtils.py +396 -0
  258. pandaserver/taskbuffer/FileSpec.py +290 -0
  259. pandaserver/taskbuffer/GlobalShares.py +244 -0
  260. pandaserver/taskbuffer/HarvesterMetricsSpec.py +92 -0
  261. pandaserver/taskbuffer/Initializer.py +82 -0
  262. pandaserver/taskbuffer/InputChunk.py +1080 -0
  263. pandaserver/taskbuffer/JediCacheSpec.py +57 -0
  264. pandaserver/taskbuffer/JediDatasetSpec.py +588 -0
  265. pandaserver/taskbuffer/JediFileSpec.py +242 -0
  266. pandaserver/taskbuffer/JediTaskSpec.py +1687 -0
  267. pandaserver/taskbuffer/JobSpec.py +1006 -0
  268. pandaserver/taskbuffer/JobUtils.py +232 -0
  269. pandaserver/taskbuffer/NucleusSpec.py +116 -0
  270. pandaserver/taskbuffer/OraDBProxy.py +174 -0
  271. pandaserver/taskbuffer/PanDAMsgProcessor.py +8 -0
  272. pandaserver/taskbuffer/PandaDBSchemaInfo.py +18 -0
  273. pandaserver/taskbuffer/ParseJobXML.py +350 -0
  274. pandaserver/taskbuffer/PickleFileSpec.py +18 -0
  275. pandaserver/taskbuffer/PickleJobSpec.py +25 -0
  276. pandaserver/taskbuffer/PrioUtil.py +40 -0
  277. pandaserver/taskbuffer/ProcessGroups.py +111 -0
  278. pandaserver/taskbuffer/ResourceSpec.py +175 -0
  279. pandaserver/taskbuffer/SQLDumper.py +37 -0
  280. pandaserver/taskbuffer/SQLManager.py +60 -0
  281. pandaserver/taskbuffer/SiteSpec.py +304 -0
  282. pandaserver/taskbuffer/SupErrors.py +10 -0
  283. pandaserver/taskbuffer/TaskBuffer.py +2882 -0
  284. pandaserver/taskbuffer/TaskBufferInterface.py +163 -0
  285. pandaserver/taskbuffer/Utils.py +848 -0
  286. pandaserver/taskbuffer/WorkQueue.py +227 -0
  287. pandaserver/taskbuffer/WorkQueueMapper.py +203 -0
  288. pandaserver/taskbuffer/WorkerSpec.py +143 -0
  289. pandaserver/taskbuffer/WrappedCursor.py +487 -0
  290. pandaserver/taskbuffer/WrappedPickle.py +88 -0
  291. pandaserver/taskbuffer/WrappedPostgresConn.py +24 -0
  292. pandaserver/taskbuffer/__init__.py +0 -0
  293. pandaserver/taskbuffer/db_proxy_mods/OraDBProxy_structure.png +0 -0
  294. pandaserver/taskbuffer/db_proxy_mods/__init__.py +7 -0
  295. pandaserver/taskbuffer/db_proxy_mods/base_module.py +841 -0
  296. pandaserver/taskbuffer/db_proxy_mods/data_carousel_module.py +772 -0
  297. pandaserver/taskbuffer/db_proxy_mods/entity_module.py +3804 -0
  298. pandaserver/taskbuffer/db_proxy_mods/job_complex_module.py +5661 -0
  299. pandaserver/taskbuffer/db_proxy_mods/job_standalone_module.py +2640 -0
  300. pandaserver/taskbuffer/db_proxy_mods/metrics_module.py +874 -0
  301. pandaserver/taskbuffer/db_proxy_mods/misc_standalone_module.py +4272 -0
  302. pandaserver/taskbuffer/db_proxy_mods/task_complex_module.py +6172 -0
  303. pandaserver/taskbuffer/db_proxy_mods/task_event_module.py +4953 -0
  304. pandaserver/taskbuffer/db_proxy_mods/task_standalone_module.py +4999 -0
  305. pandaserver/taskbuffer/db_proxy_mods/task_utils_module.py +2063 -0
  306. pandaserver/taskbuffer/db_proxy_mods/worker_module.py +1944 -0
  307. pandaserver/taskbuffer/db_proxy_mods/workflow_module.py +832 -0
  308. pandaserver/taskbuffer/retryModule.py +552 -0
  309. pandaserver/taskbuffer/task_split_rules.py +308 -0
  310. pandaserver/taskbuffer/workflow_processor.py +249 -0
  311. pandaserver/taskbuffer/wrapped_oracle_conn.py +18 -0
  312. pandaserver/test/SchemaChecker.py +40 -0
  313. pandaserver/test/__init__.py +0 -0
  314. pandaserver/test/banUser.py +43 -0
  315. pandaserver/test/boostPrio.py +19 -0
  316. pandaserver/test/boostUser.py +35 -0
  317. pandaserver/test/callbackDDM.py +11 -0
  318. pandaserver/test/daod_on_demand.py +95 -0
  319. pandaserver/test/finishJob.py +184 -0
  320. pandaserver/test/finishTaskJEDI.py +11 -0
  321. pandaserver/test/getJobs.py +53 -0
  322. pandaserver/test/killJob.py +79 -0
  323. pandaserver/test/killJobLowPrio.py +99 -0
  324. pandaserver/test/killJobsInTask.py +56 -0
  325. pandaserver/test/killTask.py +80 -0
  326. pandaserver/test/killTaskJEDI.py +11 -0
  327. pandaserver/test/killUser.py +83 -0
  328. pandaserver/test/lsst/README.txt +7 -0
  329. pandaserver/test/lsst/lsstSubmit.py +139 -0
  330. pandaserver/test/lsst/lsstSubmitMERGEtest.py +141 -0
  331. pandaserver/test/lsst/lsstSubmitPhosim332.py +53 -0
  332. pandaserver/test/lsst/prepare-client-tarball-from-bigpanda-server.sh +41 -0
  333. pandaserver/test/reassignJobs.py +17 -0
  334. pandaserver/test/reassignSite.py +82 -0
  335. pandaserver/test/reassignTask.py +117 -0
  336. pandaserver/test/reassignWaiting.py +39 -0
  337. pandaserver/test/recoverLostFiles.py +7 -0
  338. pandaserver/test/reloadInputDS.py +51 -0
  339. pandaserver/test/sendCommandToJob.py +26 -0
  340. pandaserver/test/setDebugMode.py +38 -0
  341. pandaserver/test/setPriority.py +28 -0
  342. pandaserver/test/testEvgen.py +60 -0
  343. pandaserver/test/testEvgen17.py +58 -0
  344. pandaserver/test/testG4sim.py +81 -0
  345. pandaserver/test/testG4sim17.py +90 -0
  346. pandaserver/test/testGlobalShares.py +131 -0
  347. pandaserver/test/testReco.py +103 -0
  348. pandaserver/test/testSimulReco14.py +98 -0
  349. pandaserver/test/testSiteMap.py +26 -0
  350. pandaserver/test/testUpdateWorkerPilotStatus.py +9 -0
  351. pandaserver/userinterface/Client.py +1686 -0
  352. pandaserver/userinterface/UserIF.py +1799 -0
  353. pandaserver/userinterface/__init__.py +0 -0
  354. pandaserver/workflow/__init__.py +0 -0
  355. pandaserver/workflow/data_handler_plugins/base_data_handler.py +48 -0
  356. pandaserver/workflow/data_handler_plugins/ddm_collection_data_handler.py +100 -0
  357. pandaserver/workflow/data_handler_plugins/panda_task_data_handler.py +140 -0
  358. pandaserver/workflow/pcwl_test.py +122 -0
  359. pandaserver/workflow/pcwl_utils.py +384 -0
  360. pandaserver/workflow/psnakemake_container.json +68 -0
  361. pandaserver/workflow/psnakemake_task.json +62 -0
  362. pandaserver/workflow/psnakemake_test.py +60 -0
  363. pandaserver/workflow/snakeparser/__init__.py +3 -0
  364. pandaserver/workflow/snakeparser/extensions.py +74 -0
  365. pandaserver/workflow/snakeparser/log.py +53 -0
  366. pandaserver/workflow/snakeparser/names.py +3 -0
  367. pandaserver/workflow/snakeparser/parser.py +332 -0
  368. pandaserver/workflow/snakeparser/utils.py +29 -0
  369. pandaserver/workflow/step_handler_plugins/base_step_handler.py +85 -0
  370. pandaserver/workflow/step_handler_plugins/panda_task_step_handler.py +280 -0
  371. pandaserver/workflow/workflow_base.py +531 -0
  372. pandaserver/workflow/workflow_core.py +1974 -0
  373. pandaserver/workflow/workflow_core_smoketest.py +388 -0
  374. pandaserver/workflow/workflow_parser.py +222 -0
  375. pandaserver/workflow/workflow_utils.py +1047 -0
@@ -0,0 +1,384 @@
1
+ import copy
2
+ import os.path
3
+ import re
4
+ from pathlib import Path
5
+ from urllib.parse import urlparse
6
+
7
+ from .workflow_utils import ConditionItem, Node
8
+
9
+ WORKFLOW_NAMES = ["prun", "phpo", "junction", "reana", "gitlab"]
10
+
11
+
12
+ # extract id
13
+ def extract_id(id_str):
14
+ if not id_str:
15
+ return id_str
16
+ if not isinstance(id_str, list):
17
+ id_str = [id_str]
18
+ not_list = True
19
+ else:
20
+ not_list = False
21
+ items = [re.search(r"[^/]+#.+$", s).group(0) for s in id_str]
22
+ if not_list:
23
+ return items[0]
24
+ return items
25
+
26
+
27
+ # topological sorting
28
+ def top_sort(list_data, visited):
29
+ if not list_data:
30
+ return []
31
+ new_list = []
32
+ new_visited = []
33
+ for node in list_data:
34
+ isOK = True
35
+ for p in node.parents:
36
+ if p not in visited:
37
+ isOK = False
38
+ break
39
+ if isOK:
40
+ new_visited.append(node)
41
+ visited.add(node.id)
42
+ else:
43
+ new_list.append(node)
44
+ return new_visited + top_sort(new_list, visited)
45
+
46
+
47
+ # parse CWL file
48
+ def parse_workflow_file(workflow_file, log_stream, in_loop=False):
49
+ # read the file from yaml
50
+ cwl_file = Path(os.path.abspath(workflow_file))
51
+ cwl_dir = cwl_file.parent
52
+
53
+ from cwl_utils.parser import load_document_by_uri
54
+
55
+ # make fake executables to skip validation check in CWL
56
+ for tmp_exec in WORKFLOW_NAMES:
57
+ tmp_exec_path = cwl_dir / tmp_exec
58
+ if not tmp_exec_path.exists():
59
+ tmp_exec_path.touch()
60
+
61
+ # Import CWL Object
62
+ root_obj = load_document_by_uri(cwl_file)
63
+
64
+ # root inputs
65
+ root_inputs = {extract_id(s.id): s.default for s in root_obj.inputs}
66
+
67
+ root_outputs = set([re.sub(re.sub(extract_id(s.id), "", s.id), "", s.outputSource) for s in root_obj.outputs])
68
+
69
+ # loop over steps
70
+ node_list = []
71
+ output_map = {}
72
+ serial_id = 0
73
+ for step in root_obj.steps:
74
+ cwl_name = os.path.basename(step.run)
75
+ # check cwl command
76
+ if not cwl_name.endswith(".cwl") and cwl_name not in WORKFLOW_NAMES:
77
+ log_stream.error(f"Unknown workflow {step.run}")
78
+ return False, None
79
+ serial_id += 1
80
+ workflow_name = step.id.split("#")[-1]
81
+ # leaf workflow and sub-workflow
82
+ if cwl_name == "prun.cwl":
83
+ node = Node(serial_id, "prun", None, True, workflow_name)
84
+ elif cwl_name == "phpo.cwl":
85
+ node = Node(serial_id, "phpo", None, True, workflow_name)
86
+ elif cwl_name in WORKFLOW_NAMES:
87
+ node = Node(serial_id, cwl_name, None, True, workflow_name)
88
+ else:
89
+ node = Node(serial_id, "workflow", None, False, workflow_name)
90
+ node.inputs = {extract_id(s.id): {"default": s.default, "source": extract_id(s.source)} for s in step.in_}
91
+ node.outputs = {extract_id(s): {} for s in step.out}
92
+ # add outDS if no output is defined
93
+ if not node.outputs:
94
+ node.outputs = {extract_id(step.id + "/outDS"): {}}
95
+ output_map.update({name: serial_id for name in node.outputs})
96
+ if step.scatter:
97
+ node.scatter = [extract_id(s) for s in step.scatter]
98
+ if hasattr(step, "when") and step.when:
99
+ # parse condition
100
+ node.condition = parse_condition_string(step.when)
101
+ # suppress inputs based on condition
102
+ suppress_inputs_based_on_condition(node.condition, node.inputs)
103
+ if step.hints and "loop" in step.hints:
104
+ node.loop = True
105
+ if node.loop or in_loop:
106
+ node.in_loop = True
107
+ # expand sub-workflow
108
+ if not node.is_leaf:
109
+ p = urlparse(step.run)
110
+ tmp_path = os.path.abspath(os.path.join(p.netloc, p.path))
111
+ node.sub_nodes, node.root_inputs = parse_workflow_file(tmp_path, log_stream, node.in_loop)
112
+ # check if tail
113
+ if root_outputs & set(node.outputs):
114
+ node.is_tail = True
115
+ node_list.append(node)
116
+
117
+ # look for parents
118
+ for node in node_list:
119
+ for tmp_name, tmp_data in node.inputs.items():
120
+ if not tmp_data["source"]:
121
+ continue
122
+ if isinstance(tmp_data["source"], list):
123
+ sources = tmp_data["source"]
124
+ is_str = False
125
+ else:
126
+ sources = [tmp_data["source"]]
127
+ is_str = True
128
+ parent_ids = []
129
+ for tmp_source in sources:
130
+ if tmp_source in output_map:
131
+ parent_id = output_map[tmp_source]
132
+ node.add_parent(parent_id)
133
+ parent_ids.append(parent_id)
134
+ if parent_ids:
135
+ if is_str:
136
+ parent_ids = parent_ids[0]
137
+ tmp_data["parent_id"] = parent_ids
138
+
139
+ # sort
140
+ node_list = top_sort(node_list, set())
141
+ return node_list, root_inputs
142
+
143
+
144
+ # resolve nodes
145
+ def resolve_nodes(node_list, root_inputs, data, serial_id, parent_ids, out_ds_name, log_stream):
146
+ for k in root_inputs:
147
+ kk = k.split("#")[-1]
148
+ if kk in data:
149
+ root_inputs[k] = data[kk]
150
+ tmp_to_real_id_map = {}
151
+ resolved_map = {}
152
+ all_nodes = []
153
+ for node in node_list:
154
+ # resolve input
155
+ for tmp_name, tmp_data in node.inputs.items():
156
+ if not tmp_data["source"]:
157
+ continue
158
+ if isinstance(tmp_data["source"], list):
159
+ tmp_sources = tmp_data["source"]
160
+ if "parent_id" in tmp_data:
161
+ tmp_parent_ids = tmp_data["parent_id"]
162
+ tmp_parent_ids += [None] * (len(tmp_sources) - len(tmp_parent_ids))
163
+ else:
164
+ tmp_parent_ids = [None] * len(tmp_sources)
165
+ else:
166
+ tmp_sources = [tmp_data["source"]]
167
+ if "parent_id" in tmp_data:
168
+ tmp_parent_ids = [tmp_data["parent_id"]]
169
+ else:
170
+ tmp_parent_ids = [None] * len(tmp_sources)
171
+ for tmp_source, tmp_parent_id in zip(tmp_sources, tmp_parent_ids):
172
+ isOK = False
173
+ # check root input
174
+ if tmp_source in root_inputs:
175
+ node.is_head = True
176
+ node.set_input_value(tmp_name, tmp_source, root_inputs[tmp_source])
177
+ continue
178
+ # check parent output
179
+ for i in node.parents:
180
+ for r_node in resolved_map[i]:
181
+ if tmp_source in r_node.outputs:
182
+ node.set_input_value(
183
+ tmp_name,
184
+ tmp_source,
185
+ r_node.outputs[tmp_source]["value"],
186
+ )
187
+ isOK = True
188
+ break
189
+ if isOK:
190
+ break
191
+ if isOK:
192
+ continue
193
+ # check resolved parent outputs
194
+ if tmp_parent_id is not None:
195
+ values = [list(r_node.outputs.values())[0]["value"] for r_node in resolved_map[tmp_parent_id]]
196
+ if len(values) == 1:
197
+ values = values[0]
198
+ node.set_input_value(tmp_name, tmp_source, values)
199
+ continue
200
+ # scatter
201
+ if node.scatter:
202
+ # resolve scattered parameters
203
+ scatters = None
204
+ sc_nodes = []
205
+ for item in node.scatter:
206
+ if scatters is None:
207
+ scatters = [{item: v} for v in node.inputs[item]["value"]]
208
+ else:
209
+ [i.update({item: v}) for i, v in zip(scatters, node.inputs[item]["value"])]
210
+ for idx, item in enumerate(scatters):
211
+ sc_node = copy.deepcopy(node)
212
+ for k, v in item.items():
213
+ sc_node.inputs[k]["value"] = v
214
+ for tmp_node in sc_node.sub_nodes:
215
+ tmp_node.scatter_index = idx
216
+ tmp_node.upper_root_inputs = sc_node.root_inputs
217
+ sc_nodes.append(sc_node)
218
+ else:
219
+ sc_nodes = [node]
220
+ # loop over scattered nodes
221
+ for sc_node in sc_nodes:
222
+ all_nodes.append(sc_node)
223
+ # set real node ID
224
+ resolved_map.setdefault(sc_node.id, [])
225
+ tmp_to_real_id_map.setdefault(sc_node.id, set())
226
+ # resolve parents
227
+ real_parens = set()
228
+ for i in sc_node.parents:
229
+ real_parens |= tmp_to_real_id_map[i]
230
+ sc_node.parents = real_parens
231
+ if sc_node.is_head:
232
+ sc_node.parents |= parent_ids
233
+ if sc_node.is_leaf:
234
+ resolved_map[sc_node.id].append(sc_node)
235
+ tmp_to_real_id_map[sc_node.id].add(serial_id)
236
+ sc_node.id = serial_id
237
+ serial_id += 1
238
+ else:
239
+ serial_id, sub_tail_nodes, sc_node.sub_nodes = resolve_nodes(
240
+ sc_node.sub_nodes,
241
+ sc_node.root_inputs,
242
+ sc_node.convert_dict_inputs(),
243
+ serial_id,
244
+ sc_node.parents,
245
+ out_ds_name,
246
+ log_stream,
247
+ )
248
+ resolved_map[sc_node.id] += sub_tail_nodes
249
+ tmp_to_real_id_map[sc_node.id] |= set([n.id for n in sub_tail_nodes])
250
+ sc_node.id = serial_id
251
+ serial_id += 1
252
+ # convert parameters to parent IDs in conditions
253
+ if sc_node.condition:
254
+ convert_params_in_condition_to_parent_ids(sc_node.condition, sc_node.inputs, tmp_to_real_id_map)
255
+ # resolve outputs
256
+ if sc_node.is_leaf:
257
+ for tmp_name, tmp_data in sc_node.outputs.items():
258
+ tmp_data["value"] = f"{out_ds_name}_{sc_node.id:03d}_{sc_node.name}"
259
+ # add loop count for nodes in a loop
260
+ if sc_node.in_loop:
261
+ tmp_data["value"] += ".___idds___num_run___"
262
+ # return tails
263
+ tail_nodes = []
264
+ for node in all_nodes:
265
+ if node.is_tail:
266
+ if node.is_tail:
267
+ tail_nodes.append(node)
268
+ else:
269
+ tail_nodes += resolved_map[node.id]
270
+ return serial_id, tail_nodes, all_nodes
271
+
272
+
273
+ # parse condition string
274
+ def parse_condition_string(cond_string):
275
+ # remove $()
276
+ cond_string = re.sub(r"\$\((?P<aaa>.+)\)", r"\g<aaa>", cond_string)
277
+ cond_map = {}
278
+ id = 0
279
+ while True:
280
+ # look for the most inner parentheses
281
+ item_list = re.findall(r"\(([^\(\)]+)\)", cond_string)
282
+ if not item_list:
283
+ return convert_plain_condition_string(cond_string, cond_map)
284
+ else:
285
+ for item in item_list:
286
+ cond = convert_plain_condition_string(item, cond_map)
287
+ key = f"___{id}___"
288
+ id += 1
289
+ cond_map[key] = cond
290
+ cond_string = cond_string.replace("(" + item + ")", key)
291
+
292
+
293
+ # extract parameter from token
294
+ def extract_parameter(token):
295
+ m = re.search(r"self\.([^!=]+)", token)
296
+ return m.group(1)
297
+
298
+
299
+ # convert plain condition string
300
+ def convert_plain_condition_string(cond_string, cond_map):
301
+ cond_string = re.sub(r" *! *", r"!", cond_string)
302
+ cond_string = re.sub(r"\|\|", r" || ", cond_string)
303
+ cond_string = re.sub(r"&&", r" && ", cond_string)
304
+
305
+ tokens = cond_string.split()
306
+ left = None
307
+ operator = None
308
+ for token in tokens:
309
+ token = token.strip()
310
+ if token == "||":
311
+ operator = "or"
312
+ continue
313
+ elif token == "&&":
314
+ operator = "and"
315
+ continue
316
+ elif token.startswith("self."):
317
+ param = extract_parameter(token)
318
+ right = ConditionItem(param)
319
+ if not left:
320
+ left = right
321
+ continue
322
+ elif token.startswith("!self."):
323
+ param = extract_parameter(token)
324
+ right = ConditionItem(param, operator="not")
325
+ if not left:
326
+ left = right
327
+ continue
328
+ elif re.search(r"^___\d+___$", token) and token in cond_map:
329
+ right = cond_map[token]
330
+ if not left:
331
+ left = right
332
+ continue
333
+ elif re.search(r"^!___\d+___$", token) and token[1:] in cond_map:
334
+ right = ConditionItem(cond_map[token[1:]], operator="not")
335
+ if not left:
336
+ left = right
337
+ continue
338
+ else:
339
+ raise TypeError(f'unknown token "{token}"')
340
+
341
+ left = ConditionItem(left, right, operator)
342
+ return left
343
+
344
+
345
+ # convert parameter names to parent IDs
346
+ def convert_params_in_condition_to_parent_ids(condition_item, input_data, id_map):
347
+ for item in ["left", "right"]:
348
+ param = getattr(condition_item, item)
349
+ if isinstance(param, str):
350
+ m = re.search(r"^[^\[]+\[(\d+)\]", param)
351
+ if m:
352
+ param = param.split("[")[0]
353
+ idx = int(m.group(1))
354
+ else:
355
+ idx = None
356
+ isOK = False
357
+ for tmp_name, tmp_data in input_data.items():
358
+ if param == tmp_name.split("/")[-1]:
359
+ isOK = True
360
+ if isinstance(tmp_data["parent_id"], list):
361
+ if idx is not None:
362
+ setattr(condition_item, item, id_map[tmp_data["parent_id"][idx]])
363
+ else:
364
+ setattr(condition_item, item, id_map[tmp_data["parent_id"]])
365
+ else:
366
+ setattr(condition_item, item, id_map[tmp_data["parent_id"]])
367
+ break
368
+ if not isOK:
369
+ raise ReferenceError(f"unresolved parameter {param} in the condition string")
370
+ elif isinstance(param, ConditionItem):
371
+ convert_params_in_condition_to_parent_ids(param, input_data, id_map)
372
+
373
+
374
+ # suppress inputs based on condition
375
+ def suppress_inputs_based_on_condition(condition_item, input_data):
376
+ if condition_item.right is None and condition_item.operator == "not" and isinstance(condition_item.left, str):
377
+ for tmp_name, tmp_data in input_data.items():
378
+ if condition_item.left == tmp_name.split("/")[-1]:
379
+ tmp_data["suppressed"] = True
380
+ else:
381
+ for item in ["left", "right"]:
382
+ param = getattr(condition_item, item)
383
+ if isinstance(param, ConditionItem):
384
+ suppress_inputs_based_on_condition(param, input_data)
@@ -0,0 +1,68 @@
1
+ {
2
+ "sourceURL": "https://aipanda048.cern.ch:25443",
3
+ "cliParams": "prun --cwl test.cwl --yaml a.yaml --relayHost aipanda059.cern.ch --outDS user.retmas.b8ad7187-9513-4a34-9736-dd6062ac1138",
4
+ "site": null,
5
+ "vo": "atlas",
6
+ "respectSplitRule": true,
7
+ "osInfo": "Linux-3.10.0-1160.36.2.el7.x86_64-x86_64-with-centos-7.9.2009-Core",
8
+ "log": {
9
+ "type": "template",
10
+ "param_type": "log",
11
+ "container": "user.retmas.b8ad7187-9513-4a34-9736-dd6062ac1138.log/",
12
+ "value": "user.retmas.b8ad7187-9513-4a34-9736-dd6062ac1138.log.$JEDITASKID.${SN}.log.tgz",
13
+ "dataset": "user.retmas.b8ad7187-9513-4a34-9736-dd6062ac1138.log/"
14
+ },
15
+ "transUses": "",
16
+ "excludedSite": [],
17
+ "nMaxFilesPerJob": 200,
18
+ "uniqueTaskName": true,
19
+ "taskName": "user.retmas.b8ad7187-9513-4a34-9736-dd6062ac1138/",
20
+ "transHome": null,
21
+ "includedSite": null,
22
+ "container_name": "__dummy_container__",
23
+ "multiStepExec": {
24
+ "preprocess": {
25
+ "args": "--preprocess ${TRF_ARGS}",
26
+ "command": "${TRF}"
27
+ },
28
+ "containerOptions": {
29
+ "containerImage": "__dummy_container__",
30
+ "containerExec": "echo \"=== cat exec script ===\"; cat __run_main_exec.sh; echo; echo \"=== exec script ===\"; /bin/sh __run_main_exec.sh"
31
+ },
32
+ "postprocess": {
33
+ "args": "--postprocess ${TRF_ARGS}",
34
+ "command": "${TRF}"
35
+ }
36
+ },
37
+ "jobParameters": [
38
+ {
39
+ "type": "constant",
40
+ "value": "-j \"\" --sourceURL ${SURL}"
41
+ },
42
+ {
43
+ "type": "constant",
44
+ "value": "-r ."
45
+ },
46
+ {
47
+ "padding": false,
48
+ "type": "constant",
49
+ "value": "-p \""
50
+ },
51
+ {
52
+ "padding": false,
53
+ "type": "constant",
54
+ "value": "__dummy_exec_str__"
55
+ },
56
+ {
57
+ "type": "constant",
58
+ "value": "\""
59
+ },
60
+ {
61
+ "type": "constant",
62
+ "value": "-a jobO.b6bdf294-8a46-4b02-b399-46619db4088b.tar.gz"
63
+ }
64
+ ],
65
+ "prodSourceLabel": "user",
66
+ "processingType": "panda-client-1.4.81-jedi-run",
67
+ "architecture": ""
68
+ }
@@ -0,0 +1,62 @@
1
+ {
2
+ "buildSpec": {
3
+ "jobParameters": "-i ${IN} -o ${OUT} --sourceURL ${SURL} -r ./ --useAthenaPackages --useCMake --cmtConfig x86_64-slc6-gcc49-opt",
4
+ "archiveName": "sources.5bca61db-3b0d-4e65-b102-7d56d82f4567.tar.gz",
5
+ "prodSourceLabel": "panda"
6
+ },
7
+ "sourceURL": "https://aipanda047.cern.ch:25443",
8
+ "cliParams": "prun --exec ls --useAthenaPackages --outDS user.retmas.9e640888-94b7-4e5e-bcf8-83cc927039c8",
9
+ "site": null,
10
+ "vo": "atlas",
11
+ "respectSplitRule": true,
12
+ "osInfo": "Linux-3.10.0-1160.36.2.el7.x86_64-x86_64-with-centos-7.9.2009-Core",
13
+ "log": {
14
+ "type": "template",
15
+ "param_type": "log",
16
+ "container": "user.retmas.9e640888-94b7-4e5e-bcf8-83cc927039c8.log/",
17
+ "value": "user.retmas.9e640888-94b7-4e5e-bcf8-83cc927039c8.log.$JEDITASKID.${SN}.log.tgz",
18
+ "dataset": "user.retmas.9e640888-94b7-4e5e-bcf8-83cc927039c8.log/"
19
+ },
20
+ "transUses": "Atlas-21.0.6",
21
+ "excludedSite": [],
22
+ "nMaxFilesPerJob": 200,
23
+ "uniqueTaskName": true,
24
+ "taskName": "user.retmas.9e640888-94b7-4e5e-bcf8-83cc927039c8/",
25
+ "transHome": "AnalysisTransforms",
26
+ "includedSite": null,
27
+ "jobParameters": [
28
+ {
29
+ "type": "constant",
30
+ "value": "-j \"\" --sourceURL ${SURL}"
31
+ },
32
+ {
33
+ "type": "constant",
34
+ "value": "-r ./"
35
+ },
36
+ {
37
+ "padding": false,
38
+ "type": "constant",
39
+ "value": "-p \""
40
+ },
41
+ {
42
+ "padding": false,
43
+ "type": "constant",
44
+ "value": "ls"
45
+ },
46
+ {
47
+ "type": "constant",
48
+ "value": "\""
49
+ },
50
+ {
51
+ "type": "constant",
52
+ "value": "-l ${LIB}"
53
+ },
54
+ {
55
+ "type": "constant",
56
+ "value": "--useAthenaPackages --useCMake --cmtConfig x86_64-slc6-gcc49-opt"
57
+ }
58
+ ],
59
+ "prodSourceLabel": "user",
60
+ "processingType": "panda-client-1.4.81-jedi-run",
61
+ "architecture": "x86_64-slc6-gcc49-opt@centos7"
62
+ }
@@ -0,0 +1,60 @@
1
+ __author__ = "retmas"
2
+
3
+ import json
4
+ import logging
5
+ import os
6
+ import sys
7
+
8
+ from snakeparser import Parser
9
+
10
+ from pandaserver.workflow.workflow_utils import (
11
+ convert_nodes_to_workflow,
12
+ dump_nodes,
13
+ get_node_id_map,
14
+ resolve_nodes,
15
+ set_workflow_outputs,
16
+ )
17
+
18
+ logging.basicConfig(format="%(asctime)s - %(levelname)s - %(message)s", level=logging.DEBUG)
19
+
20
+
21
+ def verify_node(node):
22
+ result, error = node.verify()
23
+ if not result:
24
+ logging.info(f"Verification error in ID {node.id}: {error}")
25
+
26
+
27
+ # noinspection PyBroadException
28
+ def main():
29
+ try:
30
+ workflow_file = sys.argv[1]
31
+ data = dict()
32
+ logging.info(f"{os.path.basename(__file__)}: workflow_file = {workflow_file}")
33
+ parser = Parser(workflow_file, level=logging.DEBUG)
34
+ nodes, root_in = parser.parse_nodes()
35
+ dot_data = parser.get_dot_data()
36
+ logging.info(f"dot data ={os.linesep}{dot_data}")
37
+ s_id, t_nodes, nodes = resolve_nodes(nodes, root_in, data, 0, set(), sys.argv[2], logging)
38
+ set_workflow_outputs(nodes)
39
+ id_map = get_node_id_map(nodes)
40
+ task_template = None
41
+ dir_ = os.path.dirname(os.path.abspath(__file__))
42
+ with open(os.path.join(dir_, "psnakemake_task.json"), "r") as task_fp:
43
+ with open(os.path.join(dir_, "psnakemake_container.json"), "r") as container_fp:
44
+ task_template = {
45
+ "athena": json.load(task_fp),
46
+ "container": json.load(container_fp),
47
+ }
48
+ if not task_template:
49
+ raise Exception("Failed to load task template")
50
+ _ = list(map(lambda o: o.resolve_params(task_template, id_map), nodes))
51
+ logging.info(dump_nodes(nodes))
52
+ workflow, dump_str_list = convert_nodes_to_workflow(nodes)
53
+ logging.info("".join(dump_str_list))
54
+ _ = list(map(lambda o: verify_node(o), nodes))
55
+ except Exception as ex:
56
+ logging.error(f"exception occurred: {ex}", exc_info=True)
57
+
58
+
59
+ if __name__ == "__main__":
60
+ main()
@@ -0,0 +1,3 @@
1
+ __author__ = "retmas"
2
+
3
+ from .parser import Parser
@@ -0,0 +1,74 @@
1
+ __author__ = "retmas"
2
+
3
+ from snakemake.parser import Rule, RuleKeywordState
4
+ from snakemake.workflow import Workflow
5
+
6
+
7
+ def scatter(self, value):
8
+ def decorate(rule_info):
9
+ rule_info.scatter = value
10
+ return rule_info
11
+
12
+ return decorate
13
+
14
+
15
+ class Scatter(RuleKeywordState):
16
+ @property
17
+ def keyword(self):
18
+ return "scatter"
19
+
20
+
21
+ def loop(self, value):
22
+ def decorate(rule_info):
23
+ rule_info.loop = value
24
+ return rule_info
25
+
26
+ return decorate
27
+
28
+
29
+ class Loop(RuleKeywordState):
30
+ @property
31
+ def keyword(self):
32
+ return "loop"
33
+
34
+
35
+ def condition(self, value):
36
+ def decorate(rule_info):
37
+ rule_info.condition = value
38
+ return rule_info
39
+
40
+ return decorate
41
+
42
+
43
+ class Condition(RuleKeywordState):
44
+ @property
45
+ def keyword(self):
46
+ return "when"
47
+
48
+
49
+ _rule_properties = dict(scatter=Scatter, loop=Loop, when=Condition)
50
+
51
+
52
+ def inject():
53
+ Rule.subautomata.update(**_rule_properties)
54
+ setattr(Workflow, "scatter", scatter)
55
+ setattr(Workflow, "loop", loop)
56
+ setattr(Workflow, "when", condition)
57
+ rule = getattr(Workflow, "rule")
58
+
59
+ def rule_new(self, name=None, lineno=None, snakefile=None, checkpoint=False):
60
+ decorate = rule(self, name, lineno, snakefile, checkpoint)
61
+
62
+ def decorate_new(rule_info):
63
+ rule_obj = self.get_rule(name)
64
+ if getattr(rule_info, "scatter", None):
65
+ rule_obj.scatter = rule_info.scatter
66
+ if getattr(rule_info, "loop", None):
67
+ rule_obj.loop = rule_info.loop
68
+ if getattr(rule_info, "condition", None):
69
+ rule_obj.condition = rule_info.condition
70
+ return decorate(rule_info)
71
+
72
+ return decorate_new
73
+
74
+ setattr(Workflow, "rule", rule_new)