1- # ' @title Invoke an Analysis Application (Internal Function)
1+
2+ # ' Internal Workflow Execution Engine
23# '
34# ' @description
4- # ' This internal function executes an analysis application after validating its dependencies.
5- # ' It handles application execution, error reporting, and performance profiling.
6- # '
7- # ' @param app A list defining the analysis application. Must contain:
8- # ' - `name`: (character) Name of the application (for logging).
9- # ' - `call`: (function) The function to execute the application logic.
10- # ' - `profiler`: (list) Stores performance metrics (e.g., execution time).
11- # ' @param context A list or environment providing runtime context, such as input data paths,
12- # ' environment variables, or configuration settings required by the application.
13- # '
14- # ' @return Invisibly returns `NULL`. The function primarily updates `app$profiler` with execution
15- # ' time metrics and may modify the `context` during execution.
5+ # ' An internal function that drives the execution of a modular workflow.
6+ # ' It sequentially processes analysis modules according to the pipeline
7+ # ' configuration while respecting disablement rules.
8+ # '
9+ # ' @param context A workflow context object containing:
10+ # ' \itemize{
11+ # ' \item{pipeline - character vector of module execution order}
12+ # ' \item{workflow - list of module definitions}
13+ # ' }
14+ # ' @param disables A named list specifying module disablement status.
15+ # ' Format: `list(module_name = TRUE/FALSE)`. Modules with TRUE will be skipped.
1616# '
1717# ' @details
18- # ' ### Key Steps:
19- # ' 1. **Dependency Check**:
20- # ' Validates if required context variables and files exist via `check_dependency(app, context)`.
21- # ' - If dependencies are met, proceeds to execute the application.
22- # ' - If dependencies are missing, throws an error with detailed missing resources.
23- # '
24- # ' 2. **Execution**:
25- # ' - Logs start/end timestamps if `options(verbose = TRUE)`.
26- # ' - Executes `app$call` with arguments `app` and `context` using `do.call()`.
27- # '
28- # ' 3. **Error Handling**:
29- # ' - Aggregates missing dependencies into readable error messages.
30- # ' - Calls `throw_err()` to terminate the workflow and report issues.
31- # '
32- # ' 4. **Profiling**:
33- # ' Records total execution time in `app$profiler$time` using `time_span()` for human-readable formatting.
34- # '
35- # ' @examples
36- # ' \dontrun{
37- # ' # Define a sample application
38- # ' app <- list(
39- # ' name = "demo_analysis",
40- # ' call = function(argv) {
41- # ' print(paste("Running:", argv$app$name))
42- # ' },
43- # ' profiler = list()
44- # ' )
45- # '
46- # ' # Execute with context
47- # ' .internal_call(app, context = list())
18+ # ' This function:
19+ # ' \enumerate{
20+ # ' \item Retrieves module execution order from `context$pipeline`
21+ # ' \item Checks disablement status through two mechanisms:
22+ # ' \itemize{
23+ # ' \item Explicit disablement via `disables` parameter
24+ # ' \item Module's own `disable` property (set by upstream modules)
25+ # ' }
26+ # ' \item Executes non-disabled modules using `.internal_call()`
27+ # ' \item Provides verbose logging when `options(verbose=TRUE)`
4828# ' }
4929# '
30+ # ' The workflow context is modified in-place by module execution.
31+ # '
5032# ' @note
51- # ' - This is an internal function and not intended for direct use.
52- # ' - Error messages include:
53- # ' - Missing context variables (e.g., `dependency$context_env_missing`).
54- # ' - Missing files (e.g., `dependency$workfiles_missing`).
33+ # ' This is an internal function not meant for direct calling by users.
34+ # ' Modules can control subsequent module execution by setting their
35+ # ' `disable` property.
5536# '
37+ # ' @return
38+ # ' Invisibly returns NULL. Modifies the workflow context object in-place
39+ # ' through module executions.
40+ # '
5641# ' @keywords internal
57- const .internal_call = function (app , context ) {
58- # check of the app dependency
59- let dependency = check_dependency(app , context );
60- let t0 = now();
61- let argv = {
62- app : app ,
63- context : context
64- };
42+ # ' @seealso \code{\link{.internal_call}} for module execution logic
43+ const __runImpl = function (context , disables = list ()) {
44+ let app_pool = context $ workflow ;
45+ let skip = FALSE ;
6546 let verbose = as.logical(getOption(" verbose" ));
6647
67- if (dependency $ check ) {
68- if (verbose ) {
69- print(` * exec: ${app$name}...` );
48+ if (verbose ) {
49+ print(" view module configs:" );
50+ str(disables );
51+ }
52+
53+ # the pipeline data slot defines the workflow module
54+ # execute sequence.
55+ #
56+ # get a specific workflow analysis app module, and then
57+ # execute the module under a given workflow context
58+ for (let app_name in context $ pipeline ) {
59+ let app = app_pool [[app_name ]];
60+ let skip = FALSE ;
61+
62+ if (is.null(app $ name )) {
63+ throw_err(`missing app module definition object for '${app_name}', please check of the app function has been hooked or not?` );
7064 }
7165
72- # check success, then
73- # run current data analysis node
74- do.call(app $ call , args = argv );
66+ if (app $ name in disables ) {
67+ if (as.logical(disables [[app $ name ]])) {
68+ skip = TRUE ;
69+ }
70+ } else {
71+ if (" disable" in app ) {
72+ # current app module may be disable by other
73+ # application from the workflow upsteam
74+ skip = app $ disable ;
75+ }
76+ }
7577
76- if (verbose ) {
77- print(`done ~ '${app$name}' ...... ${time_span(now() - t0)}` );
78+ if (! skip ) {
79+ .internal_call(app , context );
80+ } else {
81+ if (verbose ) {
82+ print(`skip '${app$name}'!` );
83+ }
7884 }
79- } else {
80- # stop the workflow
81- const context_err = dependency.context_env_missing(dependency $ context );
82- const file_err = dependency.workfiles_missing(dependency $ file );
83- const msg_err = [
84- " There are some dependency of current analysis application is not satisfied:" ,
85- paste(c(" analysis_app:" , app $ name ), sep = " " )
86- ];
8785
88- throw_err([ msg_err , context_err , file_err ]) ;
86+ NULL ;
8987 }
9088
91- app $ profiler = {
92- time : time_span(now() - t0 )
93- };
94-
9589 invisible (NULL );
96- }
90+ }
0 commit comments