• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pybuilder / pybuilder / 23886297855

02 Apr 2026 05:56AM UTC coverage: 82.155%. First build
23886297855

Pull #946

github

web-flow
Merge b032d1b79 into f80bd1f03
Pull Request #946: Add --project-info (-i) CLI option for JSON project configuration dump [release]

1408 of 1888 branches covered (74.58%)

Branch coverage included in aggregate %.

54 of 72 new or added lines in 1 file covered. (75.0%)

5562 of 6596 relevant lines covered (84.32%)

0.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.74
/src/main/python/pybuilder/cli.py
1
#   -*- coding: utf-8 -*-
2
#
3
#   This file is part of PyBuilder
4
#
5
#   Copyright 2011-2020 PyBuilder Team
6
#
7
#   Licensed under the Apache License, Version 2.0 (the "License");
8
#   you may not use this file except in compliance with the License.
9
#   You may obtain a copy of the License at
10
#
11
#       http://www.apache.org/licenses/LICENSE-2.0
12
#
13
#   Unless required by applicable law or agreed to in writing, software
14
#   distributed under the License is distributed on an "AS IS" BASIS,
15
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
#   See the License for the specific language governing permissions and
17
#   limitations under the License.
18

19
"""
20
    The PyBuilder cli module.
21
    Contains the PyBuilder command-line entrypoint.
22
"""
23

24
import datetime
1✔
25
import json
1✔
26
import optparse
1✔
27
import re
1✔
28
import sys
1✔
29
import traceback
1✔
30
from os.path import sep, normcase as nc
1✔
31

32
from pybuilder import __version__
1✔
33
from pybuilder import extern
1✔
34
from pybuilder.core import Logger
1✔
35
from pybuilder.errors import PyBuilderException
1✔
36
from pybuilder.execution import ExecutionManager
1✔
37
from pybuilder.python_utils import IS_WIN
1✔
38
from pybuilder.reactor import Reactor
1✔
39
from pybuilder.scaffolding import start_project, update_project
1✔
40
from pybuilder.terminal import (BOLD, BROWN, RED, GREEN, bold, styled_text,
1✔
41
                                fg, italic, print_text, print_text_line,
42
                                print_error, print_error_line, draw_line)
43
from pybuilder.utils import format_timestamp, get_dist_version_string
1✔
44

45
PROPERTY_OVERRIDE_PATTERN = re.compile(r'^[a-zA-Z0-9_]+=.*')
1✔
46
DEFAULT_LOG_TIME_FORMAT = '[%Y-%m-%d %H:%M:%S]'
1✔
47
_extern = extern
1✔
48

49

50
class CommandLineUsageException(PyBuilderException):
1✔
51
    def __init__(self, usage, message):
1✔
52
        super(CommandLineUsageException, self).__init__(message)
1✔
53
        self.usage = usage
1✔
54

55

56
class StdOutLogger(Logger):
1✔
57
    def _level_to_string(self, level):
1✔
58
        if Logger.DEBUG == level:
1✔
59
            return "[DEBUG]"
1✔
60
        if Logger.INFO == level:
1✔
61
            return "[INFO] "
1✔
62
        if Logger.WARN == level:
1✔
63
            return "[WARN] "
1✔
64
        return "[ERROR]"
1✔
65

66
    def _do_log(self, level, message, *arguments):
1✔
67
        formatted_message = self._format_message(message, *arguments)
1✔
68
        log_level = self._level_to_string(level)
1✔
69
        if self.log_time_format is not None:
1✔
70
            timestamp = datetime.datetime.now().strftime(self.log_time_format) + ' '
1✔
71
        else:
72
            timestamp = ''
1✔
73
        print_text_line("{0}{1} {2}".format(timestamp, log_level, formatted_message))
1✔
74

75

76
class ColoredStdOutLogger(StdOutLogger):
1✔
77
    def _level_to_string(self, level):
1✔
78
        if Logger.DEBUG == level:
1✔
79
            return italic("[DEBUG]")
1✔
80
        if Logger.INFO == level:
