"Fossies" - the Fresh Open Source Software Archive 
Member "getmail-5.16/getmailcore/baseclasses.py" (31 Oct 2021, 14670 Bytes) of package /linux/misc/getmail-5.16.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style:
standard) with prefixed line numbers.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "baseclasses.py" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
5.15_vs_5.16.
1 #!/usr/bin/env python2
2 '''Base classes used elsewhere in the package.
3
4 '''
5
6 __all__ = [
7 'ConfigurableBase',
8 'ForkingBase',
9 'ConfInstance',
10 'ConfString',
11 'ConfBool',
12 'ConfInt',
13 'ConfTupleOfStrings',
14 'ConfTupleOfUnicode',
15 'ConfTupleOfTupleOfStrings',
16 'ConfPassword',
17 'ConfDirectory',
18 'ConfFile',
19 'ConfMaildirPath',
20 'ConfMboxPath',
21 ]
22
23 import sys
24 import os
25 import time
26 import signal
27 import types
28
29 from getmailcore.exceptions import *
30 from getmailcore.compatibility import *
31 import getmailcore.logging
32 from getmailcore.utilities import eval_bool, expand_user_vars
33
34 #
35 # Base classes
36 #
37
38
39 class ConfItem:
40 securevalue = False
41 def __init__(self, name, dtype, default=None, required=True):
42 self.log = getmailcore.logging.Logger()
43 self.name = name
44 self.dtype = dtype
45 self.default = default
46 self.required = required
47
48 def validate(self, configuration, val=None):
49 if val is None:
50 # If not passed in by subclass
51 val = configuration.get(self.name, None)
52 if val is None:
53 # Not provided.
54 if self.required:
55 raise getmailConfigurationError(
56 '%s: missing required configuration parameter' % self.name
57 )
58 # Use default.
59 return self.default
60 if type(val) is not self.dtype and val != self.default:
61 # Got value, but not of expected type. Try to convert.
62 if self.securevalue:
63 self.log.debug('converting %s to type %s\n'
64 % (self.name, self.dtype))
65 else:
66 self.log.debug('converting %s (%s) to type %s\n'
67 % (self.name, val, self.dtype))
68
69 try:
70 if self.dtype == bool:
71 val = eval_bool(val)
72 else:
73 val = self.dtype(eval(val))
74 except (ValueError, SyntaxError, TypeError), o:
75 raise getmailConfigurationError(
76 '%s: configuration value (%s) not of required type %s (%s)'
77 % (self.name, val, self.dtype, o)
78 )
79 return val
80
81 class ConfInstance(ConfItem):
82 def __init__(self, name, default=None, required=True):
83 ConfItem.__init__(self, name, types.InstanceType, default=default,
84 required=required)
85
86 class ConfString(ConfItem):
87 def __init__(self, name, default=None, required=True):
88 ConfItem.__init__(self, name, str, default=default, required=required)
89
90 class ConfBool(ConfItem):
91 def __init__(self, name, default=None, required=True):
92 ConfItem.__init__(self, name, bool, default=default, required=required)
93
94 class ConfInt(ConfItem):
95 def __init__(self, name, default=None, required=True):
96 ConfItem.__init__(self, name, int, default=default, required=required)
97
98 class ConfTupleOfStrings(ConfString):
99 def __init__(self, name, default=None, required=True):
100 ConfString.__init__(self, name, default=default, required=required)
101
102 def validate(self, configuration):
103 val = ConfItem.validate(self, configuration)
104 try:
105 if not val:
106 val = '()'
107 tup = eval(val)
108 if type(tup) != tuple:
109 raise ValueError('not a tuple')
110 val = tup
111 except (ValueError, SyntaxError), o:
112 raise getmailConfigurationError(
113 '%s: incorrect format (%s)' % (self.name, o)
114 )
115 result = [str(item) for item in val]
116 return tuple(result)
117
118 class ConfTupleOfUnicode(ConfString):
119 def __init__(self, name, default=None, required=True, allow_specials=()):
120 ConfString.__init__(self, name, default=default, required=required)
121 self.specials = allow_specials
122
123 def validate(self, configuration):
124 _locals = dict([(v, v) for v in self.specials])
125 val = ConfItem.validate(self, configuration)
126 try:
127 if not val:
128 val = '()'
129 tup = eval(val, {}, _locals)
130 if tup in self.specials:
131 val = [tup]
132 else:
133 if type(tup) != tuple:
134 raise ValueError('not a tuple')
135 vals = []
136 for item in tup:
137 item = str(item)
138 try:
139 vals.append(item.decode('ascii'))
140 except UnicodeError, o:
141 try:
142 vals.append(item.decode('utf-8'))
143 except UnicodeError, o:
144 raise ValueError('not ascii or utf-8: %s' % item)
145 val = vals
146 except (ValueError, SyntaxError), o:
147 raise getmailConfigurationError(
148 '%s: incorrect format (%s)' % (self.name, o)
149 )
150 return tuple(val)
151
152 class ConfTupleOfTupleOfStrings(ConfString):
153 def __init__(self, name, default=None, required=True):
154 ConfString.__init__(self, name, default=default, required=required)
155
156 def validate(self, configuration):
157 val = ConfItem.validate(self, configuration)
158 try:
159 if not val:
160 val = '()'
161 tup = eval(val)
162 if type(tup) != tuple:
163 raise ValueError('not a tuple')
164 val = tup
165 except (ValueError, SyntaxError), o:
166 raise getmailConfigurationError(
167 '%s: incorrect format (%s)' % (self.name, o)
168 )
169 for tup in val:
170 if type(tup) != tuple:
171 raise ValueError('contained value "%s" not a tuple' % tup)
172 if len(tup) != 2:
173 raise ValueError('contained value "%s" not length 2' % tup)
174 for part in tup:
175 if type(part) != str:
176 raise ValueError('contained value "%s" has non-string part '
177 '"%s"' % (tup, part))
178
179 return val
180
181 class ConfPassword(ConfString):
182 securevalue = True
183
184 class ConfDirectory(ConfString):
185 def __init__(self, name, default=None, required=True):
186 ConfString.__init__(self, name, default=default, required=required)
187
188 def validate(self, configuration):
189 val = ConfString.validate(self, configuration)
190 if val is None:
191 return None
192 val = expand_user_vars(val)
193 if not os.path.isdir(val):
194 raise getmailConfigurationError(
195 '%s: specified directory "%s" does not exist' % (self.name, val)
196 )
197 return val
198
199 class ConfFile(ConfString):
200 def __init__(self, name, default=None, required=True):
201 ConfString.__init__(self, name, default=default, required=required)
202
203 def validate(self, configuration):
204 val = ConfString.validate(self, configuration)
205 if val is None:
206 return None
207 val = expand_user_vars(val)
208 if not os.path.isfile(val):
209 raise getmailConfigurationError(
210 '%s: specified file "%s" does not exist' % (self.name, val)
211 )
212 return val
213
214 class ConfMaildirPath(ConfDirectory):
215 def validate(self, configuration):
216 val = ConfDirectory.validate(self, configuration)
217 if val is None:
218 return None
219 if not val.endswith('/'):
220 raise getmailConfigurationError(
221 '%s: maildir must end with "/"' % self.name
222 )
223 for subdir in ('cur', 'new', 'tmp'):
224 subdirpath = os.path.join(val, subdir)
225 if not os.path.isdir(subdirpath):
226 raise getmailConfigurationError(
227 '%s: maildir subdirectory "%s" does not exist'
228 % (self.name, subdirpath)
229 )
230 return val
231
232 class ConfMboxPath(ConfString):
233 def __init__(self, name, default=None, required=True):
234 ConfString.__init__(self, name, default=default, required=required)
235
236 def validate(self, configuration):
237 val = ConfString.validate(self, configuration)
238 if val is None:
239 return None
240 val = expand_user_vars(val)
241 if not os.path.isfile(val):
242 raise getmailConfigurationError(
243 '%s: specified mbox file "%s" does not exist' % (self.name, val)
244 )
245 fd = os.open(val, os.O_RDWR)
246 status_old = os.fstat(fd)
247 f = os.fdopen(fd, 'r+b')
248 # Check if it _is_ an mbox file. mbox files must start with "From "
249 # in their first line, or are 0-length files.
250 f.seek(0, 0)
251 first_line = f.readline()
252 if first_line and first_line[:5] != 'From ':
253 # Not an mbox file; abort here
254 raise getmailConfigurationError('%s: not an mboxrd file' % val)
255 # Reset atime and mtime
256 try:
257 os.utime(val, (status_old.st_atime, status_old.st_mtime))
258 except OSError, o:
259 # Not root or owner; readers will not be able to reliably
260 # detect new mail. But you shouldn't be delivering to
261 # other peoples' mboxes unless you're root, anyways.
262 pass
263
264 return val
265
266
267 #######################################
268 class ConfigurableBase(object):
269 '''Base class for user-configurable classes.
270
271 Sub-classes must provide the following data attributes and methods:
272
273 _confitems - a tuple of dictionaries representing the parameters the class
274 takes. Each dictionary should contain the following key,
275 value pairs:
276 - name - parameter name
277 - type - a type function to compare the parameter value
278 against (i.e. str, int, bool)
279 - default - optional default value. If not preseent, the
280 parameter is required.
281 '''
282
283 def __init__(self, **args):
284 self.log = getmailcore.logging.Logger()
285 self.log.trace()
286 self.conf = {}
287 allowed_params = set([item.name for item in self._confitems])
288 for (name, value) in args.items():
289 if not name in allowed_params:
290 self.log.warning('Warning: ignoring unknown parameter "%s" '
291 '(value: %s)\n' % (name, value))
292 continue
293 if name.lower() == 'password':
294 self.log.trace('setting %s to * (%s)\n' % (name, type(value)))
295 else:
296 self.log.trace('setting %s to "%s" (%s)\n'
297 % (name, value, type(value)))
298 self.conf[name] = value
299 self.__confchecked = False
300 self.checkconf()
301
302 def checkconf(self):
303 self.log.trace()
304 if self.__confchecked:
305 return
306 for item in self._confitems:
307 # New class-based configuration item
308 self.log.trace('checking %s\n' % item.name)
309 self.conf[item.name] = item.validate(self.conf)
310 unknown_params = frozenset(self.conf.keys()).difference(
311 frozenset([item.name for item in self._confitems])
312 )
313 for param in sorted(list(unknown_params), key=str.lower):
314 self.log.warning('Warning: ignoring unknown parameter "%s" '
315 '(value: %s)\n' % (param, self.conf[param]))
316 self.__confchecked = True
317 self.log.trace('done\n')
318
319 def _confstring(self):
320 self.log.trace()
321 confstring = ''
322 names = self.conf.keys()
323 names.sort()
324 for name in names:
325 if name.lower() == 'configparser':
326 continue
327 if confstring:
328 confstring += ', '
329 if name.lower() == 'password':
330 confstring += '%s="*"' % name
331 else:
332 confstring += '%s="%s"' % (name, self.conf[name])
333 return confstring
334
335 #######################################
336 class ForkingBase(object):
337 '''Base class for classes which fork children and wait for them to exit.
338
339 Sub-classes must provide the following data attributes and methods:
340
341 log - an object of type getmailcore.logging.Logger()
342
343 '''
344 def _child_handler(self, sig, stackframe):
345 self.log.trace('handler called for signal %s' % sig)
346 try:
347 pid, r = os.wait()
348 except OSError, o:
349 # No children on SIGCHLD. Can't happen?
350 self.log.warning('handler called, but no children (%s)' % o)
351 return
352 signal.signal(signal.SIGCHLD, self.__orig_handler)
353 self.__child_pid = pid
354 self.__child_status = r
355 self.log.trace('handler reaped child %s with status %s' % (pid, r))
356 self.__child_exited = True
357
358 def _prepare_child(self):
359 self.log.trace('')
360 self.__child_exited = False
361 self.__child_pid = None
362 self.__child_status = None
363 self.__orig_handler = signal.signal(signal.SIGCHLD, self._child_handler)
364
365 def _wait_for_child(self, childpid):
366 while not self.__child_exited:
367 # Could implement a maximum wait time here
368 self.log.trace('waiting for child %d' % childpid)
369 time.sleep(1.0)
370 #raise getmailDeliveryError('failed waiting for commands %s %d (%s)'
371 # % (self.conf['command'], childpid, o))
372 if self.__child_pid != childpid:
373 #self.log.error('got child pid %d, not %d' % (pid, childpid))
374 raise getmailOperationError(
375 'got child pid %d, not %d'
376 % (self.__child_pid, childpid)
377 )
378 if os.WIFSTOPPED(self.__child_status):
379 raise getmailOperationError(
380 'child pid %d stopped by signal %d'
381 % (self.__child_pid, os.WSTOPSIG(self.__child_status))
382 )
383 if os.WIFSIGNALED(self.__child_status):
384 raise getmailOperationError(
385 'child pid %d killed by signal %d'
386 % (self.__child_pid, os.WTERMSIG(self.__child_status))
387 )
388 if not os.WIFEXITED(self.__child_status):
389 raise getmailOperationError('child pid %d failed to exit'
390 % self.__child_pid)
391 exitcode = os.WEXITSTATUS(self.__child_status)
392
393 return exitcode
394
395
396 # For Python 2.3, which lacks the sorted() builtin
397 if sys.hexversion < 0x02040000:
398 def sorted(l, key=lambda x: x):
399 lst = [(key(item), item) for item in l]
400 lst.sort()
401 return [val for (unused, val) in lst]
402 __all__.append('sorted')