blob: ded293b8c0665747fdef26a6c043316ce3a3c029 [file] [log] [blame]
#!/usr/bin/env python
r"""
This file contains functions useful for printing to stdout from robot programs.
"""
import sys
import re
import os
try:
from robot.utils import DotDict as my_ord_dict
except ImportError:
from collections import OrderedDict as my_ord_dict
import gen_print as gp
from robot.libraries.BuiltIn import BuiltIn
from robot.api import logger
try:
# The user can set environment variable "GEN_ROBOT_PRINT_DEBUG" to get
# debug output from this module.
gen_robot_print_debug = int(os.environ['GEN_ROBOT_PRINT_DEBUG'])
except KeyError:
gen_robot_print_debug = 0
###############################################################################
def get_quiet_default(var_value,
default=0):
r"""
If var_value is not None, return it. Otherwise, return the global
variable of the same name, if it exists. If not, return default.
This is meant for use by functions needing help assigning dynamic default
values to their parameters. Example:
def func1(parm1=None):
parm1 = global_default(parm1, 0)
Description of arguments:
var_value The value being evaluated.
default The value to be returned if var_value is
None AND the global
variable of the same name does not exist.
"""
var_name = gp.get_arg_name(0, 1, stack_frame_ix=2)
return dft(var_value, get_mod_global(var_name, 0))
###############################################################################
###############################################################################
def set_quiet_default(quiet=None,
default=0):
r"""
Return a default value for the quiet variable based on its current value,
the value of global ${QUIET} and default.
Description of Arguments:
quiet If this is set already, no default value
is chosen. Otherwise, it will be set to
either the global ${QUIET} robot variable
or to default (below).
default The default value to be used if global
${QUIET} does not exist.
"""
if quiet is None:
# Set default quiet value.
try:
quiet = int(BuiltIn().get_variable_value("${quiet}"))
except TypeError:
quiet = int(default)
quiet = int(quiet)
return quiet
###############################################################################
###############################################################################
def get_quiet(default=1):
r"""
Get the value of robot variable "quiet" and return it. If "quiet" is not
defined, the "default" value is returned.
Description of arguments:
default The value that is returned if robot
variable "quiet" is not defined.
"""
try:
quiet = int(BuiltIn().get_variable_value("${quiet}"))
except TypeError:
quiet = default
return quiet
###############################################################################
###############################################################################
def get_debug(default=0):
r"""
Get the value of robot variable "debug" and return it. If "debug" is not
defined, the "default" value is returned.
Description of arguments:
default The value that is returned if robot
variable "debug" is not defined.
"""
try:
debug = int(BuiltIn().get_variable_value("${debug}"))
except TypeError:
debug = default
return debug
###############################################################################
###############################################################################
def rprint(buffer="",
stream="STDOUT"):
r"""
rprint stands for "Robot Print". This keyword will print the user's
buffer to the console. This keyword does not write a linefeed. It is the
responsibility of the caller to include a line feed if desired. This
keyword is essentially an alias for "Log to Console <string> <stream>".
Description of arguments:
buffer The value that is to written to stdout.
"""
BuiltIn().log_to_console(gp.replace_passwords(str(buffer)),
no_newline=True, stream=stream)
###############################################################################
###############################################################################
def rprintn(buffer="",
stream='STDOUT'):
r"""
rprintn stands for "Robot print with linefeed". This keyword will print
the user's buffer to the console along with a linefeed. It is basically
an abbreviated form of "Log go Console <string> <stream>"
Description of arguments:
buffer The value that is to written to stdout.
"""
BuiltIn().log_to_console(gp.replace_passwords(buffer), no_newline=False,
stream=stream)
###############################################################################
###############################################################################
def sprint_vars(*args):
r"""
sprint_vars stands for "String Print Vars". This is a robot redefinition
of the sprint_vars function in gen_print.py. Given a list of variable
names, this keyword will string print each variable name and value such
that the value lines up in the same column as messages printed with rptime.
Description of arguments:
args:
If the first argument is an integer, it will be interpreted to be the
"hex" value.
If the second argument is an integer, it will be interpreted to be the
"indent" value.
If the third argument is an integer, it will be interpreted to be the
"col1_width" value.
All remaining parms are considered variable names which are to be
sprinted.
"""
if len(args) == 0:
return
# Create list from args (which is a tuple) so that it can be modified.
args_list = list(args)
# See if parm 1 is to be interpreted as "hex".
try:
if type(int(args_list[0])) is int:
hex = int(args_list[0])
args_list.pop(0)
except ValueError:
hex = 0
# See if parm 2 is to be interpreted as "indent".
try:
if type(int(args_list[0])) is int:
indent = int(args_list[0])
args_list.pop(0)
except ValueError:
indent = 0
# See if parm 3 is to be interpreted as "col1_width".
try:
if type(int(args_list[0])) is int:
loc_col1_width = int(args_list[0])
args_list.pop(0)
except ValueError:
loc_col1_width = gp.col1_width
buffer = ""
for var_name in args_list:
var_value = BuiltIn().get_variable_value("${" + str(var_name) + "}")
buffer += gp.sprint_varx(var_name, var_value, hex, indent,
loc_col1_width)
return buffer
###############################################################################
###############################################################################
def sprint_pgm_header(indent=0):
r"""
Sprint a standardized header that robot programs should print at the
beginning of the run. The header includes useful information like command
line, pid, userid, program parameters, etc. Callers need to have declared
a global @{parm_list} variable which contains the names of all program
parameters.
"""
loc_col1_width = gp.col1_width + indent
linefeed = 0
rprintn()
suite_name = BuiltIn().get_variable_value("${suite_name}")
buffer = "\n"
buffer += gp.sindent(gp.sprint_time("Running test suite \"" +
suite_name + "\".\n"),
indent)
buffer += gp.sprint_pgm_header(indent, linefeed)
# Get value of global parm_list.
parm_list = BuiltIn().get_variable_value("${parm_list}")
buffer += sprint_vars(0, str(indent), str(loc_col1_width), *parm_list)
buffer += "\n"
# Setting global program_pid.
BuiltIn().set_global_variable("${program_pid}", os.getpid())
return buffer
###############################################################################
###############################################################################
def sprint_error_report(error_text="\n"):
r"""
Print a standardized error report that robot programs should print on
failure. The report includes useful information like error text, command
line, pid, userid, program parameters, etc. Callers must have declared a
@{parm_list} variable which contains the names of all program parameters.
"""
try:
error_report_format = int(BuiltIn().get_variable_value(
"${error_report_format}"))
except TypeError:
error_report_format = 0
# Currently, supported values for error_report_format are:
# 0 - Short form
# 1 - Long form
error_text = error_text.rstrip('\n') + '\n'
if error_report_format == 0:
return gp.sprint_error(error_text)
buffer = ""
buffer += gp.sprint_dashes(width=120, char="=")
buffer += gp.sprint_error(error_text)
indent = 2
linefeed = 0
buffer += sprint_pgm_header(indent)
buffer += gp.sprint_dashes(width=120, char="=")
return buffer
###############################################################################
###############################################################################
def sprint_issuing_keyword(cmd_buf,
test_mode=0):
r"""
Return a line indicating a robot command (i.e. keyword + args) that the
program is about to execute.
For example, for the following robot code...
@{cmd_buf}= Set Variable Set Environment Variable VAR1 1
rdprint_issuing_keyword
The output would look something like this:
#(CDT) 2016/10/27 12:04:21 - Issuing: Set Environment Variable VAR1 1
Description of args:
cmd_buf A list containing the keyword and
arguments to be run.
"""
buffer = ""
cmd_buf_str = ' '.join([str(element) for element in cmd_buf])
buffer += gp.sprint_issuing(cmd_buf_str, int(test_mode))
return buffer
###############################################################################
###############################################################################
def sprint_auto_vars(headers=0):
r"""
This keyword will string print all of the Automatic Variables described in
the Robot User's Guide using rprint_vars.
NOTE: Not all automatic variables are guaranteed to exist.
Description of arguments:
headers This indicates that a header and footer
should be printed.
"""
buffer = ""
if int(headers) == 1:
buffer += gp.sprint_dashes()
buffer += "Automatic Variables:"
buffer += \
sprint_vars(
"TEST_NAME", "TEST_TAGS", "TEST_DOCUMENTATION", "TEST_STATUS",
"TEST_DOCUMENTATION", "TEST_STATUS", "TEST_MESSAGE",
"PREV_TEST_NAME", "PREV_TEST_STATUS", "PREV_TEST_MESSAGE",
"SUITE_NAME", "SUITE_SOURCE", "SUITE_DOCUMENTATION",
"SUITE_METADATA", "SUITE_STATUS", "SUITE_MESSAGE",
"KEYWORD_STATUS", "KEYWORD_MESSAGE", "LOG_LEVEL", "OUTPUT_FILE",
"LOG_FILE", "REPORT_FILE", "DEBUG_FILE", "OUTPUT_DIR")
if int(headers) == 1:
buffer += gp.sprint_dashes()
return buffer
###############################################################################
###############################################################################
# In the following section of code, we will dynamically create robot versions
# of print functions for each of the sprint functions defined in the
# gen_print.py module. So, for example, where we have an sprint_time()
# function defined above that returns the time to the caller in a string, we
# will create a corresponding rprint_time() function that will print that
# string directly to stdout.
# It can be complicated to follow what's being created by the exec statement
# below. Here is an example of the rprint_time() function that will be
# created (as of the time of this writing):
# def rprint_time(*args):
# s_func = getattr(gp, "sprint_time")
# BuiltIn().log_to_console(s_func(*args),
# stream='STDIN',
# no_newline=True)
# Here are comments describing the lines in the body of the created function.
# Put a reference to the "s" version of this function in s_func.
# Call the "s" version of this function passing it all of our arguments. Log
# the result to the console.
robot_prefix = "r"
robot_func_names =\
[
'print_error_report', 'print_pgm_header',
'print_issuing_keyword', 'print_vars', 'print_auto_vars'
]
func_names = gp.func_names + robot_func_names
explicit_definitions = ['print', 'printn']
func_names = list(my_ord_dict.fromkeys(func_names))
if gen_robot_print_debug:
rprintn()
BuiltIn().log_to_console(gp.sprint_var(func_names), no_newline=True)
rprintn()
for func_name in func_names:
if func_name not in explicit_definitions:
# The print_var function's job is to figure out the name of arg 1 and
# then call print_varx. This is not currently supported for robot
# programs. Though it IS supported for python modules.
if func_name == "print_error" or func_name == "print_error_report":
output_stream = "STDERR"
else:
output_stream = "STDIN"
if func_name in robot_func_names:
object_name = "__import__(__name__)"
else:
object_name = "gp"
func_def = \
[
"def " + robot_prefix + func_name + "(*args):",
" s_func = getattr(" + object_name + ", \"s" + func_name +
"\")",
" BuiltIn().log_to_console" +
"(gp.replace_passwords(s_func(*args)),"
" stream='" + output_stream + "',"
" no_newline=True)"
]
pgm_definition_string = '\n'.join(func_def)
if gen_robot_print_debug:
rprintn()
rprintn(pgm_definition_string)
exec(pgm_definition_string)
# Now define "q" versions of each print function. The q functions only
# print if global robot var "quiet" is 0. If the global var quiet is not
# defined, it will be treated as though it were "1", i.e. no printing will
# be done.
func_def = \
[
"def rq" + func_name + "(*args):",
" if get_quiet():",
" return",
" r" + func_name + "(*args)"
]
pgm_definition_string = '\n'.join(func_def)
if gen_robot_print_debug:
rprintn(pgm_definition_string)
exec(pgm_definition_string)
# Now define "d" versions of each print function. The d functions only
# print if global robot var "debug" is 1.
func_def = \
[
"def rd" + func_name + "(*args):",
" if not get_debug():",
" return",
" r" + func_name + "(*args)"
]
pgm_definition_string = '\n'.join(func_def)
if gen_robot_print_debug:
rprintn(pgm_definition_string)
exec(pgm_definition_string)
# Create shorter aliases.
prefixes = ["", "q", "d"]
alias = re.sub("print_", "p", func_name)
for prefix2 in prefixes:
cmd_buf = robot_prefix + prefix2 + alias + " = " + robot_prefix +\
prefix2 + func_name
if gen_robot_print_debug:
rprintn(cmd_buf)
exec(cmd_buf)
# Define an alias. rpvar is just a special case of rpvars where the args
# list contains only one element.
cmd_buf = "rpvar = rpvars"
if gen_robot_print_debug:
rprintn()
rprintn(cmd_buf)
exec(cmd_buf)
###############################################################################