1✔
81
            return bold("[INFO] ")
1✔
82
        if Logger.WARN == level:
1✔
83
            return styled_text("[WARN] ", BOLD, fg(BROWN))
1✔
84
        return styled_text("[ERROR]", BOLD, fg(RED))
1✔
85

86

87
class StdErrLogger(StdOutLogger):
1✔
88
    def _do_log(self, level, message, *arguments):
1✔
89
        formatted_message = self._format_message(message, *arguments)
1✔
90
        log_level = self._level_to_string(level)
1✔
91
        if self.log_time_format is not None:
1!
NEW
92
            timestamp = datetime.datetime.now().strftime(self.log_time_format) + ' '
×
93
        else:
94
            timestamp = ''
1✔
95
        print_error_line("{0}{1} {2}".format(timestamp, log_level, formatted_message))
1✔
96

97

98
class ColoredStdErrLogger(ColoredStdOutLogger, StdErrLogger):
1✔
99
    pass
1✔
100

101

102
def _log_time_format_argument__check_if_timestamp_passed(option, opt_str, value, parser):
1✔
103
    assert value is None
1✔
104
    value = DEFAULT_LOG_TIME_FORMAT
1✔
105

106
    for arg in parser.rargs:
1✔
107
        # stop on --foo like options
108
        if arg[:2] == "--" and len(arg) > 2:
1✔
109
            break
1✔
110
        # stop on -foo like options
111
        elif arg[:1] == "-" and len(arg) > 1:
1✔
112
            break
1✔
113
        else:
114
            value = arg.rstrip()
1✔
115
            del parser.rargs[0]
1✔
116
            break  # Only consume 1 argument
1✔
117

118
    setattr(parser.values, option.dest, value)
1✔
119

120

121
def parse_options(args):
1✔
122
    parser = optparse.OptionParser(usage="%prog [options] [+|^]task1 [[[+|^]task2] ...]",
1✔
123
                                   version="%prog " + __version__)
124

125
    def error(msg):
1✔
126
        raise CommandLineUsageException(
1✔
127
            parser.get_usage() + parser.format_option_help(), msg)
128

129
    parser.error = error
1✔
130

131
    list_tasks_option = parser.add_option("-t", "--list-tasks",
1✔
132
                                          action="store_true",
133
                                          dest="list_tasks",
134
                                          default=False,
135
                                          help="List all tasks that can be run in the current build configuration")
136

137
    list_plan_tasks_option = parser.add_option("-T", "--list-plan-tasks",
1✔
138
                                               action="store_true",
139
                                               dest="list_plan_tasks",
140
                                               default=False,
141
                                               help="List tasks that will be run with current execution plan")
142

143
    project_info_option = parser.add_option("-i", "--project-info",
1✔
144
                                            action="store_true",
145
                                            dest="project_info",
146
                                            default=False,
147
                                            help="Output project configuration as JSON")
148

149
    start_project_option = parser.add_option("--start-project",
1✔
150
                                             action="store_true",
151
                                             dest="start_project",
152
                                             default=False,
153
                                             help="Initialize build descriptors and Python project structure")
154

155
    update_project_option = parser.add_option("--update-project",
1✔
156
                                              action="store_true",
157
                                              dest="update_project",
158
                                              default=False,
159
                                              help="Update build descriptors and Python project structure")
160

161
    project_group = optparse.OptionGroup(
1✔
162
        parser, "Project Options", "Customizes the project to build.")
163

164
    project_group.add_option("-D", "--project-directory",
1✔
165
                             dest="project_directory",
166
                             help="Root directory to execute in",
167
                             metavar="<project directory>",
168
                             default=".")
169

170
    project_group.add_option("-O", "--offline",
1✔
171
                             dest="offline",
172
                             help="Attempt to execute the build without network connectivity (may cause build failure)",
173
                             default=False,
174
                             action="store_true")
175

176
    project_group.add_option("-E", "--environment",
1✔
177
                             dest="environments",
178
                             help="Activate the given environment for this build. Can be used multiple times",
179
                             metavar="<environment>",
180
                             action="append",
181
                             default=[])
