Skip to content

Commit

Permalink
A mixin for v2 goals that have non-line-oriented output. (#8565)
Browse files Browse the repository at this point in the history
The LineOriented mixin is reimplemented as a subclass.

Also detangles print_stderr from this. Callers can access that
directly at console.print_stderr.
  • Loading branch information
benjyw authored Nov 6, 2019
1 parent 6ab1504 commit d40f13f
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 29 deletions.
69 changes: 45 additions & 24 deletions src/python/pants/engine/goal.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,42 +109,63 @@ class _GoalOptions(object):
"""A marker trait for the anonymous inner `Goal.Options` classes for `Goal`s."""


class LineOriented:
"""A mixin for Goal that adds Options to support the `line_oriented` context manager."""
class Outputting:
"""A mixin for Goal that adds options to support output-related context managers.
Allows output to go to a file or to stdout.
Useful for goals whose purpose is to emit output to the end user (as distinct from incidental logging to stderr).
"""

@classmethod
def register_options(cls, register):
super().register_options(register)
register('--sep', default='\\n', metavar='<separator>',
help='String to use to separate result lines.')
register('--output-file', metavar='<path>',
help='Write line-oriented output to this file instead.')
help='Output to this file. If unspecified, outputs to stdout.')

@classmethod
@contextmanager
def line_oriented(cls, line_oriented_options, console):
"""Given Goal.Options and a Console, yields functions for writing to stdout and stderr, respectively.
def output(cls, options, console):
"""Given Goal.Options and a Console, yields a function for writing data to stdout, or a file.
The passed options instance will generally be the `Goal.Options` of a `LineOriented` `Goal`.
The passed options instance will generally be the `Goal.Options` of an `Outputting` `Goal`.
"""
if type(line_oriented_options) != cls.Options:
raise AssertionError(
'Expected Options for `{}`, got: {}'.format(cls.__name__, line_oriented_options))

output_file = line_oriented_options.values.output_file
sep = line_oriented_options.values.sep.encode().decode('unicode_escape')
with cls.output_sink(options, console) as output_sink:
yield lambda msg: output_sink.write(msg)

if output_file:
stdout_file = open(output_file, 'w')
print_stdout = lambda msg: print(msg, file=stdout_file, end=sep)
@classmethod
@contextmanager
def output_sink(cls, options, console):
if type(options) != cls.Options:
raise AssertionError('Expected Options for `{}`, got: {}'.format(cls.__name__, options))
stdout_file = None
if options.values.output_file:
stdout_file = open(options.values.output_file, 'w')
output_sink = stdout_file
else:
print_stdout = lambda msg: console.print_stdout(msg, end=sep)

print_stderr = lambda msg: console.print_stderr(msg)

output_sink = console.stdout
try:
yield print_stdout, print_stderr
yield output_sink
finally:
if output_file:
output_sink.flush()
if stdout_file:
stdout_file.close()
console.flush()


class LineOriented(Outputting):
@classmethod
def register_options(cls, register):
super().register_options(register)
register('--sep', default='\\n', metavar='<separator>',
help='String to use to separate lines in line-oriented output.')

@classmethod
@contextmanager
def line_oriented(cls, options, console):
"""Given Goal.Options and a Console, yields a function for printing lines to stdout or a file.
The passed options instance will generally be the `Goal.Options` of an `Outputting` `Goal`.
"""
sep = options.values.sep.encode().decode('unicode_escape')
with cls.output_sink(options, console) as output_sink:
yield lambda msg: print(msg, file=output_sink, end=sep)
14 changes: 14 additions & 0 deletions src/python/pants/engine/goal_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright 2019 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

from pants.engine.goal import Outputting
from pants_test.engine.util import MockConsole


def test_outputting_goal():
class DummyGoal(Outputting):
pass

console = MockConsole()
with DummyGoal.output(None, console) as output:
pass
2 changes: 1 addition & 1 deletion src/python/pants/rules/core/binary.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class CreatedBinary:

@console_rule
def create_binary(addresses: BuildFileAddresses, console: Console, workspace: Workspace, options: Binary.Options) -> Binary:
with Binary.line_oriented(options, console) as (print_stdout, print_stderr):
with Binary.line_oriented(options, console) as print_stdout:
print_stdout("Generating binaries in `dist/`")
binaries = yield [Get(CreatedBinary, Address, address.to_address()) for address in addresses]
dirs_to_materialize = tuple(
Expand Down
2 changes: 1 addition & 1 deletion src/python/pants/rules/core/filedeps.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def file_deps(
if hasattr(hydrated_target.adaptor, "sources"):
uniq_set.update(hydrated_target.adaptor.sources.snapshot.files)

with Filedeps.line_oriented(filedeps_options, console) as (print_stdout, print_stderr):
with Filedeps.line_oriented(filedeps_options, console) as print_stdout:
for f_path in uniq_set:
print_stdout(f_path)

Expand Down
2 changes: 1 addition & 1 deletion src/python/pants/rules/core/list_roots.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def all_roots(source_root_config: SourceRootConfig) -> AllSourceRoots:

@console_rule
def list_roots(console: Console, options: Roots.Options, all_roots: AllSourceRoots) -> Roots:
with Roots.line_oriented(options, console) as (print_stdout, print_stderr):
with Roots.line_oriented(options, console) as print_stdout:
for src_root in sorted(all_roots, key=lambda x: x.path):
all_langs = ','.join(sorted(src_root.langs))
print_stdout(f"{src_root.path}: {all_langs or '*'}")
Expand Down
4 changes: 2 additions & 2 deletions src/python/pants/rules/core/list_targets.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ def print_documented(target):
collection = yield Get(BuildFileAddresses, Specs, specs)
print_fn = lambda address: address.spec

with List.line_oriented(list_options, console) as (print_stdout, print_stderr):
with List.line_oriented(list_options, console) as print_stdout:
if not collection.dependencies:
print_stderr('WARNING: No targets were matched in goal `{}`.'.format('list'))
console.print_stderr('WARNING: No targets were matched in goal `{}`.'.format('list'))

for item in collection:
result = print_fn(item)
Expand Down

0 comments on commit d40f13f

Please sign in to comment.