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