182

183
    project_group.add_option("-P",
1✔
184
                             action="append",
185
                             dest="property_overrides",
186
                             default=[],
187
                             metavar="<property>=<value>",
188
                             help="Set/ override a property value")
189

190
    project_group.add_option("-x", "--exclude",
1✔
191
                             action="append",
192
                             dest="exclude_optional_tasks",
193
                             default=[],
194
                             metavar="<task>",
195
                             help="Exclude optional task dependencies")
196

197
    project_group.add_option("-o", "--exclude-all-optional",
1✔
198
                             action="store_true",
199
                             dest="exclude_all_optional",
200
                             default=False,
201
                             help="Exclude all optional task dependencies")
202

203
    project_group.add_option("--force-exclude",
1✔
204
                             action="append",
205
                             dest="exclude_tasks",
206
                             default=[],
207
                             metavar="<task>",
208
                             help="Exclude any task dependencies "
209
                                  "(dangerous, may break the build in unexpected ways)")
210

211
    project_group.add_option("--reset-plugins",
1✔
212
                             action="store_true",
213
                             dest="reset_plugins",
214
                             default=False,
215
                             help="Reset plugins directory prior to running the build")
216

217
    project_group.add_option("--no-venvs",
1✔
218
                             action="store_true",
219
                             dest="no_venvs",
220
                             default=False,
221
                             help="Disables the use of Python Virtual Environments")
222

223
    parser.add_option_group(project_group)
1✔
224

225
    output_group = optparse.OptionGroup(
1✔
226
        parser, "Output Options", "Modifies the messages printed during a build.")
227

228
    output_group.add_option("-X", "--debug",
1✔
229
                            action="store_true",
230
                            dest="debug",
231
                            default=False,
232
                            help="Print debug messages")
233

234
    output_group.add_option("-v", "--verbose",
1✔
235
                            action="store_true",
236
                            dest="verbose",
237
                            default=False,
238
                            help="Enable verbose output")
239

240
    output_group.add_option("-q", "--quiet",
1✔
241
                            action="store_true",
242
                            dest="quiet",
243
                            default=False,
244
                            help="Quiet mode; print only warnings and errors")
245

246
    output_group.add_option("-Q", "--very-quiet",
1✔
247
                            action="store_true",
248
                            dest="very_quiet",
249
                            default=False,
250
                            help="Very quiet mode; print only errors")
251

252
    output_group.add_option("-c", "--color",
1✔
253
                            action="store_true",
254
                            dest="force_color",
255
                            default=False,
256
                            help="Force colored output")
257

258
    output_group.add_option("-C", "--no-color",
1✔
259
                            action="store_true",
260
                            dest="no_color",
261
                            default=False,
262
                            help="Disable colored output")
263

264
    output_group.add_option("-f", "--log-time-format",
1✔
265
                            action='callback',
266
                            callback=_log_time_format_argument__check_if_timestamp_passed,
267
                            dest="log_time_format",
268
                            help="Define the format of timestamp in the log (default: no timestamps)",
269
                            default=None)
270

271
    parser.add_option_group(output_group)
1✔
272

273
    options, arguments = parser.parse_args(args=list(args))
1✔
274

275
    if options.list_tasks and options.list_plan_tasks:
1!
276
        parser.error("%s and %s are mutually exclusive" % (list_tasks_option, list_plan_tasks_option))
×
277
    if options.project_info and (options.list_tasks or options.list_plan_tasks):
1✔
278
        parser.error("%s is mutually exclusive with %s and %s" % (
1✔
279
            project_info_option, list_tasks_option, list_plan_tasks_option))
280
    if options.project_info and (options.start_project or options.update_project):
1✔
281
        parser.error("%s is mutually exclusive with %s and %s" % (
1✔
282
            project_info_option, start_project_option, update_project_option))
283
    if options.start_project and options.update_project:
1!
284
        parser.error("%s and %s are mutually exclusive" % (start_project_option, update_project_option))
×
285

