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