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.
- panda_server_source-0.8.0.dist-info/METADATA +55 -0
- panda_server_source-0.8.0.dist-info/RECORD +375 -0
- panda_server_source-0.8.0.dist-info/WHEEL +4 -0
- panda_server_source-0.8.0.dist-info/licenses/LICENSE.txt +202 -0
- pandajedi/__init__.py +0 -0
- pandajedi/jedibrokerage/AtlasAnalJobBroker.py +2343 -0
- pandajedi/jedibrokerage/AtlasBrokerUtils.py +1292 -0
- pandajedi/jedibrokerage/AtlasProdJobBroker.py +2064 -0
- pandajedi/jedibrokerage/AtlasProdTaskBroker.py +629 -0
- pandajedi/jedibrokerage/GenJobBroker.py +381 -0
- pandajedi/jedibrokerage/JobBrokerBase.py +133 -0
- pandajedi/jedibrokerage/TaskBrokerBase.py +15 -0
- pandajedi/jedibrokerage/__init__.py +0 -0
- pandajedi/jediconfig/__init__.py +0 -0
- pandajedi/jediconfig/jedi_config.py +40 -0
- pandajedi/jedicore/FactoryBase.py +182 -0
- pandajedi/jedicore/Interaction.py +433 -0
- pandajedi/jedicore/JediDBProxy.py +56 -0
- pandajedi/jedicore/JediDBProxyPool.py +37 -0
- pandajedi/jedicore/JediDBSchemaInfo.py +17 -0
- pandajedi/jedicore/JediException.py +13 -0
- pandajedi/jedicore/JediTaskBuffer.py +984 -0
- pandajedi/jedicore/JediTaskBufferInterface.py +50 -0
- pandajedi/jedicore/MsgWrapper.py +124 -0
- pandajedi/jedicore/ProcessUtils.py +34 -0
- pandajedi/jedicore/SiteCandidate.py +90 -0
- pandajedi/jedicore/ThreadUtils.py +205 -0
- pandajedi/jedicore/__init__.py +0 -0
- pandajedi/jediddm/AtlasDDMClient.py +1708 -0
- pandajedi/jediddm/DDMClientBase.py +16 -0
- pandajedi/jediddm/DDMInterface.py +83 -0
- pandajedi/jediddm/GenDDMClient.py +17 -0
- pandajedi/jediddm/__init__.py +0 -0
- pandajedi/jedidog/AtlasAnalWatchDog.py +648 -0
- pandajedi/jedidog/AtlasDataCarouselWatchDog.py +189 -0
- pandajedi/jedidog/AtlasDataLocalityUpdaterWatchDog.py +215 -0
- pandajedi/jedidog/AtlasProdWatchDog.py +443 -0
- pandajedi/jedidog/AtlasQueueFillerWatchDog.py +800 -0
- pandajedi/jedidog/AtlasTaskWithholderWatchDog.py +245 -0
- pandajedi/jedidog/AtlasWorkflowManagerWatchDog.py +63 -0
- pandajedi/jedidog/GenWatchDog.py +21 -0
- pandajedi/jedidog/JumboWatchDog.py +147 -0
- pandajedi/jedidog/TypicalWatchDogBase.py +122 -0
- pandajedi/jedidog/WatchDogBase.py +53 -0
- pandajedi/jedidog/__init__.py +0 -0
- pandajedi/jediexec/reniceJEDI.py +25 -0
- pandajedi/jedigen/AtlasTaskGenerator.py +110 -0
- pandajedi/jedigen/TaskGeneratorBase.py +15 -0
- pandajedi/jedigen/__init__.py +0 -0
- pandajedi/jedimsgprocessor/__init__.py +0 -0
- pandajedi/jedimsgprocessor/ai_parameter_adjuster_msg_processor +71 -0
- pandajedi/jedimsgprocessor/atlas_idds_msg_processor.py +66 -0
- pandajedi/jedimsgprocessor/base_msg_processor.py +33 -0
- pandajedi/jedimsgprocessor/forwarding_msg_processor.py +29 -0
- pandajedi/jedimsgprocessor/hpo_msg_processor.py +93 -0
- pandajedi/jedimsgprocessor/jedi_contents_feeder_msg_processor.py +63 -0
- pandajedi/jedimsgprocessor/jedi_job_generator_msg_processor.py +185 -0
- pandajedi/jedimsgprocessor/jedi_post_processor_msg_processor.py +82 -0
- pandajedi/jedimsgprocessor/kafka_msg_processor.py +44 -0
- pandajedi/jedimsgprocessor/panda_callback_msg_processor.py +182 -0
- pandajedi/jedimsgprocessor/panda_to_jedi_msg_processor.py +74 -0
- pandajedi/jedimsgprocessor/processing_msg_processor.py +134 -0
- pandajedi/jedimsgprocessor/status_report_msg_processor.py +129 -0
- pandajedi/jedimsgprocessor/tape_carousel_msg_processor.py +118 -0
- pandajedi/jedimsgprocessor/workflow_manager_msg_processor.py +89 -0
- pandajedi/jediorder/ContentsFeeder.py +785 -0
- pandajedi/jediorder/JediDaemon.py +27 -0
- pandajedi/jediorder/JediKnight.py +55 -0
- pandajedi/jediorder/JediMaster.py +238 -0
- pandajedi/jediorder/JediMsgProcessor.py +32 -0
- pandajedi/jediorder/JobBroker.py +33 -0
- pandajedi/jediorder/JobGenerator.py +2763 -0
- pandajedi/jediorder/JobSplitter.py +308 -0
- pandajedi/jediorder/JobThrottler.py +28 -0
- pandajedi/jediorder/PostProcessor.py +166 -0
- pandajedi/jediorder/TaskBroker.py +268 -0
- pandajedi/jediorder/TaskCommando.py +334 -0
- pandajedi/jediorder/TaskGenerator.py +17 -0
- pandajedi/jediorder/TaskRefiner.py +611 -0
- pandajedi/jediorder/TaskSetupper.py +17 -0
- pandajedi/jediorder/WatchDog.py +79 -0
- pandajedi/jediorder/__init__.py +0 -0
- pandajedi/jedipprocess/AtlasAnalPostProcessor.py +404 -0
- pandajedi/jedipprocess/AtlasPostProcessorUtils.py +50 -0
- pandajedi/jedipprocess/AtlasProdPostProcessor.py +178 -0
- pandajedi/jedipprocess/GenPostProcessor.py +35 -0
- pandajedi/jedipprocess/MailTemplates.py +185 -0
- pandajedi/jedipprocess/PostProcessorBase.py +269 -0
- pandajedi/jedipprocess/__init__.py +0 -0
- pandajedi/jedirefine/AtlasAnalTaskRefiner.py +259 -0
- pandajedi/jedirefine/AtlasProdTaskRefiner.py +162 -0
- pandajedi/jedirefine/GenTaskRefiner.py +61 -0
- pandajedi/jedirefine/RefinerUtils.py +154 -0
- pandajedi/jedirefine/TaskRefinerBase.py +996 -0
- pandajedi/jedirefine/__init__.py +0 -0
- pandajedi/jedisetup/AtlasTaskSetupper.py +309 -0
- pandajedi/jedisetup/GenTaskSetupper.py +16 -0
- pandajedi/jedisetup/SimpleTaskSetupper.py +152 -0
- pandajedi/jedisetup/TaskSetupperBase.py +15 -0
- pandajedi/jedisetup/__init__.py +0 -0
- pandajedi/jeditest/SchemaChecker.py +41 -0
- pandajedi/jeditest/addContWoSoTask.py +54 -0
- pandajedi/jeditest/addDataCarouselRequest.py +47 -0
- pandajedi/jeditest/addEventServiceTask.py +101 -0
- pandajedi/jeditest/addHpoTask.py +81 -0
- pandajedi/jeditest/addIDDSStageInRequest.py +56 -0
- pandajedi/jeditest/addNonAtlasTask.py +71 -0
- pandajedi/jeditest/addNonAtlasTaskEnc.py +64 -0
- pandajedi/jeditest/addTestTask.py +124 -0
- pandajedi/jeditest/addTestTaskParamToDEFT.py +70 -0
- pandajedi/jeditest/addTestTaskWithMerger.py +97 -0
- pandajedi/jeditest/brokerTest.py +78 -0
- pandajedi/jeditest/cancelDataCarouselRequest.py +35 -0
- pandajedi/jeditest/contentsFeederTest.py +28 -0
- pandajedi/jeditest/contentsFeederTestWithTask.py +18 -0
- pandajedi/jeditest/ddmTest.py +4 -0
- pandajedi/jeditest/jobGenForOneTask.py +38 -0
- pandajedi/jeditest/jobGeneratorTest.py +43 -0
- pandajedi/jeditest/jobSplitterTest.py +104 -0
- pandajedi/jeditest/jobThrottlerTest.py +22 -0
- pandajedi/jeditest/msgProcessorTest.py +9 -0
- pandajedi/jeditest/postProcessorTest.py +18 -0
- pandajedi/jeditest/resubmitDataCarouselRequest.py +35 -0
- pandajedi/jeditest/resubmitTaskParam.py +20 -0
- pandajedi/jeditest/taskBrokerTest.py +22 -0
- pandajedi/jeditest/taskBrokerTestWithTask.py +38 -0
- pandajedi/jeditest/taskBufferTest.py +5 -0
- pandajedi/jeditest/taskCommandoTest.py +28 -0
- pandajedi/jeditest/taskRefinerTest.py +30 -0
- pandajedi/jeditest/testOneTaskToRefine.py +49 -0
- pandajedi/jeditest/testOneTaskToTaskBroker.py +42 -0
- pandajedi/jeditest/testPandaDDMMap.py +62 -0
- pandajedi/jeditest/updateDataCarouselRequestParams.py +50 -0
- pandajedi/jeditest/watchDogTest.py +25 -0
- pandajedi/jeditest/workQueueTest.py +22 -0
- pandajedi/jedithrottle/AtlasAnalJobThrottler.py +19 -0
- pandajedi/jedithrottle/AtlasProdJobThrottler.py +18 -0
- pandajedi/jedithrottle/GenJobThrottler.py +26 -0
- pandajedi/jedithrottle/JobThrottlerBase.py +496 -0
- pandajedi/jedithrottle/__init__.py +0 -0
- pandaserver/__init__.py +0 -0
- pandaserver/api/__init__.py +0 -0
- pandaserver/api/v1/__init__.py +0 -0
- pandaserver/api/v1/common.py +339 -0
- pandaserver/api/v1/credential_management_api.py +372 -0
- pandaserver/api/v1/data_carousel_api.py +264 -0
- pandaserver/api/v1/event_api.py +269 -0
- pandaserver/api/v1/file_server_api.py +942 -0
- pandaserver/api/v1/harvester_api.py +445 -0
- pandaserver/api/v1/http_client.py +166 -0
- pandaserver/api/v1/idds_api.py +180 -0
- pandaserver/api/v1/job_api.py +463 -0
- pandaserver/api/v1/metaconfig_api.py +122 -0
- pandaserver/api/v1/pilot_api.py +895 -0
- pandaserver/api/v1/statistics_api.py +207 -0
- pandaserver/api/v1/system_api.py +186 -0
- pandaserver/api/v1/task_api.py +1520 -0
- pandaserver/api/v1/tests/__init__.py +0 -0
- pandaserver/api/v1/tests/credential_management_api_tests.py +116 -0
- pandaserver/api/v1/tests/data_carousel_api_tests.py +82 -0
- pandaserver/api/v1/tests/harvester_api_tests.py +188 -0
- pandaserver/api/v1/tests/idds_api_tests.py +45 -0
- pandaserver/api/v1/tests/job_api_tests.py +112 -0
- pandaserver/api/v1/tests/pilot_api_tests.py +154 -0
- pandaserver/api/v1/tests/statistics_api_tests.py +104 -0
- pandaserver/api/v1/tests/system_api_tests.py +58 -0
- pandaserver/api/v1/tests/task_api_tests.py +590 -0
- pandaserver/api/v1/version.py +2 -0
- pandaserver/api/v1/workflow_api.py +136 -0
- pandaserver/brokerage/SiteMapper.py +406 -0
- pandaserver/brokerage/__init__.py +0 -0
- pandaserver/brokerage/broker.py +272 -0
- pandaserver/config/__init__.py +0 -0
- pandaserver/config/config_utils.py +15 -0
- pandaserver/config/daemon_config.py +29 -0
- pandaserver/config/mcp_config.py +38 -0
- pandaserver/config/panda_config.py +233 -0
- pandaserver/configurator/Carbon.py +65 -0
- pandaserver/configurator/Configurator.py +908 -0
- pandaserver/configurator/__init__.py +0 -0
- pandaserver/configurator/aux.py +73 -0
- pandaserver/daemons/__init__.py +0 -0
- pandaserver/daemons/master.py +110 -0
- pandaserver/daemons/master_systemd.py +79 -0
- pandaserver/daemons/scripts/__init__.py +0 -0
- pandaserver/daemons/scripts/add_main.py +264 -0
- pandaserver/daemons/scripts/add_sub.py +221 -0
- pandaserver/daemons/scripts/cache_pilots.py +47 -0
- pandaserver/daemons/scripts/cache_schedconfig.py +286 -0
- pandaserver/daemons/scripts/carbon.py +50 -0
- pandaserver/daemons/scripts/configurator.py +94 -0
- pandaserver/daemons/scripts/copyArchive.py +1387 -0
- pandaserver/daemons/scripts/datasetManager.py +1154 -0
- pandaserver/daemons/scripts/dummy_test.py +25 -0
- pandaserver/daemons/scripts/evpPD2P.py +144 -0
- pandaserver/daemons/scripts/hs_scrapers.py +264 -0
- pandaserver/daemons/scripts/metric_collector.py +927 -0
- pandaserver/daemons/scripts/panda_activeusers_query.py +80 -0
- pandaserver/daemons/scripts/pilotStreaming.py +109 -0
- pandaserver/daemons/scripts/process_workflow_files_daemon.py +166 -0
- pandaserver/daemons/scripts/recover_lost_files_daemon.py +177 -0
- pandaserver/daemons/scripts/task_evaluator.py +405 -0
- pandaserver/daemons/scripts/tmpwatch.py +70 -0
- pandaserver/daemons/scripts/worker_synchronization.py +97 -0
- pandaserver/daemons/utils.py +753 -0
- pandaserver/dataservice/DataServiceUtils.py +323 -0
- pandaserver/dataservice/ErrorCode.py +25 -0
- pandaserver/dataservice/RecoverLostFilesCore.py +322 -0
- pandaserver/dataservice/__init__.py +0 -0
- pandaserver/dataservice/activator.py +79 -0
- pandaserver/dataservice/adder_atlas_plugin.py +1106 -0
- pandaserver/dataservice/adder_dummy_plugin.py +31 -0
- pandaserver/dataservice/adder_gen.py +954 -0
- pandaserver/dataservice/adder_plugin_base.py +27 -0
- pandaserver/dataservice/adder_result.py +75 -0
- pandaserver/dataservice/adder_simple_plugin.py +160 -0
- pandaserver/dataservice/closer.py +247 -0
- pandaserver/dataservice/closer_atlas_plugin.py +90 -0
- pandaserver/dataservice/ddm.py +1381 -0
- pandaserver/dataservice/ddm_handler.py +95 -0
- pandaserver/dataservice/dyn_data_distributer.py +824 -0
- pandaserver/dataservice/event_lookup_client_ei.py +77 -0
- pandaserver/dataservice/event_picker.py +268 -0
- pandaserver/dataservice/finisher.py +194 -0
- pandaserver/dataservice/setupper.py +225 -0
- pandaserver/dataservice/setupper_atlas_plugin.py +1851 -0
- pandaserver/dataservice/setupper_dummy_plugin.py +47 -0
- pandaserver/dataservice/setupper_plugin_base.py +67 -0
- pandaserver/jobdispatcher/DispatcherUtils.py +35 -0
- pandaserver/jobdispatcher/ErrorCode.py +19 -0
- pandaserver/jobdispatcher/JobDispatcher.py +1625 -0
- pandaserver/jobdispatcher/Protocol.py +443 -0
- pandaserver/jobdispatcher/Watcher.py +186 -0
- pandaserver/jobdispatcher/__init__.py +0 -0
- pandaserver/pandamcp/__init__.py +0 -0
- pandaserver/pandamcp/mcp_main.py +56 -0
- pandaserver/pandamcp/mcp_test_client.py +95 -0
- pandaserver/pandamcp/mcp_utils.py +74 -0
- pandaserver/proxycache/__init__.py +0 -0
- pandaserver/proxycache/panda_proxy_cache.py +240 -0
- pandaserver/proxycache/token_cache.py +141 -0
- pandaserver/server/.gacl +6 -0
- pandaserver/server/panda.py +539 -0
- pandaserver/srvcore/CoreUtils.py +572 -0
- pandaserver/srvcore/MailUtils.py +106 -0
- pandaserver/srvcore/__init__.py +0 -0
- pandaserver/srvcore/allowed_methods.py +100 -0
- pandaserver/srvcore/oidc_utils.py +152 -0
- pandaserver/srvcore/panda_request.py +196 -0
- pandaserver/srvcore/srv_msg_utils.py +42 -0
- pandaserver/taskbuffer/ConBridge.py +513 -0
- pandaserver/taskbuffer/DBProxyPool.py +95 -0
- pandaserver/taskbuffer/DataCarousel.py +3053 -0
- pandaserver/taskbuffer/DatasetSpec.py +137 -0
- pandaserver/taskbuffer/DdmSpec.py +201 -0
- pandaserver/taskbuffer/ErrorCode.py +87 -0
- pandaserver/taskbuffer/EventServiceUtils.py +396 -0
- pandaserver/taskbuffer/FileSpec.py +290 -0
- pandaserver/taskbuffer/GlobalShares.py +244 -0
- pandaserver/taskbuffer/HarvesterMetricsSpec.py +92 -0
- pandaserver/taskbuffer/Initializer.py +82 -0
- pandaserver/taskbuffer/InputChunk.py +1080 -0
- pandaserver/taskbuffer/JediCacheSpec.py +57 -0
- pandaserver/taskbuffer/JediDatasetSpec.py +588 -0
- pandaserver/taskbuffer/JediFileSpec.py +242 -0
- pandaserver/taskbuffer/JediTaskSpec.py +1687 -0
- pandaserver/taskbuffer/JobSpec.py +1006 -0
- pandaserver/taskbuffer/JobUtils.py +232 -0
- pandaserver/taskbuffer/NucleusSpec.py +116 -0
- pandaserver/taskbuffer/OraDBProxy.py +174 -0
- pandaserver/taskbuffer/PanDAMsgProcessor.py +8 -0
- pandaserver/taskbuffer/PandaDBSchemaInfo.py +18 -0
- pandaserver/taskbuffer/ParseJobXML.py +350 -0
- pandaserver/taskbuffer/PickleFileSpec.py +18 -0
- pandaserver/taskbuffer/PickleJobSpec.py +25 -0
- pandaserver/taskbuffer/PrioUtil.py +40 -0
- pandaserver/taskbuffer/ProcessGroups.py +111 -0
- pandaserver/taskbuffer/ResourceSpec.py +175 -0
- pandaserver/taskbuffer/SQLDumper.py +37 -0
- pandaserver/taskbuffer/SQLManager.py +60 -0
- pandaserver/taskbuffer/SiteSpec.py +304 -0
- pandaserver/taskbuffer/SupErrors.py +10 -0
- pandaserver/taskbuffer/TaskBuffer.py +2882 -0
- pandaserver/taskbuffer/TaskBufferInterface.py +163 -0
- pandaserver/taskbuffer/Utils.py +848 -0
- pandaserver/taskbuffer/WorkQueue.py +227 -0
- pandaserver/taskbuffer/WorkQueueMapper.py +203 -0
- pandaserver/taskbuffer/WorkerSpec.py +143 -0
- pandaserver/taskbuffer/WrappedCursor.py +487 -0
- pandaserver/taskbuffer/WrappedPickle.py +88 -0
- pandaserver/taskbuffer/WrappedPostgresConn.py +24 -0
- pandaserver/taskbuffer/__init__.py +0 -0
- pandaserver/taskbuffer/db_proxy_mods/OraDBProxy_structure.png +0 -0
- pandaserver/taskbuffer/db_proxy_mods/__init__.py +7 -0
- pandaserver/taskbuffer/db_proxy_mods/base_module.py +841 -0
- pandaserver/taskbuffer/db_proxy_mods/data_carousel_module.py +772 -0
- pandaserver/taskbuffer/db_proxy_mods/entity_module.py +3804 -0
- pandaserver/taskbuffer/db_proxy_mods/job_complex_module.py +5661 -0
- pandaserver/taskbuffer/db_proxy_mods/job_standalone_module.py +2640 -0
- pandaserver/taskbuffer/db_proxy_mods/metrics_module.py +874 -0
- pandaserver/taskbuffer/db_proxy_mods/misc_standalone_module.py +4272 -0
- pandaserver/taskbuffer/db_proxy_mods/task_complex_module.py +6172 -0
- pandaserver/taskbuffer/db_proxy_mods/task_event_module.py +4953 -0
- pandaserver/taskbuffer/db_proxy_mods/task_standalone_module.py +4999 -0
- pandaserver/taskbuffer/db_proxy_mods/task_utils_module.py +2063 -0
- pandaserver/taskbuffer/db_proxy_mods/worker_module.py +1944 -0
- pandaserver/taskbuffer/db_proxy_mods/workflow_module.py +832 -0
- pandaserver/taskbuffer/retryModule.py +552 -0
- pandaserver/taskbuffer/task_split_rules.py +308 -0
- pandaserver/taskbuffer/workflow_processor.py +249 -0
- pandaserver/taskbuffer/wrapped_oracle_conn.py +18 -0
- pandaserver/test/SchemaChecker.py +40 -0
- pandaserver/test/__init__.py +0 -0
- pandaserver/test/banUser.py +43 -0
- pandaserver/test/boostPrio.py +19 -0
- pandaserver/test/boostUser.py +35 -0
- pandaserver/test/callbackDDM.py +11 -0
- pandaserver/test/daod_on_demand.py +95 -0
- pandaserver/test/finishJob.py +184 -0
- pandaserver/test/finishTaskJEDI.py +11 -0
- pandaserver/test/getJobs.py +53 -0
- pandaserver/test/killJob.py +79 -0
- pandaserver/test/killJobLowPrio.py +99 -0
- pandaserver/test/killJobsInTask.py +56 -0
- pandaserver/test/killTask.py +80 -0
- pandaserver/test/killTaskJEDI.py +11 -0
- pandaserver/test/killUser.py +83 -0
- pandaserver/test/lsst/README.txt +7 -0
- pandaserver/test/lsst/lsstSubmit.py +139 -0
- pandaserver/test/lsst/lsstSubmitMERGEtest.py +141 -0
- pandaserver/test/lsst/lsstSubmitPhosim332.py +53 -0
- pandaserver/test/lsst/prepare-client-tarball-from-bigpanda-server.sh +41 -0
- pandaserver/test/reassignJobs.py +17 -0
- pandaserver/test/reassignSite.py +82 -0
- pandaserver/test/reassignTask.py +117 -0
- pandaserver/test/reassignWaiting.py +39 -0
- pandaserver/test/recoverLostFiles.py +7 -0
- pandaserver/test/reloadInputDS.py +51 -0
- pandaserver/test/sendCommandToJob.py +26 -0
- pandaserver/test/setDebugMode.py +38 -0
- pandaserver/test/setPriority.py +28 -0
- pandaserver/test/testEvgen.py +60 -0
- pandaserver/test/testEvgen17.py +58 -0
- pandaserver/test/testG4sim.py +81 -0
- pandaserver/test/testG4sim17.py +90 -0
- pandaserver/test/testGlobalShares.py +131 -0
- pandaserver/test/testReco.py +103 -0
- pandaserver/test/testSimulReco14.py +98 -0
- pandaserver/test/testSiteMap.py +26 -0
- pandaserver/test/testUpdateWorkerPilotStatus.py +9 -0
- pandaserver/userinterface/Client.py +1686 -0
- pandaserver/userinterface/UserIF.py +1799 -0
- pandaserver/userinterface/__init__.py +0 -0
- pandaserver/workflow/__init__.py +0 -0
- pandaserver/workflow/data_handler_plugins/base_data_handler.py +48 -0
- pandaserver/workflow/data_handler_plugins/ddm_collection_data_handler.py +100 -0
- pandaserver/workflow/data_handler_plugins/panda_task_data_handler.py +140 -0
- pandaserver/workflow/pcwl_test.py +122 -0
- pandaserver/workflow/pcwl_utils.py +384 -0
- pandaserver/workflow/psnakemake_container.json +68 -0
- pandaserver/workflow/psnakemake_task.json +62 -0
- pandaserver/workflow/psnakemake_test.py +60 -0
- pandaserver/workflow/snakeparser/__init__.py +3 -0
- pandaserver/workflow/snakeparser/extensions.py +74 -0
- pandaserver/workflow/snakeparser/log.py +53 -0
- pandaserver/workflow/snakeparser/names.py +3 -0
- pandaserver/workflow/snakeparser/parser.py +332 -0
- pandaserver/workflow/snakeparser/utils.py +29 -0
- pandaserver/workflow/step_handler_plugins/base_step_handler.py +85 -0
- pandaserver/workflow/step_handler_plugins/panda_task_step_handler.py +280 -0
- pandaserver/workflow/workflow_base.py +531 -0
- pandaserver/workflow/workflow_core.py +1974 -0
- pandaserver/workflow/workflow_core_smoketest.py +388 -0
- pandaserver/workflow/workflow_parser.py +222 -0
- pandaserver/workflow/workflow_utils.py +1047 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
__author__ = "retmas"
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import logging.config
|
|
5
|
+
import logging.handlers
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Singleton(type):
|
|
9
|
+
def __init__(cls, *args, **kwargs):
|
|
10
|
+
super(Singleton, cls).__init__(*args, **kwargs)
|
|
11
|
+
cls._instance = None
|
|
12
|
+
|
|
13
|
+
def __call__(cls, *args, **kwargs):
|
|
14
|
+
if cls._instance is None:
|
|
15
|
+
cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
|
|
16
|
+
return cls._instance
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Logger(object, metaclass=Singleton):
|
|
20
|
+
_config = {
|
|
21
|
+
"version": 1,
|
|
22
|
+
"disable_existing_loggers": False,
|
|
23
|
+
"formatters": {"default": {"format": "[%(asctime)s] [%(process)d] [%(levelname)s] [%(module)s] [%(funcName)s:%(lineno)d] - %(message)s"}},
|
|
24
|
+
"handlers": {
|
|
25
|
+
"console": {
|
|
26
|
+
"level": "DEBUG",
|
|
27
|
+
"class": "logging.StreamHandler",
|
|
28
|
+
"formatter": "default",
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"loggers": {"snakeparser.log": {"handlers": ["console"], "level": "INFO"}},
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
def __init__(self):
|
|
35
|
+
logging.config.dictConfig(self._config)
|
|
36
|
+
|
|
37
|
+
@staticmethod
|
|
38
|
+
def set_level(level):
|
|
39
|
+
Logger.get().setLevel(level)
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
def set_log_file(filename, max_bytes=0, backup_count=0, remove_console=False):
|
|
43
|
+
logger = Logger().get()
|
|
44
|
+
handler = logging.handlers.RotatingFileHandler(filename, maxBytes=max_bytes, backupCount=backup_count, encoding="utf-8")
|
|
45
|
+
handler.formatter = logger.handlers[0].formatter
|
|
46
|
+
handler.level = logging.DEBUG
|
|
47
|
+
logger.addHandler(handler)
|
|
48
|
+
if remove_console:
|
|
49
|
+
logger.removeHandler(logger.handlers[0])
|
|
50
|
+
|
|
51
|
+
@staticmethod
|
|
52
|
+
def get() -> logging.Logger:
|
|
53
|
+
return logging.getLogger(__name__)
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
__author__ = "retmas"
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
import os.path
|
|
6
|
+
import pathlib
|
|
7
|
+
import re
|
|
8
|
+
from itertools import chain
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from types import SimpleNamespace
|
|
11
|
+
|
|
12
|
+
from snakemake.api import (
|
|
13
|
+
OutputSettings,
|
|
14
|
+
ResourceSettings,
|
|
15
|
+
SnakemakeApi,
|
|
16
|
+
StorageSettings,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
from pandaserver.workflow.snakeparser.utils import ParamRule, param_of
|
|
20
|
+
from pandaserver.workflow.workflow_utils import ConditionItem, Node
|
|
21
|
+
|
|
22
|
+
from .extensions import inject
|
|
23
|
+
from .log import Logger
|
|
24
|
+
from .names import WORKFLOW_NAMES
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ParamNotFoundException(Exception):
|
|
28
|
+
def __init__(self, param_name, rule_name):
|
|
29
|
+
super().__init__(f"Parameter {param_name} is not found in {rule_name} rule")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class UnknownWorkflowTypeException(Exception):
|
|
33
|
+
def __init__(self, rule_name):
|
|
34
|
+
super().__init__(f"Unknown workflow type for rule {rule_name}")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class UnknownRuleShellException(Exception):
|
|
38
|
+
def __init__(self, rule_name):
|
|
39
|
+
super().__init__(f"Unknown shellcmd for rule {rule_name}")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class NoRuleShellException(Exception):
|
|
43
|
+
def __init__(self, rule_name):
|
|
44
|
+
super().__init__(f"No shellcmd for rule {rule_name}")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class UnknownConditionTokenException(Exception):
|
|
48
|
+
def __init__(self, token):
|
|
49
|
+
super().__init__(f"Unknown token {token}")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# noinspection DuplicatedCode
|
|
53
|
+
class Parser(object):
|
|
54
|
+
def __init__(self, workflow_file, level=None, logger=None):
|
|
55
|
+
self._workflow = None
|
|
56
|
+
self._dag = None
|
|
57
|
+
if logger:
|
|
58
|
+
self._logger = logger
|
|
59
|
+
else:
|
|
60
|
+
self._logger = Logger().get()
|
|
61
|
+
if level:
|
|
62
|
+
self._logger.setLevel(level)
|
|
63
|
+
snakefile = os.path.abspath(workflow_file)
|
|
64
|
+
workdir = os.path.dirname(snakefile)
|
|
65
|
+
self._logger.debug("create workflow")
|
|
66
|
+
# create workflow through API
|
|
67
|
+
with SnakemakeApi(
|
|
68
|
+
OutputSettings(
|
|
69
|
+
verbose=False,
|
|
70
|
+
show_failed_logs=True,
|
|
71
|
+
),
|
|
72
|
+
) as snakemake_api:
|
|
73
|
+
workflow_api = snakemake_api.workflow(
|
|
74
|
+
storage_settings=StorageSettings(),
|
|
75
|
+
resource_settings=ResourceSettings(),
|
|
76
|
+
snakefile=Path(snakefile),
|
|
77
|
+
)
|
|
78
|
+
dag_api = workflow_api.dag()
|
|
79
|
+
self._workflow = workflow_api._workflow
|
|
80
|
+
self._workflow.default_target = "all"
|
|
81
|
+
self._workflow.overwrite_workdir = None
|
|
82
|
+
current_workdir = os.getcwd()
|
|
83
|
+
try:
|
|
84
|
+
inject()
|
|
85
|
+
self._workflow.workdir(workdir)
|
|
86
|
+
finally:
|
|
87
|
+
if current_workdir:
|
|
88
|
+
os.chdir(current_workdir)
|
|
89
|
+
# build DAG
|
|
90
|
+
dag_api.unlock()
|
|
91
|
+
self._dag = self._workflow.dag
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def jobs(self):
|
|
95
|
+
if self._dag is None:
|
|
96
|
+
return list()
|
|
97
|
+
return self._dag.jobs
|
|
98
|
+
|
|
99
|
+
def parse_nodes(self, in_loop=False):
|
|
100
|
+
try:
|
|
101
|
+
return self._parse_nodes(in_loop)
|
|
102
|
+
except NoRuleShellException as ex:
|
|
103
|
+
self._logger.error(str(ex))
|
|
104
|
+
raise ex
|
|
105
|
+
except UnknownRuleShellException as ex:
|
|
106
|
+
self._logger.error(str(ex))
|
|
107
|
+
raise ex
|
|
108
|
+
except UnknownWorkflowTypeException as ex:
|
|
109
|
+
self._logger.error(str(ex))
|
|
110
|
+
raise ex
|
|
111
|
+
except ParamNotFoundException as ex:
|
|
112
|
+
self._logger.error(str(ex))
|
|
113
|
+
raise ex
|
|
114
|
+
|
|
115
|
+
def _parse_nodes(self, in_loop):
|
|
116
|
+
root_job = next(filter(lambda o: o.rule.name == self._workflow.default_target, self.jobs))
|
|
117
|
+
root_inputs = {Parser._extract_job_id(self._define_id(name)): value for name, value in root_job.params.items()}
|
|
118
|
+
root_outputs = set(
|
|
119
|
+
[
|
|
120
|
+
Parser._extract_job_id(s.id.split("#")[0] + "#" + re.sub(s.id + "/", "", s.outputSource))
|
|
121
|
+
for s in list(
|
|
122
|
+
chain(
|
|
123
|
+
*[
|
|
124
|
+
map(
|
|
125
|
+
lambda output: Parser._define_object(
|
|
126
|
+
{
|
|
127
|
+
"id": self._define_id(output),
|
|
128
|
+
"outputSource": self._define_id(f"{dep.name}/{output}"),
|
|
129
|
+
}
|
|
130
|
+
),
|
|
131
|
+
dep.output,
|
|
132
|
+
)
|
|
133
|
+
for dep in self._dag.dependencies[root_job]
|
|
134
|
+
]
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
]
|
|
138
|
+
)
|
|
139
|
+
node_list = []
|
|
140
|
+
output_map = {}
|
|
141
|
+
serial_id = 0
|
|
142
|
+
for job in filter(lambda o: o.name != self._workflow.default_target, self.jobs):
|
|
143
|
+
if job.is_shell:
|
|
144
|
+
if job.shellcmd is None:
|
|
145
|
+
raise NoRuleShellException(job.rule.name)
|
|
146
|
+
else:
|
|
147
|
+
raise UnknownRuleShellException(job.rule.name)
|
|
148
|
+
workflow_name = os.path.basename(job.shellcmd)
|
|
149
|
+
serial_id += 1
|
|
150
|
+
if workflow_name in WORKFLOW_NAMES:
|
|
151
|
+
node = Node(serial_id, workflow_name, None, True, job.name)
|
|
152
|
+
elif workflow_name.lower() == "Snakefile".lower():
|
|
153
|
+
node = Node(serial_id, "workflow", None, False, job.name)
|
|
154
|
+
else:
|
|
155
|
+
raise UnknownWorkflowTypeException(job.rule.name)
|
|
156
|
+
for name, value in job.params.items():
|
|
157
|
+
if isinstance(value, ParamRule):
|
|
158
|
+
if value.rule is None:
|
|
159
|
+
param_job = job
|
|
160
|
+
else:
|
|
161
|
+
param_job = next(filter(lambda o: o.rule.name == value.rule.name, self.jobs))
|
|
162
|
+
if value.name not in param_job.rule.params.keys():
|
|
163
|
+
raise ParamNotFoundException(value.name, param_job.rule.name)
|
|
164
|
+
source = Parser._extract_job_id(self._define_id(f"{param_job.name}/{value.name}"))
|
|
165
|
+
if param_job.name == self._workflow.default_target:
|
|
166
|
+
source = source.replace(f"{param_job.name}/", "")
|
|
167
|
+
node.inputs.update({Parser._extract_job_id(self._define_id(f"{job.name}/{name}")): {"default": None, "source": source}})
|
|
168
|
+
continue
|
|
169
|
+
if node.inputs is None:
|
|
170
|
+
node.inputs = dict()
|
|
171
|
+
default_value = value
|
|
172
|
+
source = None
|
|
173
|
+
if job.dependencies:
|
|
174
|
+
if isinstance(value, list):
|
|
175
|
+
for item in list(value):
|
|
176
|
+
if item in job.dependencies.keys():
|
|
177
|
+
if source is None:
|
|
178
|
+
source = list()
|
|
179
|
+
source.append(Parser._extract_job_id(self._define_id(f"{job.dependencies[item].name}/{item}")))
|
|
180
|
+
if source is not None:
|
|
181
|
+
default_value = None
|
|
182
|
+
else:
|
|
183
|
+
if default_value in job.dependencies.keys():
|
|
184
|
+
source = Parser._extract_job_id(self._define_id(f"{job.dependencies[default_value].name}/{default_value}"))
|
|
185
|
+
default_value = None
|
|
186
|
+
node.inputs.update(
|
|
187
|
+
{
|
|
188
|
+
Parser._extract_job_id(self._define_id(f"{job.name}/{name}")): {
|
|
189
|
+
"default": default_value,
|
|
190
|
+
"source": source,
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
)
|
|
194
|
+
node.outputs = {Parser._extract_job_id(self._define_id(f"{job.name}/{output}")): {} for output in job.output}
|
|
195
|
+
if not node.outputs:
|
|
196
|
+
node.outputs = {Parser._extract_job_id(self._define_id(f"{job.name}/outDS")): {}}
|
|
197
|
+
output_map.update({name: serial_id for name in node.outputs})
|
|
198
|
+
scatter = getattr(job.rule, "scatter", None)
|
|
199
|
+
if scatter:
|
|
200
|
+
node.scatter = [
|
|
201
|
+
Parser._extract_job_id(self._define_id(f"{next(filter(lambda o: o.rule.name == v.rule.name, self.jobs)).name}/{v.name}"))
|
|
202
|
+
for v in [ParamRule(v.name, job.rule if v.rule is None else v.rule) for v in job.rule.scatter]
|
|
203
|
+
]
|
|
204
|
+
else:
|
|
205
|
+
node.scatter = None
|
|
206
|
+
condition = getattr(job.rule, "condition", None)
|
|
207
|
+
if condition:
|
|
208
|
+
pattern = f'{param_of("")}(\w+)'
|
|
209
|
+
for param_name in re.findall(pattern, condition):
|
|
210
|
+
if param_name not in job.rule.params.keys():
|
|
211
|
+
raise ParamNotFoundException(param_name, job.rule.name)
|
|
212
|
+
condition = re.sub(r" *! *", r"!", condition)
|
|
213
|
+
condition = re.sub(r"\|\|", r" || ", condition)
|
|
214
|
+
condition = re.sub(r"&&", r" && ", condition)
|
|
215
|
+
tokens = condition.split()
|
|
216
|
+
param_left = None
|
|
217
|
+
param_operator = None
|
|
218
|
+
for token in tokens:
|
|
219
|
+
token = token.strip()
|
|
220
|
+
if token == "||":
|
|
221
|
+
param_operator = "or"
|
|
222
|
+
continue
|
|
223
|
+
elif token == "&&":
|
|
224
|
+
param_operator = "and"
|
|
225
|
+
continue
|
|
226
|
+
elif token.startswith(str(param_of(""))):
|
|
227
|
+
param_right = ConditionItem(str(re.findall(pattern, token)[0]))
|
|
228
|
+
if not param_left:
|
|
229
|
+
param_left = param_right
|
|
230
|
+
continue
|
|
231
|
+
elif token.startswith(f'!{param_of("")}'):
|
|
232
|
+
param_right = ConditionItem(str(re.findall(pattern, token)[0]), operator="not")
|
|
233
|
+
if not param_left:
|
|
234
|
+
param_left = param_right
|
|
235
|
+
continue
|
|
236
|
+
else:
|
|
237
|
+
raise UnknownConditionTokenException(token)
|
|
238
|
+
param_left = ConditionItem(param_left, param_right, param_operator)
|
|
239
|
+
node.condition = param_left
|
|
240
|
+
self._suppress_inputs(node.condition, node.inputs)
|
|
241
|
+
node.loop = bool(getattr(job.rule, "loop", False))
|
|
242
|
+
if node.loop or in_loop:
|
|
243
|
+
node.in_loop = True
|
|
244
|
+
if not node.is_leaf:
|
|
245
|
+
subworkflow_file = os.path.join(self._workflow.basedir, job.shellcmd)
|
|
246
|
+
subworkflow_parser = Parser(subworkflow_file, level=logging.DEBUG)
|
|
247
|
+
node.sub_nodes, node.root_inputs = subworkflow_parser.parse_nodes()
|
|
248
|
+
if root_outputs & set(node.outputs):
|
|
249
|
+
node.is_tail = True
|
|
250
|
+
node_list.append(node)
|
|
251
|
+
for node in node_list:
|
|
252
|
+
for name, data in node.inputs.items():
|
|
253
|
+
if not data["source"]:
|
|
254
|
+
continue
|
|
255
|
+
if isinstance(data["source"], list):
|
|
256
|
+
sources = data["source"]
|
|
257
|
+
is_str = False
|
|
258
|
+
else:
|
|
259
|
+
sources = [data["source"]]
|
|
260
|
+
is_str = True
|
|
261
|
+
parent_id_list = list()
|
|
262
|
+
for source in sources:
|
|
263
|
+
if source in output_map:
|
|
264
|
+
parent_id = output_map[source]
|
|
265
|
+
node.add_parent(parent_id)
|
|
266
|
+
parent_id_list.append(parent_id)
|
|
267
|
+
if parent_id_list:
|
|
268
|
+
if is_str:
|
|
269
|
+
parent_id_list = parent_id_list[0]
|
|
270
|
+
data["parent_id"] = parent_id_list
|
|
271
|
+
node_list = self._sort_node_list(node_list, set())
|
|
272
|
+
return node_list, root_inputs
|
|
273
|
+
|
|
274
|
+
def verify_workflow(self):
|
|
275
|
+
if self._workflow is None:
|
|
276
|
+
return False
|
|
277
|
+
self._workflow.check()
|
|
278
|
+
return True
|
|
279
|
+
|
|
280
|
+
def get_dot_data(self):
|
|
281
|
+
return str(self._dag)
|
|
282
|
+
|
|
283
|
+
@staticmethod
|
|
284
|
+
def _extract_job_id(job_id):
|
|
285
|
+
if not job_id:
|
|
286
|
+
return job_id
|
|
287
|
+
if not isinstance(job_id, list):
|
|
288
|
+
job_id = [job_id]
|
|
289
|
+
not_list = True
|
|
290
|
+
else:
|
|
291
|
+
not_list = False
|
|
292
|
+
items = [re.search(r"[^/]+#.+$", s).group(0) for s in job_id]
|
|
293
|
+
if not_list:
|
|
294
|
+
return items[0]
|
|
295
|
+
return items
|
|
296
|
+
|
|
297
|
+
@staticmethod
|
|
298
|
+
def _sort_node_list(node_list, visited):
|
|
299
|
+
if not node_list:
|
|
300
|
+
return []
|
|
301
|
+
new_list = []
|
|
302
|
+
new_visited = []
|
|
303
|
+
for node in node_list:
|
|
304
|
+
is_ok = True
|
|
305
|
+
for p in node.parents:
|
|
306
|
+
if p not in visited:
|
|
307
|
+
is_ok = False
|
|
308
|
+
break
|
|
309
|
+
if is_ok:
|
|
310
|
+
new_visited.append(node)
|
|
311
|
+
visited.add(node.id)
|
|
312
|
+
else:
|
|
313
|
+
new_list.append(node)
|
|
314
|
+
return new_visited + Parser._sort_node_list(new_list, visited)
|
|
315
|
+
|
|
316
|
+
def _define_id(self, name):
|
|
317
|
+
return f"{pathlib.Path(os.path.abspath(self._workflow.main_snakefile)).as_uri()}#{name}"
|
|
318
|
+
|
|
319
|
+
@staticmethod
|
|
320
|
+
def _define_object(dict_):
|
|
321
|
+
return SimpleNamespace(**dict_)
|
|
322
|
+
|
|
323
|
+
def _suppress_inputs(self, condition: ConditionItem, inputs):
|
|
324
|
+
if condition.right is None and condition.operator == "not" and isinstance(condition.left, str):
|
|
325
|
+
for name, data in inputs.items():
|
|
326
|
+
if condition.left == name.split("/")[-1]:
|
|
327
|
+
data["suppressed"] = True
|
|
328
|
+
else:
|
|
329
|
+
for item in ["left", "right"]:
|
|
330
|
+
condition_item = getattr(condition, item)
|
|
331
|
+
if isinstance(condition_item, ConditionItem):
|
|
332
|
+
self._suppress_inputs(condition_item, inputs)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
__author__ = "retmas"
|
|
2
|
+
|
|
3
|
+
from snakemake.workflow import Rule, RuleProxy
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ParamRule(object):
|
|
7
|
+
def __init__(self, name: str, source_rule: Rule = None):
|
|
8
|
+
self._name = name
|
|
9
|
+
self._source_rule = source_rule
|
|
10
|
+
|
|
11
|
+
def __repr__(self):
|
|
12
|
+
return f"param.{self.name}"
|
|
13
|
+
|
|
14
|
+
@property
|
|
15
|
+
def name(self) -> str:
|
|
16
|
+
return self._name
|
|
17
|
+
|
|
18
|
+
@property
|
|
19
|
+
def rule(self) -> Rule:
|
|
20
|
+
return self._source_rule
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def param_of(name, source: RuleProxy = None) -> ParamRule:
|
|
24
|
+
rule = source.rule if source is not None else None
|
|
25
|
+
return ParamRule(name, rule)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def param_exp(template):
|
|
29
|
+
return lambda wildcards: template
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
from pandaserver.workflow.workflow_base import (
|
|
2
|
+
WFDataSpec,
|
|
3
|
+
WFDataStatus,
|
|
4
|
+
WFDataType,
|
|
5
|
+
WFStepSpec,
|
|
6
|
+
WFStepStatus,
|
|
7
|
+
WFStepTargetCancelResult,
|
|
8
|
+
WFStepTargetCheckResult,
|
|
9
|
+
WFStepTargetSubmitResult,
|
|
10
|
+
WFStepType,
|
|
11
|
+
WorkflowSpec,
|
|
12
|
+
WorkflowStatus,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BaseStepHandler:
|
|
17
|
+
"""
|
|
18
|
+
Base class for step handlers in the workflow.
|
|
19
|
+
This class provides a common interface and some utility methods for step handlers.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, task_buffer, *args, **kwargs):
|
|
23
|
+
"""
|
|
24
|
+
Initialize the step handler with necessary parameters.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
task_buffer: The task buffer interface to interact with the task database.
|
|
28
|
+
*args: Additional positional arguments.
|
|
29
|
+
**kwargs: Additional keyword arguments.
|
|
30
|
+
"""
|
|
31
|
+
self.tbif = task_buffer
|
|
32
|
+
|
|
33
|
+
def submit_target(self, step_spec: WFStepSpec, **kwargs) -> WFStepTargetSubmitResult:
|
|
34
|
+
"""
|
|
35
|
+
Submit a target for processing the step.
|
|
36
|
+
This method should be implemented by subclasses to handle the specifics of target submission.
|
|
37
|
+
This method should NOT modify step_spec. Any update information should be stored in the WFStepTargetSubmitResult returned instead.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
step_spec (WFStepSpec): Specifications of the workflow step whose target is to be submitted.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
WFStepTargetSubmitResult: An object containing the result of the submission, including success status, target ID, and message.
|
|
44
|
+
|
|
45
|
+
"""
|
|
46
|
+
raise NotImplementedError("Subclasses must implement this method.")
|
|
47
|
+
|
|
48
|
+
def check_target(self, step_spec: WFStepSpec, **kwargs) -> WFStepTargetCheckResult:
|
|
49
|
+
"""
|
|
50
|
+
Check the status of the submitted target.
|
|
51
|
+
This method should be implemented by subclasses to handle the specifics of target status checking.
|
|
52
|
+
This method should NOT modify step_spec. Any update information should be stored in the WFStepTargetCheckResult returned instead.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
step_spec (WFStepSpec): Specifications of the workflow step to be checked.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
WFStepTargetCheckResult: An object containing the result of the check, including success status, current step status, and message.
|
|
59
|
+
"""
|
|
60
|
+
raise NotImplementedError("Subclasses must implement this method.")
|
|
61
|
+
|
|
62
|
+
def on_all_inputs_done(self, step_spec: WFStepSpec, **kwargs) -> None:
|
|
63
|
+
"""
|
|
64
|
+
Hook method called when all inputs for the step are done.
|
|
65
|
+
This method can be overridden by subclasses to perform actions when all inputs are ready.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
step_spec (WFStepSpec): Specifications of the workflow step whose inputs are done.
|
|
69
|
+
**kwargs: Additional keyword arguments.
|
|
70
|
+
"""
|
|
71
|
+
raise NotImplementedError("Subclasses must implement this method.")
|
|
72
|
+
|
|
73
|
+
def cancel_target(self, step_spec: WFStepSpec, **kwargs) -> WFStepTargetCancelResult:
|
|
74
|
+
"""
|
|
75
|
+
Cancel the submitted target.
|
|
76
|
+
This method can be overridden by subclasses to handle target cancellation.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
step_spec (WFStepSpec): Specifications of the workflow step whose target is to be cancelled.
|
|
80
|
+
**kwargs: Additional keyword arguments.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
WFStepTargetCancelResult: An object containing the result of the cancellation, including success status and message.
|
|
84
|
+
"""
|
|
85
|
+
raise NotImplementedError("Subclasses must implement this method.")
|