286
    property_overrides = {}
1✔
287
    for pair in options.property_overrides:
1✔
288
        if not PROPERTY_OVERRIDE_PATTERN.match(pair):
1✔
289
            parser.error("%s is not a property definition." % pair)
1✔
290
        key, val = pair.split("=", 1)
1✔
291
        property_overrides[key] = val
1✔
292

293
    options.property_overrides = property_overrides
1✔
294

295
    if options.very_quiet:
1!
296
        options.quiet = True
×
297

298
    return options, arguments
1✔
299

300

301
def init_reactor(logger):
1✔
302
    execution_manager = ExecutionManager(logger)
1✔
303
    reactor = Reactor(logger, execution_manager)
1✔
304
    return reactor
1✔
305

306

307
def should_colorize(options):
1✔
308
    return options.force_color or (sys.stdout.isatty() and not options.no_color)
1✔
309

310

311
def init_logger(options, use_stderr=False):
1✔
312
    threshold = Logger.INFO
1✔
313
    if options.debug:
1!
314
        threshold = Logger.DEBUG
1✔
315
    elif options.quiet:
×
316
        threshold = Logger.WARN
×
317

318
    if not should_colorize(options):
1!
319
        if use_stderr:
1!
NEW
320
            logger = StdErrLogger(threshold, options.log_time_format)
×
321
        else:
322
            logger = StdOutLogger(threshold, options.log_time_format)
1✔
323
    else:
324
        if IS_WIN:
×
325
            import colorama
×
326
            colorama.init()
×
NEW
327
        if use_stderr:
×
NEW
328
            logger = ColoredStdErrLogger(threshold, options.log_time_format)
×
329
        else:
NEW
330
            logger = ColoredStdOutLogger(threshold, options.log_time_format)
×
331

332
    return logger
1✔
333

334

335
def print_build_summary(options, summary):
1✔
336
    print_text_line("Build Summary")
1✔
337
    print_text_line("%20s: %s" % ("Project", summary.project.name))
1✔
338
    print_text_line("%20s: %s%s" % ("Version", summary.project.version, get_dist_version_string(summary.project)))
1✔
339
    print_text_line("%20s: %s" % ("Base directory", summary.project.basedir))
1✔
340
    print_text_line("%20s: %s" %
1✔
341
                    ("Environments", ", ".join(options.environments)))
342

343
    task_summary = ""
1✔
344
    for task in summary.task_summaries:
1✔
345
        task_summary += " %s [%d ms]" % (task.task, task.execution_time)
1✔
346

347
    print_text_line("%20s:%s" % ("Tasks", task_summary))
1✔
348

349

350
def print_styled_text(text, options, *style_attributes):
1✔
351
    if should_colorize(options):
1!
352
        add_trailing_nl = False
×
353
        if text[-1] == '\n':
×
354
            text = text[:-1]
×
355
            add_trailing_nl = True
×
356
        text = styled_text(text, *style_attributes)
×
357
        if add_trailing_nl:
×
358
            text += '\n'
×
359
    print_text(text)
1✔
360

361

362
def print_styled_text_line(text, options, *style_attributes):
1✔
363
    print_styled_text(text + "\n", options, *style_attributes)
1✔
364

365

366
def print_build_status(failure_message, options, successful):
1✔
367
    draw_line()
1✔
368
    if successful:
1!
369
        print_styled_text_line("BUILD SUCCESSFUL", options, BOLD, fg(GREEN))
1✔
370
    else:
371
        print_styled_text_line(
×
372
            "BUILD FAILED - {0}".format(failure_message), options, BOLD, fg(RED))
373
    draw_line()
1✔
374

375

376
def print_elapsed_time_summary(start, end):
1✔
377
    time_needed = end - start
1✔
378
    millis = ((time_needed.days * 24 * 60 * 60) + time_needed.seconds) * 1000 + time_needed.microseconds / 1000
1✔
379
    print_text_line("Build finished at %s" % format_timestamp(end))
1✔
380
    print_text_line("Build took %d seconds (%d ms)" %
1✔
381
                    (time_needed.seconds, millis))
