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