1#!/usr/bin/env python
2
3r"""
4This file contains functions useful for printing to stdout from robot programs.
5"""
6
7import sys
8import re
9import os
10
11try:
12    from robot.utils import DotDict as my_ord_dict
13except ImportError:
14    from collections import OrderedDict as my_ord_dict
15
16import gen_print as gp
17
18from robot.libraries.BuiltIn import BuiltIn
19from robot.api import logger
20
21try:
22    # The user can set environment variable "GEN_ROBOT_PRINT_DEBUG" to get
23    # debug output from this module.
24    gen_robot_print_debug = int(os.environ['GEN_ROBOT_PRINT_DEBUG'])
25except KeyError:
26    gen_robot_print_debug = 0
27
28
29def rprint(buffer="",
30           stream="STDOUT"):
31
32    r"""
33    rprint stands for "Robot Print".  This keyword will print the user's
34    buffer to the console.  This keyword does not write a linefeed.  It is the
35    responsibility of the caller to include a line feed if desired.  This
36    keyword is essentially an alias for "Log to Console  <string>  <stream>".
37
38    Description of arguments:
39    buffer                          The value that is to written to stdout.
40    """
41
42    BuiltIn().log_to_console(gp.replace_passwords(str(buffer)),
43                             no_newline=True, stream=stream)
44
45
46def rprintn(buffer="",
47            stream='STDOUT'):
48
49    r"""
50    rprintn stands for "Robot print with linefeed".  This keyword will print
51    the user's buffer to the console along with a linefeed.  It is basically
52    an abbreviated form of "Log go Console  <string>  <stream>"
53
54    Description of arguments:
55    buffer                          The value that is to written to stdout.
56    """
57
58    BuiltIn().log_to_console(gp.replace_passwords(buffer), no_newline=False,
59                             stream=stream)
60
61
62def sprint_vars(*args):
63
64    r"""
65    sprint_vars stands for "String Print Vars".  This is a robot redefinition
66    of the sprint_vars function in gen_print.py.  Given a list of variable
67    names, this keyword will string print each variable name and value such
68    that the value lines up in the same column as messages printed with rptime.
69
70    Description of arguments:
71    args:
72        If the first argument is an integer, it will be interpreted to be the
73        "hex" value.
74        If the second argument is an integer, it will be interpreted to be the
75        "indent" value.
76        If the third argument is an integer, it will be interpreted to be the
77        "col1_width" value.
78        All remaining parms are considered variable names which are to be
79        sprinted.
80    """
81
82    if len(args) == 0:
83        return
84
85    # Create list from args (which is a tuple) so that it can be modified.
86    args_list = list(args)
87
88    # See if parm 1 is to be interpreted as "hex".
89    try:
90        if type(int(args_list[0])) is int:
91            hex = int(args_list[0])
92            args_list.pop(0)
93    except ValueError:
94        hex = 0
95
96    # See if parm 2 is to be interpreted as "indent".
97    try:
98        if type(int(args_list[0])) is int:
99            indent = int(args_list[0])
100            args_list.pop(0)
101    except ValueError:
102        indent = 0
103
104    # See if parm 3 is to be interpreted as "col1_width".
105    try:
106        if type(int(args_list[0])) is int:
107            loc_col1_width = int(args_list[0])
108            args_list.pop(0)
109    except ValueError:
110        loc_col1_width = gp.col1_width
111
112    buffer = ""
113    for var_name in args_list:
114        var_value = BuiltIn().get_variable_value("${" + str(var_name) + "}")
115        buffer += gp.sprint_varx(var_name, var_value, hex, indent,
116                                 loc_col1_width)
117
118    return buffer
119
120
121def sprint_pgm_header(indent=0):
122
123    r"""
124    Sprint a standardized header that robot programs should print at the
125    beginning of the run.  The header includes useful information like command
126    line, pid, userid, program parameters, etc.  Callers need to have declared
127    a global @{parm_list} variable which contains the names of all program
128    parameters.
129    """
130
131    # This function is deprecated since the caller may now call the gen_print
132    # version directly.
133    return gp.sprint_pgm_header(indent, linefeed=1)
134
135
136def sprint_error_report(error_text="\n"):
137
138    r"""
139    Print a standardized error report that robot programs should print on
140    failure.  The report includes useful information like error text, command
141    line, pid, userid, program parameters, etc.  Callers must have declared a
142    @{parm_list} variable which contains the names of all program parameters.
143    """
144
145    # This function is deprecated.  The caller is advised to call the
146    # gen_print version of this function directly.
147
148    return gp.sprint_error_report(error_text)
149
150
151def sprint_issuing_keyword(cmd_buf,
152                           test_mode=0):
153
154    r"""
155    Return a line indicating a robot command (i.e. keyword + args) that the
156    program is about to execute.
157
158    For example, for the following robot code...
159
160    @{cmd_buf}=  Set Variable  Set Environment Variable  VAR1  1
161    rdprint_issuing_keyword
162
163    The output would look something like this:
164
165    #(CDT) 2016/10/27 12:04:21 - Issuing: Set Environment Variable  VAR1  1
166
167    Description of args:
168    cmd_buf                         A list containing the keyword and
169                                    arguments to be run.
170    """
171
172    buffer = ""
173    cmd_buf_str = '  '.join([str(element) for element in cmd_buf])
174    buffer += gp.sprint_issuing(cmd_buf_str, int(test_mode))
175
176    return buffer
177
178
179def sprint_auto_vars(headers=0):
180
181    r"""
182    This keyword will string print all of the Automatic Variables described in
183    the Robot User's Guide using rprint_vars.
184
185    NOTE: Not all automatic variables are guaranteed to exist.
186
187    Description of arguments:
188    headers                         This indicates that a header and footer
189                                    should be printed.
190    """
191
192    buffer = ""
193    if int(headers) == 1:
194        buffer += gp.sprint_dashes()
195        buffer += "Automatic Variables:"
196
197    buffer += \
198        sprint_vars(
199            "TEST_NAME", "TEST_TAGS", "TEST_DOCUMENTATION", "TEST_STATUS",
200            "TEST_DOCUMENTATION", "TEST_STATUS", "TEST_MESSAGE",
201            "PREV_TEST_NAME", "PREV_TEST_STATUS", "PREV_TEST_MESSAGE",
202            "SUITE_NAME", "SUITE_SOURCE", "SUITE_DOCUMENTATION",
203            "SUITE_METADATA", "SUITE_STATUS", "SUITE_MESSAGE",
204            "KEYWORD_STATUS", "KEYWORD_MESSAGE", "LOG_LEVEL", "OUTPUT_FILE",
205            "LOG_FILE", "REPORT_FILE", "DEBUG_FILE", "OUTPUT_DIR")
206
207    if int(headers) == 1:
208        buffer += gp.sprint_dashes()
209
210    return buffer
211
212
213# In the following section of code, we will dynamically create robot versions
214# of print functions for each of the sprint functions defined in the
215# gen_print.py module.  So, for example, where we have an sprint_time()
216# function defined above that returns the time to the caller in a string, we
217# will create a corresponding rprint_time() function that will print that
218# string directly to stdout.
219
220# It can be complicated to follow what's being created by the exec statement
221# below.  Here is an example of the rprint_time() function that will be
222# created (as of the time of this writing):
223
224# def rprint_time(*args):
225#   s_func = getattr(gp, "sprint_time")
226#   BuiltIn().log_to_console(s_func(*args),
227#                            stream='STDIN',
228#                            no_newline=True)
229
230# Here are comments describing the lines in the body of the created function.
231# Put a reference to the "s" version of this function in s_func.
232# Call the "s" version of this function passing it all of our arguments.  Log
233# the result to the console.
234
235robot_prefix = "r"
236robot_func_names =\
237    [
238        'print_error_report', 'print_pgm_header',
239        'print_issuing_keyword', 'print_vars', 'print_auto_vars'
240    ]
241func_names = gp.func_names + robot_func_names
242
243explicit_definitions = ['print', 'printn']
244
245func_names = list(my_ord_dict.fromkeys(func_names))
246
247if gen_robot_print_debug:
248    rprintn()
249    BuiltIn().log_to_console(gp.sprint_var(func_names), no_newline=True)
250    rprintn()
251
252for func_name in func_names:
253
254    if func_name not in explicit_definitions:
255        # The print_var function's job is to figure out the name of arg 1 and
256        # then call print_varx.  This is not currently supported for robot
257        # programs.  Though it IS supported for python modules.
258        if func_name == "print_error" or func_name == "print_error_report":
259            output_stream = "STDERR"
260        else:
261            output_stream = "STDIN"
262        if func_name in robot_func_names:
263            object_name = "__import__(__name__)"
264        else:
265            object_name = "gp"
266        func_def = \
267            [
268                "def " + robot_prefix + func_name + "(*args):",
269                "    s_func = getattr(" + object_name + ", \"s" + func_name +
270                "\")",
271                "    BuiltIn().log_to_console" +
272                "(gp.replace_passwords(s_func(*args)),"
273                " stream='" + output_stream + "',"
274                " no_newline=True)"
275            ]
276
277        pgm_definition_string = '\n'.join(func_def)
278        if gen_robot_print_debug:
279            rprintn()
280            rprintn(pgm_definition_string)
281        exec(pgm_definition_string)
282
283    # Now define "q" versions of each print function.  The q functions only
284    # print if global robot var "quiet" is 0.  If the global var quiet is not
285    # defined, it will be treated as though it were "1", i.e. no printing will
286    # be done.
287    func_def = \
288        [
289            "def rq" + func_name + "(*args):",
290            "    if int(gp.get_var_value(None, 0, \"quiet\")): return",
291            "    r" + func_name + "(*args)"
292        ]
293
294    pgm_definition_string = '\n'.join(func_def)
295    if gen_robot_print_debug:
296        rprintn(pgm_definition_string)
297    exec(pgm_definition_string)
298
299    # Now define "d" versions of each print function.  The d functions only
300    # print if global robot var "debug" is 1.
301    func_def = \
302        [
303            "def rd" + func_name + "(*args):",
304            "    if not int(gp.get_var_value(None, 0, \"debug\")): return",
305            "    r" + func_name + "(*args)"
306        ]
307
308    pgm_definition_string = '\n'.join(func_def)
309    if gen_robot_print_debug:
310        rprintn(pgm_definition_string)
311    exec(pgm_definition_string)
312
313    # Create shorter aliases.
314    prefixes = ["", "q", "d"]
315    alias = re.sub("print_", "p", func_name)
316    for prefix2 in prefixes:
317        cmd_buf = robot_prefix + prefix2 + alias + " = " + robot_prefix +\
318            prefix2 + func_name
319        if gen_robot_print_debug:
320            rprintn(cmd_buf)
321        exec(cmd_buf)
322
323# Define an alias.  rpvar is just a special case of rpvars where the args
324# list contains only one element.
325cmd_buf = "rpvar = rpvars"
326if gen_robot_print_debug:
327    rprintn()
328    rprintn(cmd_buf)
329exec(cmd_buf)
330