382

383

384
def print_summary(successful, summary, start, end, options, failure_message):
1✔
385
    print_build_status(failure_message, options, successful)
1✔
386

387
    if successful and summary:
1!
388
        print_build_summary(options, summary)
1✔
389

390
    print_elapsed_time_summary(start, end)
1✔
391

392

393
def length_of_longest_string(list_of_strings):
1✔
394
    if len(list_of_strings) == 0:
1✔
395
        return 0
1✔
396

397
    result = 0
1✔
398
    for string in list_of_strings:
1✔
399
        length_of_string = len(string)
1✔
400
        if length_of_string > result:
1✔
401
            result = length_of_string
1✔
402

403
    return result
1✔
404

405

406
def task_description(task):
1✔
407
    return " ".join(task.description) or "<no description available>"
1✔
408

409

410
def print_task_list(tasks, quiet=False):
1✔
411
    if quiet:
1✔
412
        print_text_line("\n".join([task.name + ":" + task_description(task)
1✔
413
                                   for task in tasks]))
414
        return
1✔
415

416
    column_length = length_of_longest_string(
1✔
417
        list(map(lambda task: task.name, tasks)))
418
    column_length += 4
1✔
419

420
    for task in tasks:
1✔
421
        task_name = task.name.rjust(column_length)
1✔
422
        print_text_line("{0} - {1}".format(task_name, task_description(task)))
1✔
423

424
        if task.dependencies:
1✔
425
            whitespace = (column_length + 3) * " "
1✔
426
            depends_on_message = "depends on tasks: %s" % " ".join(
1✔
427
                [str(dependency) for dependency in task.dependencies])
428
            print_text_line(whitespace + depends_on_message)
1✔
429

430

431
def print_list_of_tasks(reactor, quiet=False):
1✔
432
    tasks = reactor.get_tasks()
1✔
433
    sorted_tasks = sorted(tasks)
1✔
434
    if not quiet:
1✔
435
        print_text_line('Tasks found for project "%s":' % reactor.project.name)
1✔
436
    print_task_list(sorted_tasks, quiet)
1✔
437

438

439
def print_plan_list_of_tasks(options, arguments, reactor, quiet=False):
1✔
440
    execution_plan = reactor.create_execution_plan(arguments, options.environments)
×
441
    if not quiet:
×
442
        print_text_line('Tasks that will be executed for project "%s":' % reactor.project.name)
×
443
    print_task_list(execution_plan, quiet)
×
444

445

446
def _serialize_dependency(dep):
1✔
447
    from pybuilder.core import RequirementsFile
1✔
448
    if isinstance(dep, RequirementsFile):
1✔
449
        return {
1✔
450
            "name": dep.name,
451
            "version": None,
452
            "declaration_only": dep.declaration_only,
453
            "type": "requirements_file"
454
        }
455
    return {
1✔
456
        "name": dep.name,
457
        "version": dep.version,
458
        "url": dep.url,
459
        "extras": dep.extras,
460
        "markers": dep.markers,
461
        "declaration_only": dep.declaration_only,
462
        "type": "dependency"
463
    }
464

465

466
def _safe_property_value(value):
1✔
467
    if value is None or isinstance(value, (bool, int, float, str)):
1✔
468
        return value
1✔
469
    if isinstance(value, (list, tuple)):
1✔
470
        return [_safe_property_value(v) for v in value]
1✔
471
    if isinstance(value, dict):
1✔
472
        return {str(k): _safe_property_value(v) for k, v in value.items()}
1✔
473
    if isinstance(value, set):
1✔
474
        return sorted([_safe_property_value(v) for v in value], key=str)
1✔
475
    return repr(value)
1✔
476

477

478
def _build_project_info(reactor):
1✔
479
    project = reactor.project
1✔
480

481
    def serialize_person(p):
1✔
482
        if isinstance(p, (dict, str)):
1!
483
            return p
1✔
NEW
484
        result = {"name": p.name}
×
NEW
485
        if p.email:
