Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
669cf281a5 | ||
|
|
c507c093fa | ||
|
|
2f80522f6d | ||
|
|
665c497693 | ||
|
|
36fcb1995b | ||
|
|
1c1f6dd110 | ||
|
|
2363b5f2b7 | ||
|
|
3578817f16 | ||
|
|
2bfb82ef23 |
@@ -98,3 +98,6 @@ ENV/
|
||||
|
||||
# PyCharm IDE
|
||||
.idea/
|
||||
|
||||
# Type-checking
|
||||
.pytype/
|
||||
|
||||
+18
-2
@@ -4,14 +4,30 @@ python:
|
||||
- "3.4"
|
||||
- "3.5"
|
||||
- "3.6"
|
||||
# Workaround for testing Python 3.7:
|
||||
# https://github.com/travis-ci/travis-ci/issues/9815
|
||||
matrix:
|
||||
include:
|
||||
- python: 3.7
|
||||
dist: xenial
|
||||
sudo: yes
|
||||
before_install:
|
||||
- pip install --upgrade setuptools pip
|
||||
- pip install --upgrade pylint pytest pytest-pylint pytest-runner
|
||||
install:
|
||||
- pip install hypothesis
|
||||
- pip install hypothesis python-Levenshtein
|
||||
- python setup.py develop
|
||||
script:
|
||||
- python -m pytest # Run the tests without IPython.
|
||||
- pip install ipython
|
||||
- python -m pytest # Now run the tests with IPython.
|
||||
- if [[ $TRAVIS_PYTHON_VERSION != 3.6 ]]; then pylint fire --ignore=test_components_py3.py,parser_fuzz_test.py,console; fi
|
||||
- pylint fire --ignore=test_components_py3.py,parser_fuzz_test.py,console
|
||||
- pip install pytype
|
||||
# Run type-checking, excluding files that define or use py3 features in py2.
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then
|
||||
pytype -x
|
||||
fire/fire_test.py
|
||||
fire/inspectutils_test.py
|
||||
fire/test_components_py3.py;
|
||||
else
|
||||
pytype; fi
|
||||
|
||||
+9
-9
@@ -133,7 +133,7 @@ def Fire(component=None, command=None, name=None):
|
||||
if component_trace.HasError():
|
||||
_DisplayError(component_trace)
|
||||
raise FireExit(2, component_trace)
|
||||
elif component_trace.show_trace and component_trace.show_help:
|
||||
if component_trace.show_trace and component_trace.show_help:
|
||||
output = ['Fire trace:\n{trace}\n'.format(trace=component_trace)]
|
||||
result = component_trace.GetResult()
|
||||
help_string = helputils.HelpString(
|
||||
@@ -141,22 +141,22 @@ def Fire(component=None, command=None, name=None):
|
||||
output.append(help_string)
|
||||
Display(output)
|
||||
raise FireExit(0, component_trace)
|
||||
elif component_trace.show_trace:
|
||||
if component_trace.show_trace:
|
||||
output = ['Fire trace:\n{trace}'.format(trace=component_trace)]
|
||||
Display(output)
|
||||
raise FireExit(0, component_trace)
|
||||
elif component_trace.show_help:
|
||||
if component_trace.show_help:
|
||||
result = component_trace.GetResult()
|
||||
help_string = helputils.HelpString(
|
||||
result, component_trace, component_trace.verbose)
|
||||
output = [help_string]
|
||||
Display(output)
|
||||
raise FireExit(0, component_trace)
|
||||
else:
|
||||
# The command succeeded normally; print the result.
|
||||
_PrintResult(component_trace, verbose=component_trace.verbose)
|
||||
result = component_trace.GetResult()
|
||||
return result
|
||||
|
||||
# The command succeeded normally; print the result.
|
||||
_PrintResult(component_trace, verbose=component_trace.verbose)
|
||||
result = component_trace.GetResult()
|
||||
return result
|
||||
|
||||
|
||||
def Display(lines):
|
||||
@@ -487,7 +487,7 @@ def _Fire(component, args, context, name=None):
|
||||
# If the component is a namedtuple, we need to convert it to dict to
|
||||
# be able to use the .items() method.
|
||||
if inspectutils.IsNamedTuple(component):
|
||||
component = component._asdict()
|
||||
component = component._asdict() # pytype: disable=attribute-error
|
||||
for key, value in component.items():
|
||||
if target == str(key):
|
||||
component = value
|
||||
|
||||
+2
-2
@@ -82,7 +82,7 @@ class Namespace(dict):
|
||||
def __getattr__(self, key):
|
||||
if key not in self:
|
||||
self[key] = Namespace()
|
||||
return self.get(key)
|
||||
return self[key]
|
||||
|
||||
def __setattr__(self, key, value):
|
||||
self[key] = value
|
||||
@@ -398,7 +398,7 @@ def _consume_line(line_info, state):
|
||||
if state.section.new and state.section.format == Formats.RST:
|
||||
# The current line starts with an RST directive, e.g. ":param arg:".
|
||||
directive = _get_directive(line_info)
|
||||
directive_tokens = directive.split()
|
||||
directive_tokens = directive.split() # pytype: disable=attribute-error
|
||||
if state.section.title == Sections.ARGS:
|
||||
name = directive_tokens[-1]
|
||||
arg = _get_or_create_arg_by_name(state, name)
|
||||
|
||||
@@ -0,0 +1,473 @@
|
||||
# Copyright (C) 2018 Google Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""helptext is the new, work in progress, help text module for Fire.
|
||||
|
||||
This is a fork of, and is intended to replace, helputils.
|
||||
|
||||
Utility for producing help strings for use in Fire CLIs.
|
||||
|
||||
Can produce help strings suitable for display in Fire CLIs for any type of
|
||||
Python object, module, class, or function.
|
||||
|
||||
There are two types of informative strings: Usage and Help screens.
|
||||
|
||||
Usage screens are shown when the user accesses a group or accesses a command
|
||||
without calling it. A Usage screen shows information about how to use that group
|
||||
or command. Usage screens are typically short and show the minimal information
|
||||
necessary for the user to determine how to proceed.
|
||||
|
||||
Help screens are shown when the user requests help with the help flag (--help).
|
||||
Help screens are shown in a less-style console view, and contain detailed help
|
||||
information.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import inspect
|
||||
|
||||
from fire import completion
|
||||
from fire import docstrings
|
||||
from fire import inspectutils
|
||||
from fire import value_types
|
||||
|
||||
|
||||
def Text(component, trace=None, verbose=False):
|
||||
"""Returns the text to show for a supplied component.
|
||||
|
||||
The component can be any Python class, object, function, module, etc.
|
||||
|
||||
Args:
|
||||
component: The component to determine the help string for.
|
||||
trace: The Fire trace leading to this component.
|
||||
verbose: Whether to include private members in the help string.
|
||||
Returns:
|
||||
String suitable for display giving information about the component.
|
||||
"""
|
||||
info = inspectutils.Info(component)
|
||||
info['docstring_info'] = docstrings.parse(info['docstring'])
|
||||
|
||||
is_error_screen = False
|
||||
if trace:
|
||||
is_error_screen = trace.HasError()
|
||||
|
||||
if is_error_screen:
|
||||
return UsageText(info, trace, verbose=verbose)
|
||||
else:
|
||||
return HelpText(info, trace, verbose=verbose)
|
||||
|
||||
|
||||
def GetArgsAngFlags(component):
|
||||
"""Returns all types of arguments and flags of a component."""
|
||||
spec = inspectutils.GetFullArgSpec(component)
|
||||
args = spec.args
|
||||
if spec.defaults is None:
|
||||
num_defaults = 0
|
||||
else:
|
||||
num_defaults = len(spec.defaults)
|
||||
args_with_no_defaults = args[:len(args) - num_defaults]
|
||||
args_with_defaults = args[len(args) - num_defaults:]
|
||||
flags = args_with_defaults + spec.kwonlyargs
|
||||
return args_with_no_defaults, args_with_defaults, flags
|
||||
|
||||
|
||||
def GetSummaryAndDescription(docstring_info):
|
||||
"""Retrieves summary and description for help text generation."""
|
||||
|
||||
# To handle both empty string and None
|
||||
summary = docstring_info.summary if docstring_info.summary else None
|
||||
description = (
|
||||
docstring_info.description if docstring_info.description else None)
|
||||
return summary, description
|
||||
|
||||
|
||||
def GetCurrentCommand(trace=None):
|
||||
"""Returns current command for the purpose of generating help text."""
|
||||
if trace:
|
||||
current_command = trace.GetCommand()
|
||||
else:
|
||||
current_command = ''
|
||||
|
||||
return current_command
|
||||
|
||||
|
||||
def HelpText(component, info, trace=None, verbose=False):
|
||||
if inspect.isroutine(component) or inspect.isclass(component):
|
||||
return HelpTextForFunction(component, info, trace)
|
||||
else:
|
||||
return HelpTextForObject(component, info, trace, verbose)
|
||||
|
||||
|
||||
def HelpTextForFunction(component, info, trace=None, verbose=False):
|
||||
"""Returns detail help text for a function component.
|
||||
|
||||
Args:
|
||||
component: Current component to generate help text for.
|
||||
info: Info containing metadata of component.
|
||||
trace: FireTrace object that leads to current component.
|
||||
verbose: Whether to display help text in verbose mode.
|
||||
|
||||
Returns:
|
||||
Formatted help text for display.
|
||||
"""
|
||||
# TODO(joejoevictor): Implement verbose related output
|
||||
del verbose
|
||||
|
||||
current_command = GetCurrentCommand(trace)
|
||||
summary, description = GetSummaryAndDescription(info['docstring_info'])
|
||||
spec = inspectutils.GetFullArgSpec(component)
|
||||
args = spec.args
|
||||
|
||||
args_with_no_defaults, args_with_defaults, flags = GetArgsAngFlags(component)
|
||||
del args_with_defaults
|
||||
|
||||
output_template = """NAME
|
||||
{name_section}
|
||||
|
||||
SYNOPSIS
|
||||
{synopsis_section}
|
||||
|
||||
DESCRIPTION
|
||||
{description_section}
|
||||
{args_and_flags_section}
|
||||
NOTES
|
||||
You could also use flags syntax for POSITIONAL ARGUMENTS
|
||||
"""
|
||||
|
||||
# Name section
|
||||
name_section_template = '{current_command}{command_summary}'
|
||||
command_summary_str = ' - ' + summary if summary else ''
|
||||
name_section = name_section_template.format(
|
||||
current_command=current_command, command_summary=command_summary_str)
|
||||
|
||||
args_and_flags = ''
|
||||
if args_with_no_defaults:
|
||||
items = [arg.upper() for arg in args_with_no_defaults]
|
||||
args_and_flags = ' '.join(items)
|
||||
|
||||
synopsis_flag_template = '[--{flag_name}={flag_name_upper}]'
|
||||
if flags:
|
||||
items = [
|
||||
synopsis_flag_template.format(
|
||||
flag_name=flag, flag_name_upper=flag.upper()) for flag in flags
|
||||
]
|
||||
args_and_flags = args_and_flags + ' '.join(items)
|
||||
|
||||
# Synopsis section
|
||||
synopsis_section_template = '{current_command} {args_and_flags}'
|
||||
positional_arguments = '|'.join(args)
|
||||
if positional_arguments:
|
||||
positional_arguments = ' ' + positional_arguments
|
||||
synopsis_section = synopsis_section_template.format(
|
||||
current_command=current_command, args_and_flags=args_and_flags)
|
||||
|
||||
# Description section
|
||||
description_section = description if description else summary
|
||||
|
||||
args_and_flags_section = ''
|
||||
|
||||
# Positional arguments and flags section
|
||||
pos_arg_template = """
|
||||
POSITIONAL ARGUMENTS
|
||||
{items}
|
||||
"""
|
||||
pos_arg_items = []
|
||||
for arg in args_with_no_defaults:
|
||||
item_template = ' {arg_name}\n {arg_description}\n'
|
||||
arg_description = None
|
||||
for arg_in_docstring in info['docstring_info'].args:
|
||||
if arg_in_docstring.name == arg:
|
||||
arg_description = arg_in_docstring.description
|
||||
|
||||
item = item_template.format(
|
||||
arg_name=arg.upper(), arg_description=arg_description)
|
||||
pos_arg_items.append(item)
|
||||
if pos_arg_items:
|
||||
args_and_flags_section += pos_arg_template.format(
|
||||
items='\n'.join(pos_arg_items).rstrip('\n'))
|
||||
|
||||
flags_template = """
|
||||
FLAGS
|
||||
{items}
|
||||
"""
|
||||
flag_items = []
|
||||
for flag in flags:
|
||||
item_template = ' --{flag_name}\n {flag_description}\n'
|
||||
flag_description = None
|
||||
for arg_in_docstring in info['docstring_info'].args:
|
||||
if arg_in_docstring.name == flag:
|
||||
flag_description = arg_in_docstring.description
|
||||
|
||||
item = item_template.format(
|
||||
flag_name=flag, flag_description=flag_description)
|
||||
flag_items.append(item)
|
||||
if flag_items:
|
||||
args_and_flags_section += flags_template.format(
|
||||
items='\n'.join(flag_items).rstrip('\n'))
|
||||
|
||||
return output_template.format(
|
||||
name_section=name_section,
|
||||
synopsis_section=synopsis_section,
|
||||
description_section=description_section,
|
||||
args_and_flags_section=args_and_flags_section)
|
||||
|
||||
|
||||
def HelpTextForObject(component, info, trace=None, verbose=False):
|
||||
"""Generates help text for python objects.
|
||||
|
||||
Args:
|
||||
component: Current component to generate help text for.
|
||||
info: Info containing metadata of component.
|
||||
trace: FireTrace object that leads to current component.
|
||||
verbose: Whether to display help text in verbose mode.
|
||||
|
||||
Returns:
|
||||
Formatted help text for display.
|
||||
"""
|
||||
|
||||
output_template = """NAME
|
||||
{current_command} - {command_summary}
|
||||
|
||||
SYNOPSIS
|
||||
{synopsis}
|
||||
|
||||
DESCRIPTION
|
||||
{command_description}
|
||||
{detail_section}
|
||||
"""
|
||||
|
||||
current_command = GetCurrentCommand(trace)
|
||||
|
||||
docstring_info = info['docstring_info']
|
||||
command_summary = docstring_info.summary if docstring_info.summary else ''
|
||||
if docstring_info.description:
|
||||
command_description = docstring_info.description
|
||||
else:
|
||||
command_description = ''
|
||||
|
||||
groups = []
|
||||
commands = []
|
||||
values = []
|
||||
members = completion._Members(component, verbose) # pylint: disable=protected-access
|
||||
for member_name, member in members:
|
||||
if value_types.IsGroup(member):
|
||||
groups.append((member_name, member))
|
||||
if value_types.IsCommand(member):
|
||||
commands.append((member_name, member))
|
||||
if value_types.IsValue(member):
|
||||
values.append((member_name, member))
|
||||
|
||||
possible_actions = []
|
||||
# TODO(joejoevictor): Add global flags to here. Also, if it's a callable,
|
||||
# there will be additional flags.
|
||||
possible_flags = ''
|
||||
detail_section_string = ''
|
||||
item_template = """
|
||||
{name}
|
||||
{command_summary}
|
||||
"""
|
||||
|
||||
if groups:
|
||||
# TODO(joejoevictor): Add missing GROUPS section handling
|
||||
possible_actions.append('GROUP')
|
||||
if commands:
|
||||
possible_actions.append('COMMAND')
|
||||
commands_str_template = """
|
||||
COMMANDS
|
||||
COMMAND is one of the followings:
|
||||
{items}
|
||||
"""
|
||||
command_item_strings = []
|
||||
for command_name, command in commands:
|
||||
command_docstring_info = docstrings.parse(
|
||||
inspectutils.Info(command)['docstring'])
|
||||
command_item_strings.append(
|
||||
item_template.format(
|
||||
name=command_name,
|
||||
command_summary=command_docstring_info.summary))
|
||||
detail_section_string += commands_str_template.format(
|
||||
items=('\n'.join(command_item_strings)).rstrip('\n'))
|
||||
|
||||
if values:
|
||||
possible_actions.append('VALUES')
|
||||
values_str_template = """
|
||||
VALUES
|
||||
VALUE is one of the followings:
|
||||
{items}
|
||||
"""
|
||||
value_item_strings = []
|
||||
for value_name, value in values:
|
||||
del value
|
||||
init_docstring_info = docstrings.parse(
|
||||
inspectutils.Info(component.__class__.__init__)['docstring'])
|
||||
for arg_info in init_docstring_info.args:
|
||||
if arg_info.name == value_name:
|
||||
value_item_strings.append(
|
||||
item_template.format(
|
||||
name=value_name, command_summary=arg_info.description))
|
||||
detail_section_string += values_str_template.format(
|
||||
items=('\n'.join(value_item_strings)).rstrip('\n'))
|
||||
|
||||
possible_actions_string = ' ' + (' | '.join(possible_actions))
|
||||
|
||||
synopsis_template = '{current_command}{possible_actions}{possible_flags}'
|
||||
synopsis_string = synopsis_template.format(
|
||||
current_command=current_command,
|
||||
possible_actions=possible_actions_string,
|
||||
possible_flags=possible_flags)
|
||||
|
||||
return output_template.format(
|
||||
current_command=current_command,
|
||||
command_summary=command_summary,
|
||||
synopsis=synopsis_string,
|
||||
command_description=command_description,
|
||||
detail_section=detail_section_string)
|
||||
|
||||
|
||||
def UsageText(component, trace=None, verbose=False):
|
||||
if inspect.isroutine(component) or inspect.isclass(component):
|
||||
return UsageTextForFunction(component, trace)
|
||||
else:
|
||||
return UsageTextForObject(component, trace, verbose)
|
||||
|
||||
|
||||
def UsageTextForFunction(component, trace=None):
|
||||
"""Returns usage text for function objects.
|
||||
|
||||
Args:
|
||||
component: The component to determine the usage text for.
|
||||
trace: The Fire trace object containing all metadata of current execution.
|
||||
|
||||
Returns:
|
||||
String suitable for display in error screen.
|
||||
"""
|
||||
|
||||
output_template = """Usage: {current_command} {args_and_flags}
|
||||
{availability_lines}
|
||||
For detailed information on this command, run:
|
||||
{current_command}{hyphen_hyphen} --help
|
||||
"""
|
||||
|
||||
if trace:
|
||||
command = trace.GetCommand()
|
||||
is_help_an_arg = trace.NeedsSeparatingHyphenHyphen()
|
||||
else:
|
||||
command = None
|
||||
is_help_an_arg = False
|
||||
|
||||
if not command:
|
||||
command = ''
|
||||
|
||||
spec = inspectutils.GetFullArgSpec(component)
|
||||
args = spec.args
|
||||
if spec.defaults is None:
|
||||
num_defaults = 0
|
||||
else:
|
||||
num_defaults = len(spec.defaults)
|
||||
args_with_no_defaults = args[:len(args) - num_defaults]
|
||||
args_with_defaults = args[len(args) - num_defaults:]
|
||||
flags = args_with_defaults + spec.kwonlyargs
|
||||
|
||||
items = [arg.upper() for arg in args_with_no_defaults]
|
||||
if flags:
|
||||
items.append('<flags>')
|
||||
availability_lines = (
|
||||
'\nAvailable flags: '
|
||||
+ ' | '.join('--' + flag for flag in flags) + '\n')
|
||||
else:
|
||||
availability_lines = ''
|
||||
args_and_flags = ' '.join(items)
|
||||
|
||||
hyphen_hyphen = ' --' if is_help_an_arg else ''
|
||||
|
||||
return output_template.format(
|
||||
current_command=command,
|
||||
args_and_flags=args_and_flags,
|
||||
availability_lines=availability_lines,
|
||||
hyphen_hyphen=hyphen_hyphen)
|
||||
|
||||
|
||||
def UsageTextForObject(component, trace=None, verbose=False):
|
||||
"""Returns help text for usage screen for objects.
|
||||
|
||||
Construct help text for usage screen to inform the user about error occurred
|
||||
and correct syntax for invoking the object.
|
||||
|
||||
Args:
|
||||
component: The component to determine the usage text for.
|
||||
trace: The Fire trace object containing all metadata of current execution.
|
||||
verbose: Whether to include private members in the usage text.
|
||||
Returns:
|
||||
String suitable for display in error screen.
|
||||
"""
|
||||
output_template = """Usage: {current_command} <{possible_actions}>
|
||||
{availability_lines}
|
||||
|
||||
For detailed information on this command, run:
|
||||
{current_command} --help
|
||||
"""
|
||||
if trace:
|
||||
command = trace.GetCommand()
|
||||
else:
|
||||
command = None
|
||||
|
||||
if not command:
|
||||
command = ''
|
||||
|
||||
groups = []
|
||||
commands = []
|
||||
values = []
|
||||
|
||||
members = completion._Members(component, verbose) # pylint: disable=protected-access
|
||||
for member_name, member in members:
|
||||
if value_types.IsGroup(member):
|
||||
groups.append(member_name)
|
||||
if value_types.IsCommand(member):
|
||||
commands.append(member_name)
|
||||
if value_types.IsValue(member):
|
||||
values.append(member_name)
|
||||
|
||||
possible_actions = []
|
||||
availability_lines = []
|
||||
availability_lint_format = '{header:20s}{choices}'
|
||||
if groups:
|
||||
possible_actions.append('groups')
|
||||
groups_string = ' | '.join(groups)
|
||||
groups_text = availability_lint_format.format(
|
||||
header='available groups:',
|
||||
choices=groups_string)
|
||||
availability_lines.append(groups_text)
|
||||
if commands:
|
||||
possible_actions.append('commands')
|
||||
commands_string = ' | '.join(commands)
|
||||
commands_text = availability_lint_format.format(
|
||||
header='available commands:',
|
||||
choices=commands_string)
|
||||
availability_lines.append(commands_text)
|
||||
if values:
|
||||
possible_actions.append('values')
|
||||
values_string = ' | '.join(values)
|
||||
values_text = availability_lint_format.format(
|
||||
header='available values:',
|
||||
choices=values_string)
|
||||
availability_lines.append(values_text)
|
||||
possible_actions_string = '|'.join(possible_actions)
|
||||
availability_lines_string = '\n'.join(availability_lines)
|
||||
|
||||
return output_template.format(
|
||||
current_command=command,
|
||||
possible_actions=possible_actions_string,
|
||||
availability_lines=availability_lines_string)
|
||||
@@ -0,0 +1,231 @@
|
||||
# Copyright (C) 2018 Google Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Tests for the helptext module."""
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import textwrap
|
||||
|
||||
from fire import docstrings
|
||||
from fire import helptext
|
||||
from fire import inspectutils
|
||||
from fire import test_components as tc
|
||||
from fire import testutils
|
||||
from fire import trace
|
||||
|
||||
|
||||
class HelpScreenTest(testutils.BaseTestCase):
|
||||
|
||||
def testHelpScreen(self):
|
||||
component = tc.ClassWithDocstring()
|
||||
t = trace.FireTrace(component, name='ClassWithDocstring')
|
||||
info = inspectutils.Info(component)
|
||||
info['docstring_info'] = docstrings.parse(info['docstring'])
|
||||
help_output = helptext.HelpText(component, info, t)
|
||||
expected_output = """
|
||||
NAME
|
||||
ClassWithDocstring - Test class for testing help text output.
|
||||
|
||||
SYNOPSIS
|
||||
ClassWithDocstring COMMAND | VALUES
|
||||
|
||||
DESCRIPTION
|
||||
This is some detail description of this test class.
|
||||
|
||||
COMMANDS
|
||||
COMMAND is one of the followings:
|
||||
|
||||
print_msg
|
||||
Prints a message.
|
||||
|
||||
VALUES
|
||||
VALUE is one of the followings:
|
||||
|
||||
message
|
||||
The default message to print.
|
||||
|
||||
"""
|
||||
self.assertEqual(textwrap.dedent(expected_output).lstrip('\n'), help_output)
|
||||
|
||||
def testHelpScreenForFunctionDocstringWithLineBreak(self):
|
||||
component = tc.ClassWithMultilineDocstring.example_generator
|
||||
t = trace.FireTrace(component, name='example_generator')
|
||||
info = inspectutils.Info(component)
|
||||
info['docstring_info'] = docstrings.parse(info['docstring'])
|
||||
help_output = helptext.HelpText(component, info, t)
|
||||
expected_output = """
|
||||
NAME
|
||||
example_generator - Generators have a ``Yields`` section instead of a ``Returns`` section.
|
||||
|
||||
SYNOPSIS
|
||||
example_generator N
|
||||
|
||||
DESCRIPTION
|
||||
Generators have a ``Yields`` section instead of a ``Returns`` section.
|
||||
|
||||
POSITIONAL ARGUMENTS
|
||||
N
|
||||
The upper limit of the range to generate, from 0 to `n` - 1.
|
||||
|
||||
NOTES
|
||||
You could also use flags syntax for POSITIONAL ARGUMENTS
|
||||
"""
|
||||
self.assertEqual(textwrap.dedent(expected_output).lstrip('\n'), help_output)
|
||||
|
||||
def testHelpScreenForFunctionFunctionWithDefaultArgs(self):
|
||||
component = tc.WithDefaults().double
|
||||
t = trace.FireTrace(component, name='double')
|
||||
info = inspectutils.Info(component)
|
||||
info['docstring_info'] = docstrings.parse(info['docstring'])
|
||||
help_output = helptext.HelpText(component, info, t)
|
||||
expected_output = """
|
||||
NAME
|
||||
double - Returns the input multiplied by 2.
|
||||
|
||||
SYNOPSIS
|
||||
double [--count=COUNT]
|
||||
|
||||
DESCRIPTION
|
||||
Returns the input multiplied by 2.
|
||||
|
||||
FLAGS
|
||||
--count
|
||||
Input number that you want to double.
|
||||
|
||||
NOTES
|
||||
You could also use flags syntax for POSITIONAL ARGUMENTS
|
||||
"""
|
||||
self.assertEqual(textwrap.dedent(expected_output).lstrip('\n'), help_output)
|
||||
|
||||
|
||||
class UsageTest(testutils.BaseTestCase):
|
||||
|
||||
def testUsageOutput(self):
|
||||
component = tc.NoDefaults()
|
||||
t = trace.FireTrace(component, name='NoDefaults')
|
||||
usage_output = helptext.UsageText(component, trace=t, verbose=False)
|
||||
expected_output = '''
|
||||
Usage: NoDefaults <commands>
|
||||
available commands: double | triple
|
||||
|
||||
For detailed information on this command, run:
|
||||
NoDefaults --help
|
||||
'''
|
||||
|
||||
self.assertEqual(
|
||||
usage_output,
|
||||
textwrap.dedent(expected_output).lstrip('\n'))
|
||||
|
||||
def testUsageOutputVerbose(self):
|
||||
component = tc.NoDefaults()
|
||||
t = trace.FireTrace(component, name='NoDefaults')
|
||||
usage_output = helptext.UsageText(component, trace=t, verbose=True)
|
||||
expected_output = '''
|
||||
Usage: NoDefaults <commands>
|
||||
available commands: double | triple
|
||||
|
||||
For detailed information on this command, run:
|
||||
NoDefaults --help
|
||||
'''
|
||||
self.assertEqual(
|
||||
usage_output,
|
||||
textwrap.dedent(expected_output).lstrip('\n'))
|
||||
|
||||
def testUsageOutputMethod(self):
|
||||
component = tc.NoDefaults().double
|
||||
t = trace.FireTrace(component, name='NoDefaults')
|
||||
t.AddAccessedProperty(component, 'double', ['double'], None, None)
|
||||
usage_output = helptext.UsageText(component, trace=t, verbose=True)
|
||||
expected_output = '''
|
||||
Usage: NoDefaults double COUNT
|
||||
|
||||
For detailed information on this command, run:
|
||||
NoDefaults double --help
|
||||
'''
|
||||
self.assertEqual(
|
||||
usage_output,
|
||||
textwrap.dedent(expected_output).lstrip('\n'))
|
||||
|
||||
def testUsageOutputFunctionWithHelp(self):
|
||||
component = tc.function_with_help
|
||||
t = trace.FireTrace(component, name='function_with_help')
|
||||
usage_output = helptext.UsageText(component, trace=t, verbose=True)
|
||||
expected_output = '''
|
||||
Usage: function_with_help <flags>
|
||||
|
||||
Available flags: --help
|
||||
|
||||
For detailed information on this command, run:
|
||||
function_with_help -- --help
|
||||
'''
|
||||
self.assertEqual(
|
||||
usage_output,
|
||||
textwrap.dedent(expected_output).lstrip('\n'))
|
||||
|
||||
def testUsageOutputFunctionWithDocstring(self):
|
||||
component = tc.multiplier_with_docstring
|
||||
t = trace.FireTrace(component, name='multiplier_with_docstring')
|
||||
usage_output = helptext.UsageText(component, trace=t, verbose=True)
|
||||
expected_output = '''
|
||||
Usage: multiplier_with_docstring NUM <flags>
|
||||
|
||||
Available flags: --rate
|
||||
|
||||
For detailed information on this command, run:
|
||||
multiplier_with_docstring --help
|
||||
'''
|
||||
self.assertEqual(
|
||||
usage_output,
|
||||
textwrap.dedent(expected_output).lstrip('\n'))
|
||||
|
||||
@testutils.skip('The functionality is not implemented yet')
|
||||
def testUsageOutputCallable(self):
|
||||
# This is both a group and a command!
|
||||
component = tc.CallableWithKeywordArgument
|
||||
t = trace.FireTrace(component, name='CallableWithKeywordArgument')
|
||||
usage_output = helptext.UsageText(component, trace=t, verbose=True)
|
||||
# TODO(zuhaohen): We need to handle the case for keyword args as well
|
||||
# i.e. __call__ method of CallableWithKeywordArgument
|
||||
expected_output = '''
|
||||
Usage: CallableWithKeywordArgument <commands>
|
||||
|
||||
Available commands: print_msg
|
||||
|
||||
For detailed information on this command, run:
|
||||
CallableWithKeywordArgument -- --help
|
||||
'''
|
||||
self.assertEqual(
|
||||
usage_output,
|
||||
textwrap.dedent(expected_output).lstrip('\n'))
|
||||
|
||||
def testUsageOutputConstructorWithParameter(self):
|
||||
component = tc.InstanceVars
|
||||
t = trace.FireTrace(component, name='InstanceVars')
|
||||
usage_output = helptext.UsageText(component, trace=t, verbose=True)
|
||||
expected_output = '''
|
||||
Usage: InstanceVars ARG1 ARG2
|
||||
|
||||
For detailed information on this command, run:
|
||||
InstanceVars --help
|
||||
'''
|
||||
self.assertEqual(
|
||||
usage_output,
|
||||
textwrap.dedent(expected_output).lstrip('\n'))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
testutils.main()
|
||||
+2
-379
@@ -38,7 +38,6 @@ import inspect
|
||||
from fire import completion
|
||||
from fire import docstrings
|
||||
from fire import inspectutils
|
||||
from fire import value_types
|
||||
|
||||
|
||||
def _NormalizeField(field):
|
||||
@@ -119,22 +118,13 @@ def HelpString(component, trace=None, verbose=False):
|
||||
String suitable for display giving information about the component.
|
||||
"""
|
||||
info = inspectutils.Info(component)
|
||||
# TODO(dbieber): Stop using UsageString in favor of UsageText.
|
||||
info['usage'] = UsageString(component, trace, verbose)
|
||||
info['docstring_info'] = docstrings.parse(info['docstring'])
|
||||
|
||||
is_error_screen = False
|
||||
if trace:
|
||||
is_error_screen = trace.HasError()
|
||||
|
||||
if is_error_screen:
|
||||
# TODO(dbieber): Call UsageText instead of CommonHelpText once ready.
|
||||
return _CommonHelpText(info, trace)
|
||||
else:
|
||||
return _HelpText(info, trace)
|
||||
return _HelpText(info, trace)
|
||||
|
||||
|
||||
def _CommonHelpText(info, trace=None):
|
||||
def _HelpText(info, trace=None):
|
||||
"""Returns help text.
|
||||
|
||||
This was a copy of previous HelpString function and will be removed once the
|
||||
@@ -146,8 +136,6 @@ def _CommonHelpText(info, trace=None):
|
||||
Returns:
|
||||
String suitable for display giving information about the component.
|
||||
"""
|
||||
# TODO(joejoevictor): Currently this is just a copy of existing
|
||||
# HelpString method. We will reimplement this further in later CLs.
|
||||
fields = _GetFields(trace)
|
||||
|
||||
try:
|
||||
@@ -193,371 +181,6 @@ def GetCurrentCommand(trace=None):
|
||||
return current_command
|
||||
|
||||
|
||||
def HelpText(component, info, trace=None, verbose=False):
|
||||
if inspect.isroutine(component) or inspect.isclass(component):
|
||||
return HelpTextForFunction(component, info, trace)
|
||||
else:
|
||||
return HelpTextForObject(component, info, trace, verbose)
|
||||
|
||||
|
||||
def HelpTextForFunction(component, info, trace=None, verbose=False):
|
||||
"""Returns detail help text for a function component.
|
||||
|
||||
Args:
|
||||
component: Current component to generate help text for.
|
||||
info: Info containing metadata of component.
|
||||
trace: FireTrace object that leads to current component.
|
||||
verbose: Whether to display help text in verbose mode.
|
||||
|
||||
Returns:
|
||||
Formatted help text for display.
|
||||
"""
|
||||
# TODO(joejoevictor): Implement verbose related output
|
||||
del verbose
|
||||
|
||||
current_command = GetCurrentCommand(trace)
|
||||
summary, description = GetSummaryAndDescription(info['docstring_info'])
|
||||
spec = inspectutils.GetFullArgSpec(component)
|
||||
args = spec.args
|
||||
|
||||
if spec.defaults is None:
|
||||
num_defaults = 0
|
||||
else:
|
||||
num_defaults = len(spec.defaults)
|
||||
args_with_no_defaults = args[:len(args) - num_defaults]
|
||||
|
||||
# TODO(joejoevictor): Generate flag section using these
|
||||
# args_with_defaults = args[len(args) - num_defaults:]
|
||||
# flags = args_with_defaults + spec.kwonlyargs
|
||||
|
||||
output_template = """NAME
|
||||
{name_section}
|
||||
|
||||
SYNOPSIS
|
||||
{synopsis_section}
|
||||
|
||||
DESCRIPTION
|
||||
{description_section}
|
||||
{args_and_flags_section}
|
||||
NOTES
|
||||
You could also use flags syntax for POSITIONAL ARGUMENTS
|
||||
"""
|
||||
|
||||
# Name section
|
||||
name_section_template = '{current_command}{command_summary}'
|
||||
command_summary_str = ' - ' + summary if summary else ''
|
||||
name_section = name_section_template.format(
|
||||
current_command=current_command, command_summary=command_summary_str)
|
||||
|
||||
items = [arg.upper() for arg in args_with_no_defaults]
|
||||
args_and_flags = ' '.join(items)
|
||||
|
||||
# Synopsis section
|
||||
synopsis_section_template = '{current_command} {args_and_flags}'
|
||||
positional_arguments = '|'.join(args)
|
||||
if positional_arguments:
|
||||
positional_arguments = ' ' + positional_arguments
|
||||
synopsis_section = synopsis_section_template.format(
|
||||
current_command=current_command, args_and_flags=args_and_flags)
|
||||
|
||||
# Description section
|
||||
description_section = description if description else summary
|
||||
|
||||
args_and_flags_section = ''
|
||||
|
||||
# Positional arguments and flags section
|
||||
|
||||
pos_arg_template = """
|
||||
POSITIONAL ARGUMENTS
|
||||
{items}
|
||||
"""
|
||||
pos_arg_items = []
|
||||
for arg in args_with_no_defaults:
|
||||
item_template = ' {arg_name}\n {arg_description}\n'
|
||||
arg_description = None
|
||||
for arg_in_docstring in info['docstring_info'].args:
|
||||
if arg_in_docstring.name == arg:
|
||||
arg_description = arg_in_docstring.description
|
||||
|
||||
item = item_template.format(
|
||||
arg_name=arg.upper(), arg_description=arg_description)
|
||||
pos_arg_items.append(item)
|
||||
if pos_arg_items:
|
||||
args_and_flags_section += pos_arg_template.format(
|
||||
items='\n'.join(pos_arg_items).rstrip('\n'))
|
||||
|
||||
return output_template.format(
|
||||
name_section=name_section,
|
||||
synopsis_section=synopsis_section,
|
||||
description_section=description_section,
|
||||
args_and_flags_section=args_and_flags_section)
|
||||
|
||||
|
||||
def HelpTextForObject(component, info, trace=None, verbose=False):
|
||||
"""Generates help text for python objects.
|
||||
|
||||
Args:
|
||||
component: Current component to generate help text for.
|
||||
info: Info containing metadata of component.
|
||||
trace: FireTrace object that leads to current component.
|
||||
verbose: Whether to display help text in verbose mode.
|
||||
|
||||
Returns:
|
||||
Formatted help text for display.
|
||||
"""
|
||||
|
||||
output_template = """NAME
|
||||
{current_command} - {command_summary}
|
||||
|
||||
SYNOPSIS
|
||||
{synopsis}
|
||||
|
||||
DESCRIPTION
|
||||
{command_description}
|
||||
{detail_section}
|
||||
"""
|
||||
|
||||
current_command = GetCurrentCommand(trace)
|
||||
|
||||
docstring_info = info['docstring_info']
|
||||
command_summary = docstring_info.summary if docstring_info.summary else ''
|
||||
if docstring_info.description:
|
||||
command_description = docstring_info.description
|
||||
else:
|
||||
command_description = ''
|
||||
|
||||
groups = []
|
||||
commands = []
|
||||
values = []
|
||||
members = completion._Members(component, verbose) # pylint: disable=protected-access
|
||||
for member_name, member in members:
|
||||
if value_types.IsGroup(member):
|
||||
groups.append((member_name, member))
|
||||
if value_types.IsCommand(member):
|
||||
commands.append((member_name, member))
|
||||
if value_types.IsValue(member):
|
||||
values.append((member_name, member))
|
||||
|
||||
possible_actions = []
|
||||
# TODO(joejoevictor): Add global flags to here. Also, if it's a callable,
|
||||
# there will be additional flags.
|
||||
possible_flags = ''
|
||||
detail_section_string = ''
|
||||
item_template = """
|
||||
{name}
|
||||
{command_summary}
|
||||
"""
|
||||
|
||||
if groups:
|
||||
# TODO(joejoevictor): Add missing GROUPS section handling
|
||||
possible_actions.append('GROUP')
|
||||
if commands:
|
||||
possible_actions.append('COMMAND')
|
||||
commands_str_template = """
|
||||
COMMANDS
|
||||
COMMAND is one of the followings:
|
||||
{items}
|
||||
"""
|
||||
command_item_strings = []
|
||||
for command_name, command in commands:
|
||||
command_docstring_info = docstrings.parse(
|
||||
inspectutils.Info(command)['docstring'])
|
||||
command_item_strings.append(
|
||||
item_template.format(
|
||||
name=command_name,
|
||||
command_summary=command_docstring_info.summary))
|
||||
detail_section_string += commands_str_template.format(
|
||||
items=('\n'.join(command_item_strings)).rstrip('\n'))
|
||||
|
||||
if values:
|
||||
possible_actions.append('VALUES')
|
||||
values_str_template = """
|
||||
VALUES
|
||||
VALUE is one of the followings:
|
||||
{items}
|
||||
"""
|
||||
value_item_strings = []
|
||||
for value_name, value in values:
|
||||
del value
|
||||
init_docstring_info = docstrings.parse(
|
||||
inspectutils.Info(component.__class__.__init__)['docstring'])
|
||||
for arg_info in init_docstring_info.args:
|
||||
if arg_info.name == value_name:
|
||||
value_item_strings.append(
|
||||
item_template.format(
|
||||
name=value_name, command_summary=arg_info.description))
|
||||
detail_section_string += values_str_template.format(
|
||||
items=('\n'.join(value_item_strings)).rstrip('\n'))
|
||||
|
||||
possible_actions_string = ' ' + (' | '.join(possible_actions))
|
||||
|
||||
synopsis_template = '{current_command}{possible_actions}{possible_flags}'
|
||||
synopsis_string = synopsis_template.format(
|
||||
current_command=current_command,
|
||||
possible_actions=possible_actions_string,
|
||||
possible_flags=possible_flags)
|
||||
|
||||
return output_template.format(
|
||||
current_command=current_command,
|
||||
command_summary=command_summary,
|
||||
synopsis=synopsis_string,
|
||||
command_description=command_description,
|
||||
detail_section=detail_section_string)
|
||||
|
||||
|
||||
def UsageText(component, trace=None, verbose=False):
|
||||
if inspect.isroutine(component) or inspect.isclass(component):
|
||||
return UsageTextForFunction(component, trace)
|
||||
else:
|
||||
return UsageTextForObject(component, trace, verbose)
|
||||
|
||||
|
||||
def UsageTextForFunction(component, trace=None):
|
||||
"""Returns usage text for function objects.
|
||||
|
||||
Args:
|
||||
component: The component to determine the usage text for.
|
||||
trace: The Fire trace object containing all metadata of current execution.
|
||||
|
||||
Returns:
|
||||
String suitable for display in error screen.
|
||||
"""
|
||||
|
||||
output_template = """Usage: {current_command} {args_and_flags}
|
||||
{availability_lines}
|
||||
For detailed information on this command, run:
|
||||
{current_command}{hyphen_hyphen} --help
|
||||
"""
|
||||
|
||||
if trace:
|
||||
command = trace.GetCommand()
|
||||
is_help_an_arg = trace.NeedsSeparatingHyphenHyphen()
|
||||
else:
|
||||
command = None
|
||||
is_help_an_arg = False
|
||||
|
||||
if not command:
|
||||
command = ''
|
||||
|
||||
spec = inspectutils.GetFullArgSpec(component)
|
||||
args = spec.args
|
||||
if spec.defaults is None:
|
||||
num_defaults = 0
|
||||
else:
|
||||
num_defaults = len(spec.defaults)
|
||||
args_with_no_defaults = args[:len(args) - num_defaults]
|
||||
args_with_defaults = args[len(args) - num_defaults:]
|
||||
flags = args_with_defaults + spec.kwonlyargs
|
||||
|
||||
items = [arg.upper() for arg in args_with_no_defaults]
|
||||
if flags:
|
||||
items.append('<flags>')
|
||||
availability_lines = (
|
||||
'\nAvailable flags: '
|
||||
+ ' | '.join('--' + flag for flag in flags) + '\n')
|
||||
else:
|
||||
availability_lines = ''
|
||||
args_and_flags = ' '.join(items)
|
||||
|
||||
hyphen_hyphen = ' --' if is_help_an_arg else ''
|
||||
|
||||
return output_template.format(
|
||||
current_command=command,
|
||||
args_and_flags=args_and_flags,
|
||||
availability_lines=availability_lines,
|
||||
hyphen_hyphen=hyphen_hyphen)
|
||||
|
||||
|
||||
def UsageTextForObject(component, trace=None, verbose=False):
|
||||
"""Returns help text for usage screen for objects.
|
||||
|
||||
Construct help text for usage screen to inform the user about error occurred
|
||||
and correct syntax for invoking the object.
|
||||
|
||||
Args:
|
||||
component: The component to determine the usage text for.
|
||||
trace: The Fire trace object containing all metadata of current execution.
|
||||
verbose: Whether to include private members in the usage text.
|
||||
Returns:
|
||||
String suitable for display in error screen.
|
||||
"""
|
||||
output_template = """Usage: {current_command} <{possible_actions}>
|
||||
{availability_lines}
|
||||
|
||||
For detailed information on this command, run:
|
||||
{current_command} --help
|
||||
"""
|
||||
if trace:
|
||||
command = trace.GetCommand()
|
||||
else:
|
||||
command = None
|
||||
|
||||
if not command:
|
||||
command = ''
|
||||
|
||||
groups = []
|
||||
commands = []
|
||||
values = []
|
||||
|
||||
members = completion._Members(component, verbose) # pylint: disable=protected-access
|
||||
for member_name, member in members:
|
||||
if value_types.IsGroup(member):
|
||||
groups.append(member_name)
|
||||
if value_types.IsCommand(member):
|
||||
commands.append(member_name)
|
||||
if value_types.IsValue(member):
|
||||
values.append(member_name)
|
||||
|
||||
possible_actions = []
|
||||
availability_lines = []
|
||||
availability_lint_format = '{header:20s}{choices}'
|
||||
if groups:
|
||||
possible_actions.append('groups')
|
||||
groups_string = ' | '.join(groups)
|
||||
groups_text = availability_lint_format.format(
|
||||
header='available groups:',
|
||||
choices=groups_string)
|
||||
availability_lines.append(groups_text)
|
||||
if commands:
|
||||
possible_actions.append('commands')
|
||||
commands_string = ' | '.join(commands)
|
||||
commands_text = availability_lint_format.format(
|
||||
header='available commands:',
|
||||
choices=commands_string)
|
||||
availability_lines.append(commands_text)
|
||||
if values:
|
||||
possible_actions.append('values')
|
||||
values_string = ' | '.join(values)
|
||||
values_text = availability_lint_format.format(
|
||||
header='available values:',
|
||||
choices=values_string)
|
||||
availability_lines.append(values_text)
|
||||
possible_actions_string = '|'.join(possible_actions)
|
||||
availability_lines_string = '\n'.join(availability_lines)
|
||||
|
||||
return output_template.format(
|
||||
current_command=command,
|
||||
possible_actions=possible_actions_string,
|
||||
availability_lines=availability_lines_string)
|
||||
|
||||
|
||||
def _HelpText(info, trace=None):
|
||||
"""Returns help text for extensive help screen.
|
||||
|
||||
Construct help text for help screen when user explicitly requesting help by
|
||||
having -h, --help in the command sequence.
|
||||
|
||||
Args:
|
||||
info: The IR object containing metadata of an object.
|
||||
trace: The Fire trace object containing all metadata of current execution.
|
||||
Returns:
|
||||
String suitable for display in extensive help screen.
|
||||
"""
|
||||
|
||||
# TODO(joejoevictor): Implement real help text construction.
|
||||
return _CommonHelpText(info, trace)
|
||||
|
||||
|
||||
def _UsageStringFromFullArgSpec(command, spec):
|
||||
"""Get a usage string from the FullArgSpec for the given command.
|
||||
|
||||
|
||||
@@ -19,14 +19,10 @@ from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import textwrap
|
||||
|
||||
from fire import docstrings
|
||||
from fire import helputils
|
||||
from fire import inspectutils
|
||||
from fire import test_components as tc
|
||||
from fire import testutils
|
||||
from fire import trace
|
||||
import six
|
||||
|
||||
|
||||
@@ -131,179 +127,5 @@ class HelpUtilsTest(testutils.BaseTestCase):
|
||||
self.assertIn('Line: ', helpstring)
|
||||
|
||||
|
||||
class HelpScreenTest(testutils.BaseTestCase):
|
||||
|
||||
def testHelpScreen(self):
|
||||
component = tc.ClassWithDocstring()
|
||||
t = trace.FireTrace(component, name='ClassWithDocstring')
|
||||
info = inspectutils.Info(component)
|
||||
info['docstring_info'] = docstrings.parse(info['docstring'])
|
||||
help_output = helputils.HelpText(component, info, t)
|
||||
expected_output = """
|
||||
NAME
|
||||
ClassWithDocstring - Test class for testing help text output.
|
||||
|
||||
SYNOPSIS
|
||||
ClassWithDocstring COMMAND | VALUES
|
||||
|
||||
DESCRIPTION
|
||||
This is some detail description of this test class.
|
||||
|
||||
COMMANDS
|
||||
COMMAND is one of the followings:
|
||||
|
||||
print_msg
|
||||
Prints a message.
|
||||
|
||||
VALUES
|
||||
VALUE is one of the followings:
|
||||
|
||||
message
|
||||
The default message to print.
|
||||
|
||||
"""
|
||||
self.assertEqual(textwrap.dedent(expected_output).lstrip('\n'), help_output)
|
||||
|
||||
def testHelpScreenWithLineBreak(self):
|
||||
component = tc.ClassWithMultilineDocstring.example_generator
|
||||
t = trace.FireTrace(component, name='example_generator')
|
||||
info = inspectutils.Info(component)
|
||||
info['docstring_info'] = docstrings.parse(info['docstring'])
|
||||
help_output = helputils.HelpText(component, info, t)
|
||||
expected_output = """
|
||||
NAME
|
||||
example_generator - Generators have a ``Yields`` section instead of a ``Returns`` section.
|
||||
|
||||
SYNOPSIS
|
||||
example_generator N
|
||||
|
||||
DESCRIPTION
|
||||
Generators have a ``Yields`` section instead of a ``Returns`` section.
|
||||
|
||||
POSITIONAL ARGUMENTS
|
||||
N
|
||||
The upper limit of the range to generate, from 0 to `n` - 1.
|
||||
|
||||
NOTES
|
||||
You could also use flags syntax for POSITIONAL ARGUMENTS
|
||||
"""
|
||||
self.assertEqual(textwrap.dedent(expected_output).lstrip('\n'), help_output)
|
||||
|
||||
|
||||
class UsageTest(testutils.BaseTestCase):
|
||||
|
||||
def testUsageOutput(self):
|
||||
component = tc.NoDefaults()
|
||||
t = trace.FireTrace(component, name='NoDefaults')
|
||||
usage_output = helputils.UsageText(component, trace=t, verbose=False)
|
||||
expected_output = '''
|
||||
Usage: NoDefaults <commands>
|
||||
available commands: double | triple
|
||||
|
||||
For detailed information on this command, run:
|
||||
NoDefaults --help
|
||||
'''
|
||||
|
||||
self.assertEqual(
|
||||
usage_output,
|
||||
textwrap.dedent(expected_output).lstrip('\n'))
|
||||
|
||||
def testUsageOutputVerbose(self):
|
||||
component = tc.NoDefaults()
|
||||
t = trace.FireTrace(component, name='NoDefaults')
|
||||
usage_output = helputils.UsageText(component, trace=t, verbose=True)
|
||||
expected_output = '''
|
||||
Usage: NoDefaults <commands>
|
||||
available commands: double | triple
|
||||
|
||||
For detailed information on this command, run:
|
||||
NoDefaults --help
|
||||
'''
|
||||
self.assertEqual(
|
||||
usage_output,
|
||||
textwrap.dedent(expected_output).lstrip('\n'))
|
||||
|
||||
def testUsageOutputMethod(self):
|
||||
component = tc.NoDefaults().double
|
||||
t = trace.FireTrace(component, name='NoDefaults')
|
||||
t.AddAccessedProperty(component, 'double', ['double'], None, None)
|
||||
usage_output = helputils.UsageText(component, trace=t, verbose=True)
|
||||
expected_output = '''
|
||||
Usage: NoDefaults double COUNT
|
||||
|
||||
For detailed information on this command, run:
|
||||
NoDefaults double --help
|
||||
'''
|
||||
self.assertEqual(
|
||||
usage_output,
|
||||
textwrap.dedent(expected_output).lstrip('\n'))
|
||||
|
||||
def testUsageOutputFunctionWithHelp(self):
|
||||
component = tc.function_with_help
|
||||
t = trace.FireTrace(component, name='function_with_help')
|
||||
usage_output = helputils.UsageText(component, trace=t, verbose=True)
|
||||
expected_output = '''
|
||||
Usage: function_with_help <flags>
|
||||
|
||||
Available flags: --help
|
||||
|
||||
For detailed information on this command, run:
|
||||
function_with_help -- --help
|
||||
'''
|
||||
self.assertEqual(
|
||||
usage_output,
|
||||
textwrap.dedent(expected_output).lstrip('\n'))
|
||||
|
||||
def testUsageOutputFunctionWithDocstring(self):
|
||||
component = tc.multiplier_with_docstring
|
||||
t = trace.FireTrace(component, name='multiplier_with_docstring')
|
||||
usage_output = helputils.UsageText(component, trace=t, verbose=True)
|
||||
expected_output = '''
|
||||
Usage: multiplier_with_docstring NUM <flags>
|
||||
|
||||
Available flags: --rate
|
||||
|
||||
For detailed information on this command, run:
|
||||
multiplier_with_docstring --help
|
||||
'''
|
||||
self.assertEqual(
|
||||
usage_output,
|
||||
textwrap.dedent(expected_output).lstrip('\n'))
|
||||
|
||||
@testutils.skip('The functionality is not implemented yet')
|
||||
def testUsageOutputCallable(self):
|
||||
# This is both a group and a command!
|
||||
component = tc.CallableWithKeywordArgument
|
||||
t = trace.FireTrace(component, name='CallableWithKeywordArgument')
|
||||
usage_output = helputils.UsageText(component, trace=t, verbose=True)
|
||||
# TODO(zuhaohen): We need to handle the case for keyword args as well
|
||||
# i.e. __call__ method of CallableWithKeywordArgument
|
||||
expected_output = '''
|
||||
Usage: CallableWithKeywordArgument <commands>
|
||||
|
||||
Available commands: print_msg
|
||||
|
||||
For detailed information on this command, run:
|
||||
CallableWithKeywordArgument -- --help
|
||||
'''
|
||||
self.assertEqual(
|
||||
usage_output,
|
||||
textwrap.dedent(expected_output).lstrip('\n'))
|
||||
|
||||
def testUsageOutputConstructorWithParameter(self):
|
||||
component = tc.InstanceVars
|
||||
t = trace.FireTrace(component, name='InstanceVars')
|
||||
usage_output = helputils.UsageText(component, trace=t, verbose=True)
|
||||
expected_output = '''
|
||||
Usage: InstanceVars ARG1 ARG2
|
||||
|
||||
For detailed information on this command, run:
|
||||
InstanceVars --help
|
||||
'''
|
||||
self.assertEqual(
|
||||
usage_output,
|
||||
textwrap.dedent(expected_output).lstrip('\n'))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
testutils.main()
|
||||
|
||||
+1
-1
@@ -94,7 +94,7 @@ def _LiteralEval(value):
|
||||
SyntaxError: If the value string has a syntax error.
|
||||
"""
|
||||
root = ast.parse(value, mode='eval')
|
||||
if isinstance(root.body, ast.BinOp):
|
||||
if isinstance(root.body, ast.BinOp): # pytype: disable=attribute-error
|
||||
raise ValueError(value)
|
||||
|
||||
for node in ast.walk(root):
|
||||
|
||||
@@ -87,8 +87,17 @@ class NoDefaults(object):
|
||||
|
||||
|
||||
class WithDefaults(object):
|
||||
"""Class with functions that have default arguments."""
|
||||
|
||||
def double(self, count=0):
|
||||
"""Returns the input multiplied by 2.
|
||||
|
||||
Args:
|
||||
count: Input number that you want to double.
|
||||
|
||||
Returns:
|
||||
A number that is the double of count.s
|
||||
"""
|
||||
return 2 * count
|
||||
|
||||
def triple(self, count=0):
|
||||
|
||||
@@ -65,7 +65,9 @@ class FireTrace(object):
|
||||
|
||||
def GetResult(self):
|
||||
"""Returns the component from the last element of the trace."""
|
||||
# pytype: disable=attribute-error
|
||||
return self.GetLastHealthyElement().component
|
||||
# pytype: enable=attribute-error
|
||||
|
||||
def GetLastHealthyElement(self):
|
||||
"""Returns the last element of the trace that is not an error.
|
||||
|
||||
@@ -9,3 +9,7 @@ test = pytest
|
||||
|
||||
[tool:pytest]
|
||||
addopts = --ignore=fire/test_components_py3.py --ignore=fire/parser_fuzz_test.py
|
||||
|
||||
[pytype]
|
||||
inputs = .
|
||||
output = .pytype
|
||||
|
||||
Reference in New Issue
Block a user