blob: 65ab0b481c8a6e36065c22c5989603a7e037c0a2 [file] [log] [blame]
Michael Walshde791732016-09-06 14:25:24 -05001#!/usr/bin/env python
2
Michael Walsh7423c012016-10-04 10:27:21 -05003r"""
4This module provides many valuable functions such as my_parm_file.
5"""
Michael Walshde791732016-09-06 14:25:24 -05006
7# sys and os are needed to get the program dir path and program name.
8import sys
Michael Walsheaa16852017-09-19 16:30:43 -05009import errno
Michael Walshde791732016-09-06 14:25:24 -050010import os
11import ConfigParser
12import StringIO
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060013import re
Michael Walsh1c85bab2017-05-04 14:29:24 -050014import socket
Michael Walshde791732016-09-06 14:25:24 -050015
Michael Walsh7423c012016-10-04 10:27:21 -050016import gen_print as gp
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060017import gen_cmd as gc
18
19
20robot_env = 1
21try:
22 from robot.libraries.BuiltIn import BuiltIn
23except ImportError:
24 robot_env = 0
Michael Walsh7423c012016-10-04 10:27:21 -050025
Michael Walshde791732016-09-06 14:25:24 -050026
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060027def add_trailing_slash(dir_path):
Michael Walsh7db77942017-01-10 11:37:06 -060028
29 r"""
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060030 Add a trailing slash to the directory path if it doesn't already have one
31 and return it.
Michael Walsh7db77942017-01-10 11:37:06 -060032
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060033 Description of arguments:
34 dir_path A directory path.
Michael Walsh7db77942017-01-10 11:37:06 -060035 """
36
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060037 return os.path.normpath(dir_path) + os.path.sep
38
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060039
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060040def 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
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060069
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060070def 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
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060091
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060092def 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
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600127
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600128def 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))
Michael Walsh7db77942017-01-10 11:37:06 -0600152
Michael Walsh7db77942017-01-10 11:37:06 -0600153
Michael Walsh7db77942017-01-10 11:37:06 -0600154def 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
Michael Walsh7db77942017-01-10 11:37:06 -0600183
Michael Walshde791732016-09-06 14:25:24 -0500184def 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:
Michael Walsh7423c012016-10-04 10:27:21 -0500200 prop_file_path The caller should pass the path to the
201 properties file.
Michael Walshde791732016-09-06 14:25:24 -0500202 """
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
Michael Walsh7423c012016-10-04 10:27:21 -0500225
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600226def 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
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600262
Michael Walsh7423c012016-10-04 10:27:21 -0500263def 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
Michael Walsh7db77942017-01-10 11:37:06 -0600276
Michael Walsh7db77942017-01-10 11:37:06 -0600277def quote_bash_parm(parm):
278
279 r"""
280 Return the bash command line parm with single quotes if they are needed.
281
282 Description of arguments:
283 parm The string to be quoted.
284 """
285
286 # If any of these characters are found in the parm string, then the
287 # string should be quoted. This list is by no means complete and should
288 # be expanded as needed by the developer of this function.
289 bash_special_chars = set(' $')
290
291 if any((char in bash_special_chars) for char in parm):
292 return "'" + parm + "'"
293
294 return parm
295
Michael Walsh1c85bab2017-05-04 14:29:24 -0500296
Michael Walsh1c85bab2017-05-04 14:29:24 -0500297def get_host_name_ip(host):
298
299 r"""
300 Get the host name and the IP address for the given host and return them as
301 a tuple.
302
303 Description of argument(s):
Michael Walshd1b6c702017-05-30 17:54:30 -0500304 host The host name or IP address to be obtained.
Michael Walsh1c85bab2017-05-04 14:29:24 -0500305 """
306
307 host_host_name = socket.getfqdn(host)
Michael Walshd1b6c702017-05-30 17:54:30 -0500308 try:
309 host_ip = socket.gethostbyname(host)
310 except socket.gaierror as my_gaierror:
311 message = "Unable to obtain the host name for the following host:" +\
312 "\n" + gp.sprint_var(host)
313 gp.print_error_report(message)
314 raise my_gaierror
Michael Walsh1c85bab2017-05-04 14:29:24 -0500315
316 return host_host_name, host_ip
317
Michael Walsheaa16852017-09-19 16:30:43 -0500318
319def pid_active(pid):
320
321 r"""
322 Return true if pid represents an active pid and false otherwise.
323
324 Description of argument(s):
325 pid The pid whose status is being sought.
326 """
327
328 try:
329 os.kill(int(pid), 0)
330 except OSError as err:
331 if err.errno == errno.ESRCH:
332 # ESRCH == No such process
333 return False
334 elif err.errno == errno.EPERM:
335 # EPERM clearly means there's a process to deny access to
336 return True
337 else:
338 # According to "man 2 kill" possible error values are
339 # (EINVAL, EPERM, ESRCH)
340 raise
341
342 return True