1#!/usr/bin/env python
2
3r"""
4This module provides validation functions like valid_value(), valid_integer(),
5etc. for robot programs.
6"""
7
8import re
9import gen_print as gp
10import gen_valid as gv
11import func_args as fa
12
13from robot.libraries.BuiltIn import BuiltIn
14
15
16def valid_var_name(var_name):
17    r"""
18    Validate the robot variable name and return its value.
19
20    If the variable is undefined, this function will print an error message
21    and call BuiltIn().fail().
22
23    Description of arguments():
24    var_name                        The name of the robot variable (e.g.
25                                    "var1").  Do not include "${}" (e.g.
26                                    "${var1}".  Just provide the simple name
27                                    of the variable.
28    """
29
30    # Note: get_variable_value() seems to have no trouble with local variables.
31    var_value = BuiltIn().get_variable_value("${" + var_name + "}")
32    if var_value is None:
33        var_value = "<undefined>"
34        error_message = gv.valid_value(var_value, invalid_values=[var_value],
35                                       var_name=var_name)
36        BuiltIn().fail(error_message)
37
38    return var_value
39
40
41def valid_init(var_name, *args, **kwargs):
42    r"""
43    Do initialization for variable validation and return var_name, args and
44    kwargs.
45
46    This function is to be called by all of the various validation functions
47    in this module.
48
49    This function is designed solely for use by other functions in this file.
50
51    Description of argument(s):
52    var_name                        The name of the variable to be validated.
53    args                            The positional arguments to be passed to a
54                                    validation function.
55    kwargs                          The keyword arguments to be passed to a
56                                    validation function.
57    """
58
59    var_value = valid_var_name(var_name)
60    # Convert python string object definitions to objects (useful for robot
61    # callers).
62    args = fa.args_to_objects(args)
63    kwargs = fa.args_to_objects(kwargs)
64    return var_value, args, kwargs
65
66
67def process_error_message(error_message):
68    r"""
69    Process an error message.
70
71    If error_message is non-blank, fail.  Otherwise, do nothing.
72
73    This function is designed solely for use by other functions in this file.
74
75    Description of argument(s):
76    error_message                   The error message to be processed.
77    """
78
79    if error_message:
80        error_message = gp.sprint_error_report(error_message)
81        BuiltIn().fail(error_message)
82
83
84# The docstring header will be pre-pended to each validation function's
85# existing docstring.
86docstring_header = \
87    r"""
88    Fail if the variable named by var_name is invalid.
89    """
90
91
92def customize_doc_string(doc_string):
93    r"""
94    Customize a gen_valid function docstring and return the result.
95
96    This function is designed solely for use by other functions in this file.
97
98    The caller should pass a docstring from a gen_valid.py validation
99    function.  This docstring will be changed to make a suitable docstring for
100    this module's corresponding validation function.
101
102    For example:
103
104    Let's suppose that gen_valid.py has a function called "valid_value()".
105    This module could make the following call to essentially copy gen_valid's
106    "valid_value()" function, modify it and then assign it to the local
107    version of the valid_value() function.
108
109    valid.__doc__ = customize_doc_string(gv.valid.__doc__)
110
111    Description of argument(s):
112    doc_string                      The docstring to be customized.
113    """
114
115    doc_string = docstring_header + doc_string
116    doc_string = doc_string.split("\n")
117
118    start_ix = 0
119    # Find the "var_value" line.
120    start_ix = next((index for index, value in
121                     enumerate(doc_string[start_ix:], start_ix)
122                     if re.match("[ ]+var_value  ", value)), None)
123    # Replace the "var_value" line with our "var_name" line.
124    doc_string[start_ix] = "    var_name                        " \
125        + "The name of the variable to be validated."
126
127    return "\n".join(doc_string)
128
129
130# All of the following functions are robot wrappers for the equivalent
131# functions defined in gen_valid.py.  Note that the only difference between
132# any two of these locally defined functions is the function name and the
133# gv.<function name> which they call.  Also, note that the docstring for each
134# is created by modifying the docstring from the supporting gen_valid.py
135# function.
136
137def valid_type(var_name, *args, **kwargs):
138
139    var_value, args, kwargs = valid_init(var_name, *args, **kwargs)
140    error_message = \
141        gv.valid_type(var_value, *args, var_name=var_name, **kwargs)
142    process_error_message(error_message)
143
144
145def valid_value(var_name, *args, **kwargs):
146
147    var_value, args, kwargs = valid_init(var_name, *args, **kwargs)
148    error_message = \
149        gv.valid_value(var_value, *args, var_name=var_name, **kwargs)
150    process_error_message(error_message)
151
152
153def valid_range(var_name, *args, **kwargs):
154
155    var_value, args, kwargs = valid_init(var_name, *args, **kwargs)
156    error_message = \
157        gv.valid_range(var_value, *args, var_name=var_name, **kwargs)
158    process_error_message(error_message)
159
160
161def valid_integer(var_name, *args, **kwargs):
162
163    var_value, args, kwargs = valid_init(var_name, *args, **kwargs)
164    error_message = \
165        gv.valid_integer(var_value, *args, var_name=var_name, **kwargs)
166    process_error_message(error_message)
167
168
169def valid_dir_path(var_name, *args, **kwargs):
170
171    var_value, args, kwargs = valid_init(var_name, *args, **kwargs)
172    error_message = \
173        gv.valid_dir_path(var_value, *args, var_name=var_name, **kwargs)
174    process_error_message(error_message)
175
176
177def valid_file_path(var_name, *args, **kwargs):
178
179    var_value, args, kwargs = valid_init(var_name, *args, **kwargs)
180    error_message = \
181        gv.valid_file_path(var_value, *args, var_name=var_name, **kwargs)
182    process_error_message(error_message)
183
184
185def valid_path(var_name, *args, **kwargs):
186
187    var_value, args, kwargs = valid_init(var_name, *args, **kwargs)
188    error_message = \
189        gv.valid_path(var_value, *args, var_name=var_name, **kwargs)
190    process_error_message(error_message)
191
192
193def valid_list(var_name, *args, **kwargs):
194
195    var_value, args, kwargs = valid_init(var_name, *args, **kwargs)
196    error_message = \
197        gv.valid_list(var_value, *args, var_name=var_name, **kwargs)
198    process_error_message(error_message)
199
200
201def valid_dict(var_name, *args, **kwargs):
202
203    var_value, args, kwargs = valid_init(var_name, *args, **kwargs)
204    error_message = \
205        gv.valid_dict(var_value, *args, var_name=var_name, **kwargs)
206    process_error_message(error_message)
207
208
209# Modify the validation function docstrings by calling customize_doc_string
210# for each function in the func_names list.
211func_names = [
212    "valid_type", "valid_value", "valid_range", "valid_integer",
213    "valid_dir_path", "valid_file_path", "valid_path", "valid_list",
214    "valid_dict"
215]
216
217for func_name in func_names:
218    cmd_buf = func_name \
219        + ".__doc__ = customize_doc_string(gv.raw_doc_strings['" \
220        + func_name + "'])"
221    exec(cmd_buf)
222
223# Define aliases for backward compatibility.
224rvalid_value = valid_value
225