×
NEW
486
            result["email"] = p.email
×
NEW
487
        if p.roles:
×
NEW
488
            result["roles"] = list(p.roles)
×
NEW
489
        return result
×
490

491
    def serialize_person_list(persons):
1✔
492
        return [serialize_person(p) for p in persons]
1✔
493

494
    tasks_info = []
1✔
495
    for task in sorted(reactor.get_tasks()):
1✔
496
        task_deps = []
1✔
497
        for dep in task.dependencies:
1✔
498
            task_deps.append({
1✔
499
                "name": str(dep.name),
500
                "optional": dep.optional
501
            })
502
        tasks_info.append({
1✔
503
            "name": task.name,
504
            "description": task_description(task),
505
            "dependencies": task_deps
506
        })
507

508
    return {
1✔
509
        "pybuilder_version": __version__,
510
        "project": {
511
            "name": project.name,
512
            "version": project.version,
513
            "dist_version": project.dist_version,
514
            "basedir": project.basedir,
515
            "summary": project.summary,
516
            "description": project.description,
517
            "author": project.author,
518
            "authors": serialize_person_list(project.authors),
519
            "maintainer": project.maintainer,
520
            "maintainers": serialize_person_list(project.maintainers),
521
            "license": project.license,
522
            "url": project.url,
523
            "urls": project.urls,
524
            "requires_python": project.requires_python,
525
            "default_task": project.default_task,
526
            "obsoletes": project.obsoletes,
527
            "explicit_namespaces": project.explicit_namespaces,
528
        },
529
        "environments": list(project.environments),
530
        "properties": {str(k): _safe_property_value(v) for k, v in sorted(project.properties.items())},
531
        "plugins": list(reactor.get_plugins()),
532
        "dependencies": {
533
            "runtime": [_serialize_dependency(d) for d in project.dependencies],
534
            "build": [_serialize_dependency(d) for d in project.build_dependencies],
535
            "plugin": [_serialize_dependency(d) for d in project.plugin_dependencies],
536
            "extras": {name: [_serialize_dependency(d) for d in deps]
537
                       for name, deps in project.extras_dependencies.items()}
538
        },
539
        "tasks": tasks_info,
540
        "manifest_included_files": project.manifest_included_files,
541
        "package_data": dict(project.package_data),
542
        "files_to_install": [[dest, files] for dest, files in project.files_to_install],
543
    }
544

545

546
def print_project_info(reactor, environments):
1✔
547
    reactor.project._environments = tuple(environments)
1✔
548
    reactor.execution_manager.execute_initializers(
1✔
549
        environments, logger=reactor.logger, project=reactor.project, reactor=reactor)
550

551
    info = _build_project_info(reactor)
1✔
552
    print_text_line(json.dumps(info, indent=2, default=str))
1✔
553

554

555
def get_failure_message():
1✔
556
    exc_type, exc_obj, exc_tb = sys.exc_info()
1✔
557

558
    filename = None
1✔
559
    lineno = None
1✔
560

561
    while exc_tb.tb_next:
1!
562
        exc_tb = exc_tb.tb_next
×
563

564
    frame = exc_tb.tb_frame
1✔
565
    if hasattr(frame, "f_code"):
1!
566
        code = frame.f_code
1✔
567
        filename = code.co_filename
1✔
568
        lineno = exc_tb.tb_lineno
1✔
569

570
        filename = nc(filename)
1✔
571
        for path in sys.path:
1!
572
            path = nc(path)
1✔
573
            if filename.startswith(path) and len(filename) > len(path) and filename[len(path)] == sep:
1✔
574
                filename = filename[len(path) + 1:]
1✔
575
                break
1✔
576

577
    return "%s%s%s" % ("%s: " % exc_type.__name__ if not isinstance(exc_obj, PyBuilderException) else "",
1✔
578
                       exc_obj,
579
                       " (%s:%d)" % (filename, lineno) if filename else "")
580

581

582
def main(*args):
1✔
583
    if not args:
1!
584
        args = sys.argv[1:]
