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