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

pybuilder / pybuilder / 23883260620

02 Apr 2026 04:05AM UTC coverage: 82.89%. First build
23883260620

Pull #946

github

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

1427 of 1890 branches covered (75.5%)

Branch coverage included in aggregate %.

56 of 74 new or added lines in 1 file covered. (75.68%)

5612 of 6602 relevant lines covered (85.0%)

29.05 hits per line

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

76.71
/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
35✔
25
import json
35✔
26
import optparse
35✔
27
import re
35✔
28
import sys
35✔
29
import traceback
35✔
30
from os.path import sep, normcase as nc
35✔
31

32
from pybuilder import __version__
35✔
33
from pybuilder import extern
35✔
34
from pybuilder.core import Logger
35✔
35
from pybuilder.errors import PyBuilderException
35✔
36
from pybuilder.execution import ExecutionManager
35✔
37
from pybuilder.python_utils import IS_WIN
35✔
38
from pybuilder.reactor import Reactor
35✔
39
from pybuilder.scaffolding import start_project, update_project
35✔
40
from pybuilder.terminal import (BOLD, BROWN, RED, GREEN, bold, styled_text,
35✔
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
35✔
44

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

49

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

55

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

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

75

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

86

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

97

98
class ColoredStdErrLogger(ColoredStdOutLogger):
35✔
99
    def _do_log(self, level, message, *arguments):
35✔
100
        formatted_message = self._format_message(message, *arguments)
35✔
101
        log_level = self._level_to_string(level)
35✔
102
        if self.log_time_format is not None:
35!
103
            timestamp = datetime.datetime.now().strftime(self.log_time_format) + ' '
×
104
        else:
105
            timestamp = ''
35✔
106
        print_error_line("{0}{1} {2}".format(timestamp, log_level, formatted_message))
35✔
107

108

109
def _log_time_format_argument__check_if_timestamp_passed(option, opt_str, value, parser):
35✔
110
    assert value is None
35✔
111
    value = DEFAULT_LOG_TIME_FORMAT
35✔
112

113
    for arg in parser.rargs:
35✔
114
        # stop on --foo like options
115
        if arg[:2] == "--" and len(arg) > 2:
35✔
116
            break
35✔
117
        # stop on -foo like options
118
        elif arg[:1] == "-" and len(arg) > 1:
35✔
119
            break
35✔
120
        else:
121
            value = arg.rstrip()
35✔
122
            del parser.rargs[0]
35✔
123
            break  # Only consume 1 argument
35✔
124

125
    setattr(parser.values, option.dest, value)
35✔
126

127

128
def parse_options(args):
35✔
129
    parser = optparse.OptionParser(usage="%prog [options] [+|^]task1 [[[+|^]task2] ...]",
35✔
130
                                   version="%prog " + __version__)
131

132
    def error(msg):
35✔
133
        raise CommandLineUsageException(
35✔
134
            parser.get_usage() + parser.format_option_help(), msg)
135

136
    parser.error = error
35✔
137

138
    list_tasks_option = parser.add_option("-t", "--list-tasks",
35✔
139
                                          action="store_true",
140
                                          dest="list_tasks",
141
                                          default=False,
142
                                          help="List all tasks that can be run in the current build configuration")
143

144
    list_plan_tasks_option = parser.add_option("-T", "--list-plan-tasks",
35✔
145
                                               action="store_true",
146
                                               dest="list_plan_tasks",
147
                                               default=False,
148
                                               help="List tasks that will be run with current execution plan")
149

150
    project_info_option = parser.add_option("-i", "--project-info",
35✔
151
                                            action="store_true",
152
                                            dest="project_info",
153
                                            default=False,
154
                                            help="Output project configuration as JSON")
155

156
    start_project_option = parser.add_option("--start-project",
35✔
157
                                             action="store_true",
158
                                             dest="start_project",
159
                                             default=False,
160
                                             help="Initialize build descriptors and Python project structure")
161

162
    update_project_option = parser.add_option("--update-project",
35✔
163
                                              action="store_true",
164
                                              dest="update_project",
165
                                              default=False,
166
                                              help="Update build descriptors and Python project structure")
167

168
    project_group = optparse.OptionGroup(
35✔
169
        parser, "Project Options", "Customizes the project to build.")
170

171
    project_group.add_option("-D", "--project-directory",
35✔
172
                             dest="project_directory",
173
                             help="Root directory to execute in",
174
                             metavar="<project directory>",
175
                             default=".")
176

177
    project_group.add_option("-O", "--offline",
35✔
178
                             dest="offline",
179
                             help="Attempt to execute the build without network connectivity (may cause build failure)",
180
                             default=False,
181
                             action="store_true")
182

183
    project_group.add_option("-E", "--environment",
35✔
184
                             dest="environments",
185
                             help="Activate the given environment for this build. Can be used multiple times",
186
                             metavar="<environment>",
187
                             action="append",
188
                             default=[])
189

190
    project_group.add_option("-P",
35✔
191
                             action="append",
192
                             dest="property_overrides",
193
                             default=[],
194
                             metavar="<property>=<value>",
195
                             help="Set/ override a property value")
196

197
    project_group.add_option("-x", "--exclude",
35✔
198
                             action="append",
199
                             dest="exclude_optional_tasks",
200
                             default=[],
201
                             metavar="<task>",
202
                             help="Exclude optional task dependencies")
203

204
    project_group.add_option("-o", "--exclude-all-optional",
35✔
205
                             action="store_true",
206
                             dest="exclude_all_optional",
207
                             default=False,
208
                             help="Exclude all optional task dependencies")
209

210
    project_group.add_option("--force-exclude",
35✔
211
                             action="append",
212
                             dest="exclude_tasks",
213
                             default=[],
214
                             metavar="<task>",
215
                             help="Exclude any task dependencies "
216
                                  "(dangerous, may break the build in unexpected ways)")
217

218
    project_group.add_option("--reset-plugins",
35✔
219
                             action="store_true",
220
                             dest="reset_plugins",
221
                             default=False,
222
                             help="Reset plugins directory prior to running the build")
223

224
    project_group.add_option("--no-venvs",
35✔
225
                             action="store_true",
226
                             dest="no_venvs",
227
                             default=False,
228
                             help="Disables the use of Python Virtual Environments")
229

230
    parser.add_option_group(project_group)
35✔
231

232
    output_group = optparse.OptionGroup(
35✔
233
        parser, "Output Options", "Modifies the messages printed during a build.")
234

235
    output_group.add_option("-X", "--debug",
35✔
236
                            action="store_true",
237
                            dest="debug",
238
                            default=False,
239
                            help="Print debug messages")
240

241
    output_group.add_option("-v", "--verbose",
35✔
242
                            action="store_true",
243
                            dest="verbose",
244
                            default=False,
245
                            help="Enable verbose output")
246

247
    output_group.add_option("-q", "--quiet",
35✔
248
                            action="store_true",
249
                            dest="quiet",
250
                            default=False,
251
                            help="Quiet mode; print only warnings and errors")
252

253
    output_group.add_option("-Q", "--very-quiet",
35✔
254
                            action="store_true",
255
                            dest="very_quiet",
256
                            default=False,
257
                            help="Very quiet mode; print only errors")
258

259
    output_group.add_option("-c", "--color",
35✔
260
                            action="store_true",
261
                            dest="force_color",
262
                            default=False,
263
                            help="Force colored output")
264

265
    output_group.add_option("-C", "--no-color",
35✔
266
                            action="store_true",
267
                            dest="no_color",
268
                            default=False,
269
                            help="Disable colored output")
270

271
    output_group.add_option("-f", "--log-time-format",
35✔
272
                            action='callback',
273
                            callback=_log_time_format_argument__check_if_timestamp_passed,
274
                            dest="log_time_format",
275
                            help="Define the format of timestamp in the log (default: no timestamps)",
276
                            default=None)
277

278
    parser.add_option_group(output_group)
35✔
279

280
    options, arguments = parser.parse_args(args=list(args))
35✔
281

282
    if options.list_tasks and options.list_plan_tasks:
35!
283
        parser.error("%s and %s are mutually exclusive" % (list_tasks_option, list_plan_tasks_option))
×
284
    if options.project_info and (options.list_tasks or options.list_plan_tasks):
35✔
285
        parser.error("%s is mutually exclusive with %s and %s" % (
35✔
286
            project_info_option, list_tasks_option, list_plan_tasks_option))
287
    if options.project_info and (options.start_project or options.update_project):
35✔
288
        parser.error("%s is mutually exclusive with %s and %s" % (
35✔
289
            project_info_option, start_project_option, update_project_option))
290
    if options.start_project and options.update_project:
35!
291
        parser.error("%s and %s are mutually exclusive" % (start_project_option, update_project_option))
×
292

293
    property_overrides = {}
35✔
294
    for pair in options.property_overrides:
35✔
295
        if not PROPERTY_OVERRIDE_PATTERN.match(pair):
35✔
296
            parser.error("%s is not a property definition." % pair)
35✔
297
        key, val = pair.split("=", 1)
35✔
298
        property_overrides[key] = val
35✔
299

300
    options.property_overrides = property_overrides
35✔
301

302
    if options.very_quiet:
35!
303
        options.quiet = True
×
304

305
    return options, arguments
35✔
306

307

308
def init_reactor(logger):
35✔
309
    execution_manager = ExecutionManager(logger)
22✔
310
    reactor = Reactor(logger, execution_manager)
22✔
311
    return reactor
22✔
312

313

314
def should_colorize(options):
35✔
315
    return options.force_color or (sys.stdout.isatty() and not options.no_color)
22✔
316

317

318
def init_logger(options, use_stderr=False):
35✔
319
    threshold = Logger.INFO
22✔
320
    if options.debug:
22!
321
        threshold = Logger.DEBUG
22✔
NEW
322
    elif options.quiet:
×
323
        threshold = Logger.WARN
×
324

325
    if not should_colorize(options):
22!
326
        if use_stderr:
22!
NEW
327
            logger = StdErrLogger(threshold, options.log_time_format)
×
328
        else:
329
            logger = StdOutLogger(threshold, options.log_time_format)
22✔
330
    else:
331
        if IS_WIN:
×
332
            import colorama
×
333
            colorama.init()
×
334
        if use_stderr:
×
335
            logger = ColoredStdErrLogger(threshold, options.log_time_format)
×
336
        else:
337
            logger = ColoredStdOutLogger(threshold, options.log_time_format)
×
338

339
    return logger
22✔
340

341

342
def print_build_summary(options, summary):
35✔
343
    print_text_line("Build Summary")
22✔
344
    print_text_line("%20s: %s" % ("Project", summary.project.name))
22✔
345
    print_text_line("%20s: %s%s" % ("Version", summary.project.version, get_dist_version_string(summary.project)))
22✔
346
    print_text_line("%20s: %s" % ("Base directory", summary.project.basedir))
22✔
347
    print_text_line("%20s: %s" %
22✔
348
                    ("Environments", ", ".join(options.environments)))
349

350
    task_summary = ""
22✔
351
    for task in summary.task_summaries:
22✔
352
        task_summary += " %s [%d ms]" % (task.task, task.execution_time)
22✔
353

354
    print_text_line("%20s:%s" % ("Tasks", task_summary))
22✔
355

356

357
def print_styled_text(text, options, *style_attributes):
35✔
358
    if should_colorize(options):
22!
359
        add_trailing_nl = False
×
360
        if text[-1] == '\n':
×
361
            text = text[:-1]
×
362
            add_trailing_nl = True
×
363
        text = styled_text(text, *style_attributes)
×
364
        if add_trailing_nl:
×
365
            text += '\n'
×
366
    print_text(text)
22✔
367

368

369
def print_styled_text_line(text, options, *style_attributes):
35✔
370
    print_styled_text(text + "\n", options, *style_attributes)
22✔
371

372

373
def print_build_status(failure_message, options, successful):
35✔
374
    draw_line()
22✔
375
    if successful:
22!
376
        print_styled_text_line("BUILD SUCCESSFUL", options, BOLD, fg(GREEN))
22✔
377
    else:
378
        print_styled_text_line(
×
379
            "BUILD FAILED - {0}".format(failure_message), options, BOLD, fg(RED))
380
    draw_line()
22✔
381

382

383
def print_elapsed_time_summary(start, end):
35✔
384
    time_needed = end - start
22✔
385
    millis = ((time_needed.days * 24 * 60 * 60) + time_needed.seconds) * 1000 + time_needed.microseconds / 1000
22✔
386
    print_text_line("Build finished at %s" % format_timestamp(end))
22✔
387
    print_text_line("Build took %d seconds (%d ms)" %
22✔
388
                    (time_needed.seconds, millis))
389

390

391
def print_summary(successful, summary, start, end, options, failure_message):
35✔
392
    print_build_status(failure_message, options, successful)
22✔
393

394
    if successful and summary:
22!
395
        print_build_summary(options, summary)
22✔
396

397
    print_elapsed_time_summary(start, end)
22✔
398

399

400
def length_of_longest_string(list_of_strings):
35✔
401
    if len(list_of_strings) == 0:
35✔
402
        return 0
35✔
403

404
    result = 0
35✔
405
    for string in list_of_strings:
35✔
406
        length_of_string = len(string)
35✔
407
        if length_of_string > result:
35✔
408
            result = length_of_string
35✔
409

410
    return result
35✔
411

412

413
def task_description(task):
35✔
414
    return " ".join(task.description) or "<no description available>"
35✔
415

416

417
def print_task_list(tasks, quiet=False):
35✔
418
    if quiet:
35✔
419
        print_text_line("\n".join([task.name + ":" + task_description(task)
35✔
420
                                   for task in tasks]))
421
        return
35✔
422

423
    column_length = length_of_longest_string(
35✔
424
        list(map(lambda task: task.name, tasks)))
425
    column_length += 4
35✔
426

427
    for task in tasks:
35✔
428
        task_name = task.name.rjust(column_length)
35✔
429
        print_text_line("{0} - {1}".format(task_name, task_description(task)))
35✔
430

431
        if task.dependencies:
35✔
432
            whitespace = (column_length + 3) * " "
35✔
433
            depends_on_message = "depends on tasks: %s" % " ".join(
35✔
434
                [str(dependency) for dependency in task.dependencies])
435
            print_text_line(whitespace + depends_on_message)
35✔
436

437

438
def print_list_of_tasks(reactor, quiet=False):
35✔
439
    tasks = reactor.get_tasks()
35✔
440
    sorted_tasks = sorted(tasks)
35✔
441
    if not quiet:
35✔
442
        print_text_line('Tasks found for project "%s":' % reactor.project.name)
35✔
443
    print_task_list(sorted_tasks, quiet)
35✔
444

445

446
def print_plan_list_of_tasks(options, arguments, reactor, quiet=False):
35✔
NEW
447
    execution_plan = reactor.create_execution_plan(arguments, options.environments)
×
NEW
448
    if not quiet:
×
NEW
449
        print_text_line('Tasks that will be executed for project "%s":' % reactor.project.name)
×
NEW
450
    print_task_list(execution_plan, quiet)
×
451

452

453
def _serialize_dependency(dep):
35✔
454
    from pybuilder.core import RequirementsFile
35✔
455
    if isinstance(dep, RequirementsFile):
35✔
456
        return {
35✔
457
            "name": dep.name,
458
            "version": None,
459
            "declaration_only": dep.declaration_only,
460
            "type": "requirements_file"
461
        }
462
    return {
35✔
463
        "name": dep.name,
464
        "version": dep.version,
465
        "url": dep.url,
466
        "extras": dep.extras,
467
        "markers": dep.markers,
468
        "declaration_only": dep.declaration_only,
469
        "type": "dependency"
470
    }
471

472

473
def _safe_property_value(value):
35✔
474
    if value is None or isinstance(value, (bool, int, float, str)):
35✔
475
        return value
35✔
476
    if isinstance(value, (list, tuple)):
35✔
477
        return [_safe_property_value(v) for v in value]
35✔
478
    if isinstance(value, dict):
35✔
479
        return {str(k): _safe_property_value(v) for k, v in value.items()}
35✔
480
    if isinstance(value, set):
35✔
481
        return sorted([_safe_property_value(v) for v in value], key=str)
35✔
482
    return repr(value)
35✔
483

484

485
def _build_project_info(reactor):
35✔
486
    project = reactor.project
35✔
487

488
    def serialize_person(p):
35✔
489
        if isinstance(p, (dict, str)):
35!
490
            return p
35✔
NEW
491
        result = {"name": p.name} if hasattr(p, 'name') else {"name": str(p)}
×
NEW
492
        if hasattr(p, 'email') and p.email:
×
NEW
493
            result["email"] = p.email
×
NEW
494
        if hasattr(p, 'roles') and p.roles:
×
NEW
495
            result["roles"] = list(p.roles)
×
NEW
496
        return result
×
497

498
    def serialize_person_list(persons):
35✔
499
        return [serialize_person(p) for p in persons]
35✔
500

501
    tasks_info = []
35✔
502
    for task in sorted(reactor.get_tasks()):
35✔
503
        task_deps = []
35✔
504
        for dep in task.dependencies:
35✔
505
            task_deps.append({
35✔
506
                "name": str(dep.name) if hasattr(dep, 'name') else str(dep),
507
                "optional": dep.optional if hasattr(dep, 'optional') else False
508
            })
509
        tasks_info.append({
35✔
510
            "name": task.name,
511
            "description": " ".join(task.description) if task.description else "",
512
            "dependencies": task_deps
513
        })
514

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

552

553
def print_project_info(reactor, environments):
35✔
554
    reactor.project._environments = tuple(environments)
35✔
555
    reactor.execution_manager.execute_initializers(
35✔
556
        environments, logger=reactor.logger, project=reactor.project, reactor=reactor)
557

558
    info = _build_project_info(reactor)
35✔
559
    print_text_line(json.dumps(info, indent=2, default=str))
35✔
560

561

562
def get_failure_message():
35✔
563
    exc_type, exc_obj, exc_tb = sys.exc_info()
35✔
564

565
    filename = None
35✔
566
    lineno = None
35✔
567

568
    while exc_tb.tb_next:
35!
569
        exc_tb = exc_tb.tb_next
×
570

571
    frame = exc_tb.tb_frame
35✔
572
    if hasattr(frame, "f_code"):
35!
573
        code = frame.f_code
35✔
574
        filename = code.co_filename
35✔
575
        lineno = exc_tb.tb_lineno
35✔
576

577
        filename = nc(filename)
35✔
578
        for path in sys.path:
35!
579
            path = nc(path)
35✔
580
            if filename.startswith(path) and len(filename) > len(path) and filename[len(path)] == sep:
35✔
581
                filename = filename[len(path) + 1:]
35✔
582
                break
35✔
583

584
    return "%s%s%s" % ("%s: " % exc_type.__name__ if not isinstance(exc_obj, PyBuilderException) else "",
35✔
585
                       exc_obj,
586
                       " (%s:%d)" % (filename, lineno) if filename else "")
587

588

589
def main(*args):
35✔
590
    if not args:
22!
591
        args = sys.argv[1:]
×
592
    try:
22✔
593
        options, arguments = parse_options(args)
22✔
NEW
594
    except CommandLineUsageException as e:
×
595
        print_error_line("Usage error: %s\n" % e)
×
596
        print_error(e.usage)
×
597
        return 1
×
598

599
    start = datetime.datetime.now()
22✔
600

601
    logger = init_logger(options, use_stderr=options.project_info)
22✔
602
    reactor = init_reactor(logger)
22✔
603

604
    if options.start_project:
22!
NEW
605
        return start_project()
×
606

607
    if options.update_project:
22!
NEW
608
        return update_project()
×
609

610
    if options.project_info:
22!
NEW
611
        try:
×
NEW
612
            reactor.prepare_build(property_overrides=options.property_overrides,
×
613
                                  project_directory=options.project_directory,
614
                                  exclude_optional_tasks=options.exclude_optional_tasks,
615
                                  exclude_tasks=options.exclude_tasks,
616
                                  exclude_all_optional=options.exclude_all_optional,
617
                                  offline=options.offline,
618
                                  no_venvs=options.no_venvs
619
                                  )
620
            print_project_info(reactor, options.environments)
×
621
            return 0
×
622
        except PyBuilderException:
×
623
            print_build_status(get_failure_message(), options, successful=False)
×
624
            return 1
×
625

626
    if options.list_tasks or options.list_plan_tasks:
22!
627
        try:
×
628
            reactor.prepare_build(property_overrides=options.property_overrides,
×
629
                                  project_directory=options.project_directory,
630
                                  exclude_optional_tasks=options.exclude_optional_tasks,
631
                                  exclude_tasks=options.exclude_tasks,
632
                                  exclude_all_optional=options.exclude_all_optional,
633
                                  offline=options.offline,
634
                                  no_venvs=options.no_venvs
635
                                  )
636
            if options.list_tasks:
×
637
                print_list_of_tasks(reactor, quiet=options.very_quiet)
×
638

639
            if options.list_plan_tasks:
×
640
                print_plan_list_of_tasks(options, arguments, reactor, quiet=options.very_quiet)
×
641
            return 0
×
642
        except PyBuilderException:
×
643
            print_build_status(get_failure_message(), options, successful=False)
×
644
            return 1
×
645

646
    if not options.very_quiet:
22!
647
        print_styled_text_line(
22✔
648
            "PyBuilder version {0}".format(__version__), options, BOLD)
649
        print_text_line("Build started at %s" % format_timestamp(start))
22✔
650
        draw_line()
22✔
651

652
    successful = True
22✔
653
    failure_message = None
22✔
654
    summary = None
22✔
655

656
    try:
22✔
657
        try:
22✔
658
            reactor.prepare_build(property_overrides=options.property_overrides,
22✔
659
                                  project_directory=options.project_directory,
660
                                  exclude_optional_tasks=options.exclude_optional_tasks,
661
                                  exclude_tasks=options.exclude_tasks,
662
                                  exclude_all_optional=options.exclude_all_optional,
663
                                  reset_plugins=options.reset_plugins,
664
                                  offline=options.offline,
665
                                  no_venvs=options.no_venvs
666
                                  )
667

668
            if options.verbose or options.debug:
22!
669
                logger.debug("Verbose output enabled.\n")
22✔
670
                reactor.project.set_property("verbose", True)
22✔
671

672
            summary = reactor.build(
22✔
673
                environments=options.environments, tasks=arguments)
674

675
        except KeyboardInterrupt:
×
676
            raise PyBuilderException("Build aborted")
×
677

678
    except (Exception, SystemExit):
×
679
        successful = False
×
680
        failure_message = get_failure_message()
×
681
        if options.debug:
×
682
            traceback.print_exc(file=sys.stderr)
×
683

684
    finally:
685
        end = datetime.datetime.now()
22✔
686
        if not options.very_quiet:
22!
687
            print_summary(
22✔
688
                successful, summary, start, end, options, failure_message)
689

690
    return 0 if successful else 1
22✔
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