×
585
    try:
1✔
586
        options, arguments = parse_options(args)
1✔
587
    except CommandLineUsageException as e:
×
588
        print_error_line("Usage error: %s\n" % e)
×
589
        print_error(e.usage)
×
590
        return 1
×
591

592
    start = datetime.datetime.now()
1✔
593

594
    logger = init_logger(options, use_stderr=options.project_info)
1✔
595
    reactor = init_reactor(logger)
1✔
596

597
    if options.start_project:
1!
598
        return start_project()
×
599

600
    if options.update_project:
1!
601
        return update_project()
×
602

603
    if options.project_info:
1!
NEW
604
        try:
×
NEW
605
            reactor.prepare_build(property_overrides=options.property_overrides,
×
606
                                  project_directory=options.project_directory,
607
                                  exclude_optional_tasks=options.exclude_optional_tasks,
608
                                  exclude_tasks=options.exclude_tasks,
609
                                  exclude_all_optional=options.exclude_all_optional,
610
                                  offline=options.offline,
611
                                  no_venvs=options.no_venvs
612
                                  )
NEW
613
            print_project_info(reactor, options.environments)
×
NEW
614
            return 0
×
NEW
615
        except PyBuilderException:
×
NEW
616
            print_build_status(get_failure_message(), options, successful=False)
×
NEW
617
            return 1
×
618

619
    if options.list_tasks or options.list_plan_tasks:
1!
620
        try:
×
621
            reactor.prepare_build(property_overrides=options.property_overrides,
×
622
                                  project_directory=options.project_directory,
623
                                  exclude_optional_tasks=options.exclude_optional_tasks,
624
                                  exclude_tasks=options.exclude_tasks,
625
                                  exclude_all_optional=options.exclude_all_optional,
626
                                  offline=options.offline,
627
                                  no_venvs=options.no_venvs
628
                                  )
629
            if options.list_tasks:
×
630
                print_list_of_tasks(reactor, quiet=options.very_quiet)
×
631

632
            if options.list_plan_tasks:
×
633
                print_plan_list_of_tasks(options, arguments, reactor, quiet=options.very_quiet)
×
634
            return 0
×
635
        except PyBuilderException:
×
636
            print_build_status(get_failure_message(), options, successful=False)
×
637
            return 1
×
638

639
    if not options.very_quiet:
1!
640
        print_styled_text_line(
1✔
641
            "PyBuilder version {0}".format(__version__), options, BOLD)
642
        print_text_line("Build started at %s" % format_timestamp(start))
1✔
643
        draw_line()
1✔
644

645
    successful = True
1✔
646
    failure_message = None
1✔
647
    summary = None
1✔
648

649
    try:
1✔
650
        try:
1✔
651
            reactor.prepare_build(property_overrides=options.property_overrides,
1✔
652
                                  project_directory=options.project_directory,
653
                                  exclude_optional_tasks=options.exclude_optional_tasks,
654
                                  exclude_tasks=options.exclude_tasks,
655
                                  exclude_all_optional=options.exclude_all_optional,
656
                                  reset_plugins=options.reset_plugins,
657
                                  offline=options.offline,
658
                                  no_venvs=options.no_venvs
659
                                  )
660

661
            if options.verbose or options.debug:
1!
662
                logger.debug("Verbose output enabled.\n")
1✔
663
                reactor.project.set_property("verbose", True)
1✔
664

665
            summary = reactor.build(
1✔
666
                environments=options.environments, tasks=arguments)
667

668
        except KeyboardInterrupt:
×
669
            raise PyBuilderException("Build aborted")
×
670

671
    except (Exception, SystemExit):
×
672
        successful = False
×
673
        failure_message = get_failure_message()
×
674
        if options.debug:
×
675
            traceback.print_exc(file=sys.stderr)
×
676

677
    finally:
678
        end = datetime.datetime.now()
1✔
679
        if not options.very_quiet:
1!
680
            print_summary(
1✔
681
                successful, summary, start, end, options, failure_message)
682

683
    return 0 if successful else 1
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc