1#!/usr/bin/env python
2
3r"""
4This module provides many valuable functions such as my_parm_file.
5"""
6
7# sys and os are needed to get the program dir path and program name.
8import sys
9import errno
10import os
11import ConfigParser
12import StringIO
13import re
14import socket
15
16import gen_print as gp
17import gen_cmd as gc
18
19robot_env = gp.robot_env
20if robot_env:
21    from robot.libraries.BuiltIn import BuiltIn
22
23
24def add_trailing_slash(dir_path):
25
26    r"""
27    Add a trailing slash to the directory path if it doesn't already have one
28    and return it.
29
30    Description of arguments:
31    dir_path                        A directory path.
32    """
33
34    return os.path.normpath(dir_path) + os.path.sep
35
36
37def which(file_path):
38
39    r"""
40    Find the full path of an executable file and return it.
41
42    The PATH environment variable dictates the results of this function.
43
44    Description of arguments:
45    file_path                       The relative file path (e.g. "my_file" or
46                                    "lib/my_file").
47    """
48
49    shell_rc, out_buf = gc.cmd_fnc_u("which " + file_path, quiet=1,
50                                     print_output=0, show_err=0)
51    if shell_rc != 0:
52        error_message = "Failed to find complete path for file \"" +\
53                        file_path + "\".\n"
54        error_message += gp.sprint_var(shell_rc, 1)
55        error_message += out_buf
56        if robot_env:
57            BuiltIn().fail(gp.sprint_error(error_message))
58        else:
59            gp.print_error_report(error_message)
60            return False
61
62    file_path = out_buf.rstrip("\n")
63
64    return file_path
65
66
67def dft(value, default):
68
69    r"""
70    Return default if value is None.  Otherwise, return value.
71
72    This is really just shorthand as shown below.
73
74    dft(value, default)
75
76    vs
77
78    default if value is None else value
79
80    Description of arguments:
81    value                           The value to be returned.
82    default                         The default value to return if value is
83                                    None.
84    """
85
86    return default if value is None else value
87
88
89def get_mod_global(var_name,
90                   default=None,
91                   mod_name="__main__"):
92
93    r"""
94    Get module global variable value and return it.
95
96    If we are running in a robot environment, the behavior will default to
97    calling get_variable_value.
98
99    Description of arguments:
100    var_name                        The name of the variable whose value is
101                                    sought.
102    default                         The value to return if the global does not
103                                    exist.
104    mod_name                        The name of the module containing the
105                                    global variable.
106    """
107
108    if robot_env:
109        return BuiltIn().get_variable_value("${" + var_name + "}", default)
110
111    try:
112        module = sys.modules[mod_name]
113    except KeyError:
114        gp.print_error_report("Programmer error - The mod_name passed to" +
115                              " this function is invalid:\n" +
116                              gp.sprint_var(mod_name))
117        raise ValueError('Programmer error.')
118
119    if default is None:
120        return getattr(module, var_name)
121    else:
122        return getattr(module, var_name, default)
123
124
125def global_default(var_value,
126                   default=0):
127
128    r"""
129    If var_value is not None, return it.  Otherwise, return the global
130    variable of the same name, if it exists.  If not, return default.
131
132    This is meant for use by functions needing help assigning dynamic default
133    values to their parms.  Example:
134
135    def func1(parm1=None):
136
137        parm1 = global_default(parm1, 0)
138
139    Description of arguments:
140    var_value                       The value being evaluated.
141    default                         The value to be returned if var_value is
142                                    None AND the global variable of the same
143                                    name does not exist.
144    """
145
146    var_name = gp.get_arg_name(0, 1, stack_frame_ix=2)
147
148    return dft(var_value, get_mod_global(var_name, 0))
149
150
151def set_mod_global(var_value,
152                   mod_name="__main__",
153                   var_name=None):
154
155    r"""
156    Set a global variable for a given module.
157
158    Description of arguments:
159    var_value                       The value to set in the variable.
160    mod_name                        The name of the module whose variable is
161                                    to be set.
162    var_name                        The name of the variable to set.  This
163                                    defaults to the name of the variable used
164                                    for var_value when calling this function.
165    """
166
167    try:
168        module = sys.modules[mod_name]
169    except KeyError:
170        gp.print_error_report("Programmer error - The mod_name passed to" +
171                              " this function is invalid:\n" +
172                              gp.sprint_var(mod_name))
173        raise ValueError('Programmer error.')
174
175    if var_name is None:
176        var_name = gp.get_arg_name(None, 1, 2)
177
178    setattr(module, var_name, var_value)
179
180
181def my_parm_file(prop_file_path):
182
183    r"""
184    Read a properties file, put the keys/values into a dictionary and return
185    the dictionary.
186
187    The properties file must have the following format:
188    var_name<= or :>var_value
189    Comment lines (those beginning with a "#") and blank lines are allowed and
190    will be ignored.  Leading and trailing single or double quotes will be
191    stripped from the value.  E.g.
192    var1="This one"
193    Quotes are stripped so the resulting value for var1 is:
194    This one
195
196    Description of arguments:
197    prop_file_path                  The caller should pass the path to the
198                                    properties file.
199    """
200
201    # ConfigParser expects at least one section header in the file (or you
202    # get ConfigParser.MissingSectionHeaderError).  Properties files don't
203    # need those so I'll write a dummy section header.
204
205    string_file = StringIO.StringIO()
206    # Write the dummy section header to the string file.
207    string_file.write('[dummysection]\n')
208    # Write the entire contents of the properties file to the string file.
209    string_file.write(open(prop_file_path).read())
210    # Rewind the string file.
211    string_file.seek(0, os.SEEK_SET)
212
213    # Create the ConfigParser object.
214    config_parser = ConfigParser.ConfigParser()
215    # Make the property names case-sensitive.
216    config_parser.optionxform = str
217    # Read the properties from the string file.
218    config_parser.readfp(string_file)
219    # Return the properties as a dictionary.
220    return dict(config_parser.items('dummysection'))
221
222
223def file_to_list(file_path,
224                 newlines=0,
225                 comments=1,
226                 trim=0):
227
228    r"""
229    Return the contents of a file as a list.  Each element of the resulting
230    list is one line from the file.
231
232    Description of arguments:
233    file_path                       The path to the file (relative or
234                                    absolute).
235    newlines                        Include newlines from the file in the
236                                    results.
237    comments                        Include comment lines and blank lines in
238                                    the results.  Comment lines are any that
239                                    begin with 0 or more spaces followed by
240                                    the pound sign ("#").
241    trim                            Trim white space from the beginning and
242                                    end of each line.
243    """
244
245    lines = []
246    file = open(file_path)
247    for line in file:
248        if not comments:
249            if re.match(r"[ ]*#|^$", line):
250                continue
251        if not newlines:
252            line = line.rstrip("\n")
253        if trim:
254            line = line.strip()
255        lines.append(line)
256
257    return lines
258
259
260def return_path_list():
261
262    r"""
263    This function will split the PATH environment variable into a PATH_LIST
264    and return it.  Each element in the list will be normalized and have a
265    trailing slash added.
266    """
267
268    PATH_LIST = os.environ['PATH'].split(":")
269    PATH_LIST = [os.path.normpath(path) + os.sep for path in PATH_LIST]
270
271    return PATH_LIST
272
273
274def escape_bash_quotes(buffer):
275
276    r"""
277    Escape quotes in string and return it.
278
279    The escape style implemented will be for use on the bash command line.
280
281    Example:
282    That's all.
283
284    Result:
285    That'\''s all.
286
287    The result may then be single quoted on a bash command.  Example:
288
289    echo 'That'\''s all.'
290
291    Description of argument(s):
292    buffer                          The string whose quotes are to be escaped.
293    """
294
295    return re.sub("\'", "\'\\\'\'", buffer)
296
297
298def quote_bash_parm(parm):
299
300    r"""
301    Return the bash command line parm with single quotes if they are needed.
302
303    Description of arguments:
304    parm                            The string to be quoted.
305    """
306
307    # If any of these characters are found in the parm string, then the
308    # string should be quoted.  This list is by no means complete and should
309    # be expanded as needed by the developer of this function.
310    bash_special_chars = set(' $')
311
312    if any((char in bash_special_chars) for char in parm):
313        return "'" + parm + "'"
314
315    return parm
316
317
318def get_host_name_ip(host):
319
320    r"""
321    Get the host name and the IP address for the given host and return them as
322    a tuple.
323
324    Description of argument(s):
325    host                            The host name or IP address to be obtained.
326    """
327
328    host_host_name = socket.getfqdn(host)
329    try:
330        host_ip = socket.gethostbyname(host)
331    except socket.gaierror as my_gaierror:
332        message = "Unable to obtain the host name for the following host:" +\
333                  "\n" + gp.sprint_var(host)
334        gp.print_error_report(message)
335        raise my_gaierror
336
337    return host_host_name, host_ip
338
339
340def pid_active(pid):
341
342    r"""
343    Return true if pid represents an active pid and false otherwise.
344
345    Description of argument(s):
346    pid                             The pid whose status is being sought.
347    """
348
349    try:
350        os.kill(int(pid), 0)
351    except OSError as err:
352        if err.errno == errno.ESRCH:
353            # ESRCH == No such process
354            return False
355        elif err.errno == errno.EPERM:
356            # EPERM clearly means there's a process to deny access to
357            return True
358        else:
359            # According to "man 2 kill" possible error values are
360            # (EINVAL, EPERM, ESRCH)
361            raise
362
363    return True
364