summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTristan Van Berkom <tristan.vanberkom@codethink.co.uk>2018-04-15 18:55:59 +0900
committerTristan Van Berkom <tristan.vanberkom@codethink.co.uk>2018-04-15 19:04:07 +0900
commitf6f4738c43f13ab52148c355a7705c1c3faeae28 (patch)
treeec84af447e65304cdfd6d2996fce9811e89f8e56
parentd41940f516498f827967b8e5d311ce6accb88f56 (diff)
downloadbuildstream-f6f4738c43f13ab52148c355a7705c1c3faeae28.tar.gz
_frontend/app.py: Adhere to policy on private symbols
This is a part of issue #285
-rw-r--r--buildstream/_frontend/app.py411
-rw-r--r--tests/frontend/main.py10
2 files changed, 226 insertions, 195 deletions
diff --git a/buildstream/_frontend/app.py b/buildstream/_frontend/app.py
index 4d94ff9fa..653a87009 100644
--- a/buildstream/_frontend/app.py
+++ b/buildstream/_frontend/app.py
@@ -52,55 +52,68 @@ from . import Profile, LogLine, Status
INDENT = 4
-##################################################################
-# Main Application State #
-##################################################################
+# App()
+#
+# Main Application State
+#
+# Args:
+# main_options (dict): The main CLI options of the `bst`
+# command, before any subcommand
+#
class App():
def __init__(self, main_options):
- # Snapshot the start time of the session at the earliest opportunity,
- # this is used for inclusive session time logging
- self.session_start = datetime.datetime.now()
-
- self.main_options = main_options
- self.logger = None
- self.status = None
- self.target = None
-
- # Main asset handles
- self.context = None
- self.project = None
- self.scheduler = None
- self.pipeline = None
+ #
+ # Public members
+ #
+ self.context = None # The Context object
+ self.project = None # The toplevel Project object
+ self.scheduler = None # The Scheduler
+ self.pipeline = None # The Pipeline
+ self.logger = None # The LogLine object
+ self.interactive = None # Whether we are running in interactive mode
+ self.colors = None # Whether to use colors in logging
- # Failure messages, hashed by unique plugin id
- self.fail_messages = {}
+ #
+ # Private members
+ #
+ self._session_start = datetime.datetime.now()
+ self._main_options = main_options # Main CLI options, before any command
+ self._status = None # The Status object
+ self._fail_messages = {} # Failure messages by unique plugin id
+ self._interactive_failures = None # Whether to handle failures interactively
# UI Colors Profiles
- self.content_profile = Profile(fg='yellow')
- self.format_profile = Profile(fg='cyan', dim=True)
- self.success_profile = Profile(fg='green')
- self.error_profile = Profile(fg='red', dim=True)
- self.detail_profile = Profile(dim=True)
+ self._content_profile = Profile(fg='yellow')
+ self._format_profile = Profile(fg='cyan', dim=True)
+ self._success_profile = Profile(fg='green')
+ self._error_profile = Profile(fg='red', dim=True)
+ self._detail_profile = Profile(dim=True)
- # Check if we are connected to a tty
- self.is_a_tty = Terminal().is_a_tty
+ #
+ # Earily initialization
+ #
+ is_a_tty = Terminal().is_a_tty
- # Figure out interactive mode
- if self.main_options['no_interactive']:
+ # Enable interactive mode if we're attached to a tty
+ if main_options['no_interactive']:
self.interactive = False
else:
- self.interactive = self.is_a_tty
+ self.interactive = is_a_tty
- # Whether we handle failures interactively
- # defaults to whether we are interactive or not.
- self.interactive_failures = self.interactive
+ # Handle errors interactively if we're in interactive mode
+ # and --on-error was not specified on the command line
+ if main_options.get('on_error') is not None:
+ self._interactive_failures = False
+ else:
+ self._interactive_failures = self.interactive
- # Resolve whether to use colors in output
- if self.main_options['colors'] is None:
- self.colors = self.is_a_tty
- elif self.main_options['colors']:
+ # Use color output if we're attached to a tty, unless
+ # otherwise specified on the comand line
+ if main_options['colors'] is None:
+ self.colors = is_a_tty
+ elif main_options['colors']:
self.colors = True
else:
self.colors = False
@@ -127,15 +140,14 @@ class App():
#
@contextmanager
def partially_initialized(self, *, fetch_subprojects=False):
- directory = self.main_options['directory']
- config = self.main_options['config']
+ directory = self._main_options['directory']
+ config = self._main_options['config']
try:
self.context = Context(fetch_subprojects=fetch_subprojects)
self.context.load(config)
except BstError as e:
- self.print_error(e, "Error loading user configuration")
- sys.exit(-1)
+ self._error_exit(e, "Error loading user configuration")
# Override things in the context from our command line options,
# the command line when used, trumps the config files.
@@ -153,23 +165,17 @@ class App():
'network_retries': 'sched_network_retries'
}
for cli_option, context_attr in override_map.items():
- option_value = self.main_options.get(cli_option)
+ option_value = self._main_options.get(cli_option)
if option_value is not None:
setattr(self.context, context_attr, option_value)
- # Disable interactive failures if --on-error was specified
- # on the command line, but not if it was only specified
- # in the config.
- if self.main_options.get('on_error') is not None:
- self.interactive_failures = False
-
# Create the logger right before setting the message handler
self.logger = LogLine(
- self.content_profile,
- self.format_profile,
- self.success_profile,
- self.error_profile,
- self.detail_profile,
+ self._content_profile,
+ self._format_profile,
+ self._success_profile,
+ self._error_profile,
+ self._detail_profile,
# Indentation for detailed messages
indent=INDENT,
# Number of last lines in an element's log to print (when encountering errors)
@@ -181,10 +187,10 @@ class App():
message_format=self.context.log_message_format)
# Propagate pipeline feedback to the user
- self.context.set_message_handler(self.message_handler)
+ self.context.set_message_handler(self._message_handler)
try:
- self.project = Project(directory, self.context, cli_options=self.main_options['option'])
+ self.project = Project(directory, self.context, cli_options=self._main_options['option'])
except LoadError as e:
# Let's automatically start a `bst init` session in this case
@@ -194,19 +200,16 @@ class App():
if click.confirm("Would you like to create a new project here ?"):
self.init_project(None)
- self.print_error(e, "Error loading project")
- sys.exit(-1)
+ self._error_exit(e, "Error loading project")
except BstError as e:
- self.print_error(e, "Error loading project")
- sys.exit(-1)
+ self._error_exit(e, "Error loading project")
# Run the body of the session here, once everything is loaded
try:
yield
except BstError as e:
- self.print_error(e)
- sys.exit(-1)
+ self._error_exit(e)
# initialized()
#
@@ -249,27 +252,26 @@ class App():
# Mark the beginning of the session
if session_name:
- self.message(MessageType.START, session_name)
+ self._message(MessageType.START, session_name)
# Create the application's scheduler
- self.scheduler = Scheduler(self.context, self.session_start,
- interrupt_callback=self.interrupt_handler,
- ticker_callback=self.tick,
- job_start_callback=self.job_started,
- job_complete_callback=self.job_completed)
+ self.scheduler = Scheduler(self.context, self._session_start,
+ interrupt_callback=self._interrupt_handler,
+ ticker_callback=self._tick,
+ job_start_callback=self._job_started,
+ job_complete_callback=self._job_completed)
try:
self.pipeline = Pipeline(self.context, self.project, elements, except_,
rewritable=rewritable)
except BstError as e:
- self.print_error(e, "Error loading pipeline")
- sys.exit(-1)
+ self._error_exit(e, "Error loading pipeline")
# Create our status printer, only available in interactive
- self.status = Status(self.content_profile, self.format_profile,
- self.success_profile, self.error_profile,
- self.pipeline, self.scheduler,
- colors=self.colors)
+ self._status = Status(self._content_profile, self._format_profile,
+ self._success_profile, self._error_profile,
+ self.pipeline, self.scheduler,
+ colors=self.colors)
# Initialize pipeline
try:
@@ -278,8 +280,7 @@ class App():
track_elements=track_elements,
track_cross_junctions=track_cross_junctions)
except BstError as e:
- self.print_error(e, "Error initializing pipeline")
- sys.exit(-1)
+ self._error_exit(e, "Error initializing pipeline")
# Pipeline is loaded, now we can tell the logger about it
self.logger.size_request(self.pipeline)
@@ -288,7 +289,7 @@ class App():
# Print the heading
if session_name:
- self.print_heading()
+ self._print_heading()
# Run the body of the session here, once everything is loaded
try:
@@ -300,20 +301,20 @@ class App():
if session_name:
if isinstance(e, PipelineError) and e.terminated: # pylint: disable=no-member
- self.message(MessageType.WARN, session_name + ' Terminated', elapsed=elapsed)
+ self._message(MessageType.WARN, session_name + ' Terminated', elapsed=elapsed)
else:
- self.message(MessageType.FAIL, session_name, elapsed=elapsed)
+ self._message(MessageType.FAIL, session_name, elapsed=elapsed)
if session_name:
- self.print_summary()
+ self._print_summary()
# Let the outer context manager print the error and exit
raise
else:
# No exceptions occurred, print session time and summary
if session_name:
- self.message(MessageType.SUCCESS, session_name, elapsed=self.scheduler.elapsed_time())
- self.print_summary()
+ self._message(MessageType.SUCCESS, session_name, elapsed=self.scheduler.elapsed_time())
+ self._print_summary()
# init_project()
#
@@ -328,7 +329,7 @@ class App():
# force (bool): Allow overwriting an existing project.conf
#
def init_project(self, project_name, format_version=BST_FORMAT_VERSION, element_path='elements', force=False):
- directory = self.main_options['directory']
+ directory = self._main_options['directory']
directory = os.path.abspath(directory)
project_path = os.path.join(directory, 'project.conf')
@@ -342,8 +343,8 @@ class App():
# If project name was specified, user interaction is not desired, just
# perform some validation and write the project.conf
_yaml.assert_symbol_name(None, project_name, 'project name')
- self.assert_format_version(format_version)
- self.assert_element_path(element_path)
+ self._assert_format_version(format_version)
+ self._assert_element_path(element_path)
elif not self.interactive:
raise AppError("Cannot initialize a new project without specifying the project name",
@@ -351,36 +352,77 @@ class App():
else:
# Collect the parameters using an interactive session
project_name, format_version, element_path = \
- self.init_project_interactive(project_name, format_version, element_path)
+ self._init_project_interactive(project_name, format_version, element_path)
- except BstError as e:
- self.print_error(e)
- sys.exit(-1)
+ # Create the directory if it doesnt exist
+ try:
+ os.makedirs(directory, exist_ok=True)
+ except IOError as e:
+ raise AppError("Error creating project directory {}: {}".format(directory, e)) from e
- # Create the directory if it doesnt exist
- os.makedirs(directory, exist_ok=True)
+ # Dont use ruamel.yaml here, because it doesnt let
+ # us programatically insert comments or whitespace at
+ # the toplevel.
+ try:
+ with open(project_path, 'w') as f:
+ f.write("# Unique project name\n" +
+ "name: {}\n\n".format(project_name) +
+ "# Required BuildStream format version\n" +
+ "format-version: {}\n\n".format(format_version) +
+ "# Subdirectory where elements are stored\n" +
+ "element-path: {}\n".format(element_path))
+ except IOError as e:
+ raise AppError("Error writing {}: {}".format(project_path, e)) from e
- # Dont use ruamel.yaml here, because it doesnt let
- # us programatically insert comments or whitespace at
- # the toplevel.
- #
- try:
- with open(project_path, 'w') as f:
- f.write("# Unique project name\n" +
- "name: {}\n\n".format(project_name) +
- "# Required BuildStream format version\n" +
- "format-version: {}\n\n".format(format_version) +
- "# Subdirectory where elements are stored\n" +
- "element-path: {}\n".format(element_path))
- except IOError as e:
- click.echo("", err=True)
- click.echo("Error writing {}: {}".format(project_path, e), err=True)
- sys.exit(-1)
+ except BstError as e:
+ self._error_exit(e)
click.echo("", err=True)
click.echo("Created project.conf at: {}".format(project_path), err=True)
sys.exit(0)
+ # shell()
+ #
+ # Run a shell
+ #
+ # Args:
+ # element (Element): An Element object to run the shell for
+ # scope (Scope): The scope for the shell (Scope.BUILD or Scope.RUN)
+ # directory (str): A directory where an existing prestaged sysroot is expected, or None
+ # mounts (list of HostMount): Additional directories to mount into the sandbox
+ # isolate (bool): Whether to isolate the environment like we do in builds
+ # command (list): An argv to launch in the sandbox, or None
+ #
+ # Returns:
+ # (int): The exit code of the launched shell
+ #
+ def shell(self, element, scope, directory, *, mounts=None, isolate=False, command=None):
+ _, key, dim = element._get_display_key()
+ element_name = element._get_full_name()
+
+ if self.colors:
+ prompt = self._format_profile.fmt('[') + \
+ self._content_profile.fmt(key, dim=dim) + \
+ self._format_profile.fmt('@') + \
+ self._content_profile.fmt(element_name) + \
+ self._format_profile.fmt(':') + \
+ self._content_profile.fmt('$PWD') + \
+ self._format_profile.fmt(']$') + ' '
+ else:
+ prompt = '[{}@{}:${{PWD}}]$ '.format(key, element_name)
+
+ return element._shell(scope, directory, mounts=mounts, isolate=isolate, prompt=prompt, command=command)
+
+ # cleanup()
+ #
+ # Cleans up application state
+ #
+ # This is called by Click at exit time
+ #
+ def cleanup(self):
+ if self.pipeline:
+ self.pipeline.cleanup()
+
############################################################
# Workspace Commands #
############################################################
@@ -439,7 +481,7 @@ class App():
target._open_workspace()
self.project.workspaces.save_config()
- self.message(MessageType.INFO, "Saved workspace configuration")
+ self._message(MessageType.INFO, "Saved workspace configuration")
# close_workspace
#
@@ -469,7 +511,7 @@ class App():
# Delete the workspace and save the configuration
self.project.workspaces.delete_workspace(element_name)
self.project.workspaces.save_config()
- self.message(MessageType.INFO, "Saved workspace configuration")
+ self._message(MessageType.INFO, "Saved workspace configuration")
# reset_workspace
#
@@ -497,7 +539,7 @@ class App():
# Local message propagator
#
- def message(self, message_type, message, **kwargs):
+ def _message(self, message_type, message, **kwargs):
args = dict(kwargs)
self.context.message(
Message(None, message_type, message, **args))
@@ -505,21 +547,21 @@ class App():
#
# Render the status area, conditional on some internal state
#
- def maybe_render_status(self):
+ def _maybe_render_status(self):
# If we're suspended or terminating, then dont render the status area
- if self.status and self.scheduler and \
+ if self._status and self.scheduler and \
not (self.scheduler.suspended or self.scheduler.terminated):
- self.status.render()
+ self._status.render()
#
# Handle ^C SIGINT interruptions in the scheduling main loop
#
- def interrupt_handler(self):
+ def _interrupt_handler(self):
# Only handle ^C interactively in interactive mode
if not self.interactive:
- self.status.clear()
+ self._status.clear()
self.scheduler.terminate_jobs()
return
@@ -527,7 +569,7 @@ class App():
# like to continue, abort immediately, or only complete processing of
# the currently ongoing tasks. We can also print something more
# intelligent, like how many tasks remain to complete overall.
- with self.interrupted():
+ with self._interrupted():
click.echo("\nUser interrupted with ^C\n" +
"\n"
"Choose one of the following options:\n" +
@@ -540,7 +582,7 @@ class App():
try:
choice = click.prompt("Choice:",
- value_proc=prefix_choice_value_proc(['continue', 'quit', 'terminate']),
+ value_proc=_prefix_choice_value_proc(['continue', 'quit', 'terminate']),
default='continue', err=True)
except click.Abort:
# Ensure a newline after automatically printed '^C'
@@ -557,35 +599,38 @@ class App():
elif choice == 'continue':
click.echo("\nContinuing\n", err=True)
- def job_started(self, element, action_name):
- self.status.add_job(element, action_name)
- self.maybe_render_status()
+ def _tick(self, elapsed):
+ self._maybe_render_status()
+
+ def _job_started(self, element, action_name):
+ self._status.add_job(element, action_name)
+ self._maybe_render_status()
- def job_completed(self, element, queue, action_name, success):
- self.status.remove_job(element, action_name)
- self.maybe_render_status()
+ def _job_completed(self, element, queue, action_name, success):
+ self._status.remove_job(element, action_name)
+ self._maybe_render_status()
# Dont attempt to handle a failure if the user has already opted to
# terminate
if not success and not self.scheduler.terminated:
# Get the last failure message for additional context
- failure = self.fail_messages.get(element._get_unique_id())
+ failure = self._fail_messages.get(element._get_unique_id())
# XXX This is dangerous, sometimes we get the job completed *before*
# the failure message reaches us ??
if not failure:
- self.status.clear()
+ self._status.clear()
click.echo("\n\n\nBUG: Message handling out of sync, " +
"unable to retrieve failure message for element {}\n\n\n\n\n"
.format(element), err=True)
else:
- self.handle_failure(element, queue, failure)
+ self._handle_failure(element, queue, failure)
- def handle_failure(self, element, queue, failure):
+ def _handle_failure(self, element, queue, failure):
# Handle non interactive mode setting of what to do when a job fails.
- if not self.interactive_failures:
+ if not self._interactive_failures:
if self.context.sched_error_action == 'terminate':
self.scheduler.terminate_jobs()
@@ -596,7 +641,7 @@ class App():
return
# Interactive mode for element failures
- with self.interrupted():
+ with self._interrupted():
summary = ("\n{} failure on element: {}\n".format(failure.action_name, element.name) +
"\n" +
@@ -623,7 +668,7 @@ class App():
try:
choice = click.prompt("Choice:", default='continue', err=True,
- value_proc=prefix_choice_value_proc(choices))
+ value_proc=_prefix_choice_value_proc(choices))
except click.Abort:
# Ensure a newline after automatically printed '^C'
click.echo("", err=True)
@@ -656,49 +701,37 @@ class App():
queue.failed_elements.remove(element)
queue.enqueue([element])
- def shell(self, element, scope, directory, *, mounts=None, isolate=False, command=None):
- _, key, dim = element._get_display_key()
- element_name = element._get_full_name()
-
- if self.colors:
- prompt = self.format_profile.fmt('[') + \
- self.content_profile.fmt(key, dim=dim) + \
- self.format_profile.fmt('@') + \
- self.content_profile.fmt(element_name) + \
- self.format_profile.fmt(':') + \
- self.content_profile.fmt('$PWD') + \
- self.format_profile.fmt(']$') + ' '
- else:
- prompt = '[{}@{}:${{PWD}}]$ '.format(key, element_name)
-
- return element._shell(scope, directory, mounts=mounts, isolate=isolate, prompt=prompt, command=command)
-
- def tick(self, elapsed):
- self.maybe_render_status()
-
#
# Prints the application startup heading, used for commands which
# will process a pipeline.
#
- def print_heading(self, deps=None):
+ def _print_heading(self, deps=None):
self.logger.print_heading(self.pipeline,
- self.main_options['log_file'],
+ self._main_options['log_file'],
styling=self.colors,
deps=deps)
#
# Print a summary of the queues
#
- def print_summary(self):
+ def _print_summary(self):
click.echo("", err=True)
self.logger.print_summary(self.pipeline, self.scheduler,
- self.main_options['log_file'],
+ self._main_options['log_file'],
styling=self.colors)
+ # _error_exit()
+ #
+ # Exit with an error
#
- # Print an error
+ # This will print the passed error to stderr and exit the program
+ # with -1 status
#
- def print_error(self, error, prefix=None):
+ # Args:
+ # error (BstError): A BstError exception to print
+ # prefix (str): An optional string to prepend to the error message
+ #
+ def _error_exit(self, error, prefix=None):
click.echo("", err=True)
main_error = "{}".format(error)
if prefix is not None:
@@ -710,10 +743,12 @@ class App():
detail = '\n' + indent + indent.join(error.detail.splitlines(True))
click.echo("{}".format(detail), err=True)
+ sys.exit(-1)
+
#
# Handle messages from the pipeline
#
- def message_handler(self, message, context):
+ def _message_handler(self, message, context):
# Drop status messages from the UI if not verbose, we'll still see
# info messages and status messages will still go to the log files.
@@ -722,45 +757,41 @@ class App():
# Hold on to the failure messages
if message.message_type in [MessageType.FAIL, MessageType.BUG] and message.unique_id is not None:
- self.fail_messages[message.unique_id] = message
+ self._fail_messages[message.unique_id] = message
# Send to frontend if appropriate
if self.context.silent_messages() and (message.message_type not in unconditional_messages):
return
- if self.status:
- self.status.clear()
+ if self._status:
+ self._status.clear()
text = self.logger.render(message)
click.echo(text, color=self.colors, nl=False, err=True)
# Maybe render the status area
- self.maybe_render_status()
+ self._maybe_render_status()
# Additionally log to a file
- if self.main_options['log_file']:
- click.echo(text, file=self.main_options['log_file'], color=False, nl=False)
+ if self._main_options['log_file']:
+ click.echo(text, file=self._main_options['log_file'], color=False, nl=False)
@contextmanager
- def interrupted(self):
+ def _interrupted(self):
self.scheduler.disconnect_signals()
- self.status.clear()
+ self._status.clear()
self.scheduler.suspend_jobs()
yield
- self.maybe_render_status()
+ self._maybe_render_status()
self.scheduler.resume_jobs()
self.scheduler.connect_signals()
- def cleanup(self):
- if self.pipeline:
- self.pipeline.cleanup()
-
# Some validation routines for project initialization
#
- def assert_format_version(self, format_version):
+ def _assert_format_version(self, format_version):
message = "The version must be supported by this " + \
"version of buildstream (0 - {})\n".format(BST_FORMAT_VERSION)
@@ -774,7 +805,7 @@ class App():
if number < 0 or number > BST_FORMAT_VERSION:
raise AppError(message, reason='invalid-format-version')
- def assert_element_path(self, element_path):
+ def _assert_element_path(self, element_path):
message = "The element path cannot be an absolute path or contain any '..' components\n"
# Validate the path is not absolute
@@ -790,7 +821,7 @@ class App():
if basename == '..':
raise AppError(message, reason='invalid-element-path')
- # init_project_interactive()
+ # _init_project_interactive()
#
# Collect the user input for an interactive session for App.init_project()
#
@@ -804,7 +835,7 @@ class App():
# format_version (int): The user selected format version
# element_path (str): The user selected element path
#
- def init_project_interactive(self, project_name, format_version=BST_FORMAT_VERSION, element_path='elements'):
+ def _init_project_interactive(self, project_name, format_version=BST_FORMAT_VERSION, element_path='elements'):
def project_name_proc(user_input):
try:
@@ -816,14 +847,14 @@ class App():
def format_version_proc(user_input):
try:
- self.assert_format_version(user_input)
+ self._assert_format_version(user_input)
except AppError as e:
raise UsageError(str(e)) from e
return user_input
def element_path_proc(user_input):
try:
- self.assert_element_path(user_input)
+ self._assert_element_path(user_input)
except AppError as e:
raise UsageError(str(e)) from e
return user_input
@@ -832,57 +863,57 @@ class App():
# Collect project name
click.echo("", err=True)
- click.echo(self.content_profile.fmt("Choose a unique name for your project"), err=True)
- click.echo(self.format_profile.fmt("-------------------------------------"), err=True)
+ click.echo(self._content_profile.fmt("Choose a unique name for your project"), err=True)
+ click.echo(self._format_profile.fmt("-------------------------------------"), err=True)
click.echo("", err=True)
- click.echo(self.detail_profile.fmt(
+ click.echo(self._detail_profile.fmt(
w.fill("The project name is a unique symbol for your project and will be used "
"to distinguish your project from others in user preferences, namspaceing "
"of your project's artifacts in shared artifact caches, and in any case where "
"BuildStream needs to distinguish between multiple projects.")), err=True)
click.echo("", err=True)
- click.echo(self.detail_profile.fmt(
+ click.echo(self._detail_profile.fmt(
w.fill("The project name must contain only alphanumeric characters, "
"may not start with a digit, and may contain dashes or underscores.")), err=True)
click.echo("", err=True)
- project_name = click.prompt(self.content_profile.fmt("Project name"),
+ project_name = click.prompt(self._content_profile.fmt("Project name"),
value_proc=project_name_proc, err=True)
click.echo("", err=True)
# Collect format version
- click.echo(self.content_profile.fmt("Select the minimum required format version for your project"), err=True)
- click.echo(self.format_profile.fmt("-----------------------------------------------------------"), err=True)
+ click.echo(self._content_profile.fmt("Select the minimum required format version for your project"), err=True)
+ click.echo(self._format_profile.fmt("-----------------------------------------------------------"), err=True)
click.echo("", err=True)
- click.echo(self.detail_profile.fmt(
+ click.echo(self._detail_profile.fmt(
w.fill("The format version is used to provide users who build your project "
"with a helpful error message in the case that they do not have a recent "
"enough version of BuildStream supporting all the features which your "
"project might use.")), err=True)
click.echo("", err=True)
- click.echo(self.detail_profile.fmt(
+ click.echo(self._detail_profile.fmt(
w.fill("The lowest version allowed is 0, the currently installed version of BuildStream "
"supports up to format version {}.".format(BST_FORMAT_VERSION))), err=True)
click.echo("", err=True)
- format_version = click.prompt(self.content_profile.fmt("Format version"),
+ format_version = click.prompt(self._content_profile.fmt("Format version"),
value_proc=format_version_proc,
default=format_version, err=True)
click.echo("", err=True)
# Collect element path
- click.echo(self.content_profile.fmt("Select the element path"), err=True)
- click.echo(self.format_profile.fmt("-----------------------"), err=True)
+ click.echo(self._content_profile.fmt("Select the element path"), err=True)
+ click.echo(self._format_profile.fmt("-----------------------"), err=True)
click.echo("", err=True)
- click.echo(self.detail_profile.fmt(
+ click.echo(self._detail_profile.fmt(
w.fill("The element path is a project subdirectory where element .bst files are stored "
"within your project.")), err=True)
click.echo("", err=True)
- click.echo(self.detail_profile.fmt(
+ click.echo(self._detail_profile.fmt(
w.fill("Elements will be displayed in logs as filenames relative to "
"the element path, and similarly, dependencies must be expressed as filenames "
"relative to the element path.")), err=True)
click.echo("", err=True)
- element_path = click.prompt(self.content_profile.fmt("Element path"),
+ element_path = click.prompt(self._content_profile.fmt("Element path"),
value_proc=element_path_proc,
default=element_path, err=True)
@@ -901,7 +932,7 @@ class App():
# 'click.UsageError' exception. That way, Click display an error message and
# ask for a new input.
#
-def prefix_choice_value_proc(choices):
+def _prefix_choice_value_proc(choices):
def value_proc(user_input):
remaining_candidate = [choice for choice in choices if choice.startswith(user_input)]
diff --git a/tests/frontend/main.py b/tests/frontend/main.py
index 41337b54d..c330c39d9 100644
--- a/tests/frontend/main.py
+++ b/tests/frontend/main.py
@@ -1,11 +1,11 @@
-from buildstream._frontend.app import prefix_choice_value_proc
+from buildstream._frontend.app import _prefix_choice_value_proc
import pytest
import click
def test_prefix_choice_value_proc_full_match():
- value_proc = prefix_choice_value_proc(['foo', 'bar', 'baz'])
+ value_proc = _prefix_choice_value_proc(['foo', 'bar', 'baz'])
assert("foo" == value_proc("foo"))
assert("bar" == value_proc("bar"))
@@ -13,13 +13,13 @@ def test_prefix_choice_value_proc_full_match():
def test_prefix_choice_value_proc_prefix_match():
- value_proc = prefix_choice_value_proc(['foo'])
+ value_proc = _prefix_choice_value_proc(['foo'])
assert ("foo" == value_proc("f"))
def test_prefix_choice_value_proc_ambigous_match():
- value_proc = prefix_choice_value_proc(['bar', 'baz'])
+ value_proc = _prefix_choice_value_proc(['bar', 'baz'])
assert ("bar" == value_proc("bar"))
assert ("baz" == value_proc("baz"))
@@ -28,7 +28,7 @@ def test_prefix_choice_value_proc_ambigous_match():
def test_prefix_choice_value_proc_value_not_in_choices():
- value_proc = prefix_choice_value_proc(['bar', 'baz'])
+ value_proc = _prefix_choice_value_proc(['bar', 'baz'])
with pytest.raises(click.UsageError):
value_proc("foo")