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