josh-gmail-backup 0.104

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,580 @@
1
+ # Copyright (C) 2008 Jan Svec and Filip Jurcicek
2
+ #
3
+ # YOU USE THIS TOOL ON YOUR OWN RISK!
4
+ #
5
+ # email: info@gmail-backup.com
6
+ #
7
+ #
8
+ # Disclaimer of Warranty
9
+ # ----------------------
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, licensor provides
12
+ # this tool (and each contributor provides its contributions) on an "AS IS"
13
+ # BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14
+ # implied, including, without limitation, any warranties or conditions of
15
+ # TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR
16
+ # PURPOSE. You are solely responsible for determining the appropriateness of
17
+ # using this work and assume any risks associated with your exercise of
18
+ # permissions under this license.
19
+
20
+ import sys
21
+ import os
22
+ import re
23
+ import time
24
+ import getpass
25
+ from copy import copy
26
+ from threading import Thread
27
+ from select import select
28
+ from subprocess import Popen, PIPE
29
+
30
+ from svc.egg import PythonEgg, MetaEgg
31
+ from svc.scripting import *
32
+ from svc.utils import sym, issequence
33
+
34
+ import warnings
35
+
36
+ Generator = sym('Generator')
37
+ AllowOverwrite = sym('AllowOverwrite')
38
+ OmitStdout = sym('OmitStdout')
39
+ AsyncMethod = sym('AsyncMethod')
40
+ # TODO:
41
+ # GNUargs = sym('GNUargs')
42
+ ExecGenerator = sym('ExecGenerator')
43
+ ExecList = sym('ExecList')
44
+ ExecStr = sym('ExecStr')
45
+ ExecNoStdout = sym('ExecNoStdout')
46
+ ExecAsync = sym('ExecAsync')
47
+
48
+ class ExternalError(OSError):
49
+ pass
50
+
51
+ class ExternalMethod(PythonEgg):
52
+ def __init__(self, file_name, name=None, args=[], logger=None, env=None, path=None, etype=ExecGenerator, pre_func=None, post_func=None):
53
+ super(ExternalMethod, self).__init__()
54
+ if path is None:
55
+ self.fileName = file_name
56
+ else:
57
+ self.fileName = os.path.join(path, file_name)
58
+ if name is None:
59
+ self.name = os.path.basename(file_name)
60
+ else:
61
+ self.name = name
62
+ self.args = args
63
+ self.logger = logger
64
+ self.env = env
65
+ if self.getExecuteMethod(etype) is None:
66
+ raise ValueError("Unknown execute type: %r" % etype)
67
+ self.etype = etype
68
+ self.pre_func = pre_func
69
+ self.post_func = post_func
70
+
71
+ def _logStderr(self, line):
72
+ if self.logger:
73
+ self.logger(self, line)
74
+ else:
75
+ sys.stderr.write(line)
76
+
77
+ def getOSEnv(self):
78
+ return dict(os.environ)
79
+
80
+ def getEnv(self):
81
+ e = self.getOSEnv()
82
+ if self._env is not None:
83
+ e.update(self._env)
84
+ return e
85
+
86
+ def setEnv(self, env):
87
+ self._env = env
88
+
89
+ def _strEnv(self, env):
90
+ ret = {}
91
+ for key, value in env.iteritems():
92
+ ret[key] = str(value)
93
+ return ret
94
+
95
+ def _strArgs(self, args):
96
+ return [str(a) for a in args]
97
+
98
+ def getArgs(self):
99
+ return self._args
100
+
101
+ def setArgs(self, args):
102
+ self._args = args
103
+
104
+ def execute(self, *args, **kwargs):
105
+ """Executes program `fn` with cmdline `args` and in environment `env`
106
+
107
+ It would raise an ExternalError if the program `fn` returned non-zero
108
+ exit status. Environment of new process is initialized using
109
+ `self.getEnv` method and updated with `env`.
110
+
111
+ :Returns:
112
+ Generator yielding lines of stdout of `fn`
113
+ """
114
+ stdin = kwargs.pop('stdin', None)
115
+ stdin_file = kwargs.pop('stdin_file', None)
116
+ stdout_file = kwargs.pop('stdout_file', None)
117
+ env = kwargs.pop('env', {})
118
+
119
+ if kwargs:
120
+ raise TypeError("Bad keyword arguments: %s" % kwargs.keys())
121
+
122
+ if stdin is not None and stdin_file is not None:
123
+ raise TypeError("You cannot use both stdin and stdin_file argument")
124
+
125
+ e = dict(self.getEnv())
126
+ e.update(env)
127
+ e = self._strEnv(e)
128
+ args = self._strArgs(self.args + list(args))
129
+
130
+ to_close = []
131
+
132
+ if stdin is not None:
133
+ stdin_func = iter(stdin)
134
+ stdin = PIPE
135
+ elif stdin_file is not None:
136
+ stdin_func = None
137
+ stdin = file(stdin_file, 'r')
138
+ to_close.append(stdin)
139
+ else:
140
+ stdin_func = None
141
+ stdin = None
142
+
143
+ if stdout_file is not None:
144
+ stdout = file(stdout_file, 'w')
145
+ to_close.append(stdout)
146
+ else:
147
+ stdout = PIPE
148
+
149
+ exec_list = [self.fileName] + args
150
+ if self.pre_func is not None:
151
+ self.pre_func(self, exec_list, env)
152
+ try:
153
+ process = Popen(exec_list, shell=False, env=e,
154
+ stdin=stdin, stdout=stdout, stderr=PIPE, bufsize=0)
155
+ except OSError, e:
156
+ raise ExternalError("Couldn't execute external method %r: %s" % (self.name, e))
157
+
158
+ stdin = process.stdin
159
+ stdout = process.stdout
160
+ stderr = process.stderr
161
+ if stdout_file is not None:
162
+ poll_r = [stderr]
163
+ else:
164
+ poll_r = [stdout, stderr]
165
+ if stdin_func is not None:
166
+ poll_w = [stdin]
167
+ else:
168
+ poll_w = []
169
+
170
+ while poll_r or poll_w:
171
+ readable, writeable, errorable = select(poll_r, poll_w, [])
172
+
173
+ if stderr in readable:
174
+ line = stderr.readline()
175
+ if not line:
176
+ stderr.close()
177
+ poll_r.remove(stderr)
178
+ else:
179
+ self._logStderr(line)
180
+ continue
181
+
182
+ if stdout in readable:
183
+ line = stdout.readline()
184
+ if not line:
185
+ stdout.close()
186
+ poll_r.remove(stdout)
187
+ else:
188
+ yield line
189
+
190
+ if stdin in errorable:
191
+ poll_w.remove(stdin)
192
+ elif stdin in writeable:
193
+ try:
194
+ stdin.write(stdin_func.next())
195
+ except StopIteration:
196
+ stdin.close()
197
+ poll_w.remove(stdin)
198
+
199
+ retcode = process.wait()
200
+ for f in to_close:
201
+ f.close()
202
+ if self.post_func is not None:
203
+ self.post_func(self, exec_list, env)
204
+ if retcode < 0:
205
+ raise ExternalError("External method %s (%r) killed by signal (%d)" % (self.name, ' '.join(exec_list), -retcode))
206
+ elif retcode > 0:
207
+ raise ExternalError("External method %s (%r) returned with nonzero exit status (%d)" % (self.name, ' '.join(exec_list), retcode))
208
+
209
+ def executeGenerator(self, *args, **kwargs):
210
+ return self.execute(*args, **kwargs)
211
+
212
+ __iter__ = executeGenerator
213
+
214
+ def executeList(self, *args, **kwargs):
215
+ return list(self.execute(*args, **kwargs))
216
+
217
+ def executeStr(self, *args, **kwargs):
218
+ return ''.join(self.execute(*args, **kwargs))
219
+
220
+ def executeNoStdout(self, *args, **kwargs):
221
+ for foo in self.execute(*args, **kwargs):
222
+ pass
223
+
224
+ def executeAsync(self, *args, **kwargs):
225
+ t = Thread(target=self.executeNoStdout, args=args, kwargs=kwargs)
226
+ t.setDaemon(False)
227
+ t.start()
228
+ return t
229
+
230
+ def getExecuteMethod(self, etype):
231
+ etype = str(etype)
232
+ if not etype.startswith('Exec'):
233
+ return None
234
+ etype = etype[4:]
235
+ return getattr(self, 'execute%s' % etype, None)
236
+
237
+ def __call__(self, *args, **kwargs):
238
+ m = self.getExecuteMethod(self.etype)
239
+ return m(*args, **kwargs)
240
+
241
+ def new(self, *args, **kwargs):
242
+ if 'stdin' in kwargs:
243
+ stdin = kwargs.pop('stdin')
244
+ else:
245
+ stdin = None
246
+
247
+ if 'env' in kwargs:
248
+ env = kwargs.pop('env')
249
+ else:
250
+ env = {}
251
+
252
+ if kwargs:
253
+ raise TypeError("Bad keyword arguments: %s" % kwargs.keys())
254
+
255
+ e = dict(self.getEnv())
256
+ e.update(env)
257
+ e = self._strEnv(e)
258
+ args = self._strArgs(self.args + list(args))
259
+
260
+ ret = copy(self)
261
+ ret.setArgs(args)
262
+ return ret
263
+
264
+
265
+ # Pipeline overloading
266
+ def __or__(left, right):
267
+ return Pipeline( (left, right) )
268
+
269
+ def __ror__(right, left):
270
+ return Pipeline( (left, right) )
271
+
272
+ def __rshift__(self, fn):
273
+ return Pipeline( (self,), redir_stdout=fn)
274
+
275
+ def __rrshift__(self, fn):
276
+ return Pipeline( (self,), redir_stdin=fn)
277
+
278
+ class Pipeline(PythonEgg):
279
+ def __init__(self, pipeline, redir_stdin=None, redir_stdout=None):
280
+ super(Pipeline, self).__init__()
281
+ self.setupPipeline(pipeline, redir_stdin, redir_stdout)
282
+
283
+ def setupPipeline(self, pipeline, redir_stdin, redir_stdout):
284
+ ret = []
285
+
286
+ no_stdin = False
287
+
288
+ for i, item in enumerate(pipeline):
289
+ first = (i==0)
290
+ last = (i==len(pipeline)-1)
291
+
292
+ item_callable = callable(item)
293
+
294
+ if not item_callable:
295
+ if first:
296
+ no_stdin = True
297
+ else:
298
+ raise ValueError("Items in pipeline must be callable")
299
+
300
+ if isinstance(item, Pipeline):
301
+ if first and item.redir_stdin is not None:
302
+ if redir_stdin:
303
+ raise ValueError("Input of pipeline is redirected")
304
+ else:
305
+ redir_stdin = item.redir_stdin
306
+ if last and item.redir_stdout is not None:
307
+ if redir_stdout:
308
+ raise ValueError("Output of pipeline is redirected")
309
+ else:
310
+ redir_stdout = item.redir_stdout
311
+ ret.extend(item._pipeline)
312
+ else:
313
+ if not callable(item):
314
+ if first:
315
+ no_stdin = True
316
+ else:
317
+ raise ValueError("Items in pipeline must be callable")
318
+ ret.append(item)
319
+
320
+ self._pipeline = tuple(ret)
321
+ self.no_stdin = no_stdin
322
+ self.redir_stdin = redir_stdin
323
+ self.redir_stdout = redir_stdout
324
+
325
+ def getOSEnv(self):
326
+ return dict(os.environ)
327
+
328
+ def _strEnv(self, env):
329
+ ret = {}
330
+ for key, value in env.iteritems():
331
+ ret[key] = str(value)
332
+ return ret
333
+
334
+ def execute(self, stdin=None, env={}):
335
+ e = self.getOSEnv()
336
+ e.update(env)
337
+ e = self._strEnv(e)
338
+
339
+ pipeline = self._pipeline
340
+
341
+ if self.no_stdin and stdin is not None:
342
+ raise ValueError("Pipeline doesn't have a stdin")
343
+
344
+ if self.redir_stdin and stdin is not None:
345
+ raise ValueError("Pipeline have a redirected stdin")
346
+
347
+ generator = stdin
348
+ for i, item in enumerate(pipeline):
349
+ first = (i==0)
350
+ last = (i==len(pipeline)-1)
351
+
352
+ if isinstance(item, ExternalMethod):
353
+ if not last:
354
+ method = item.executeGenerator
355
+ else:
356
+ method = item
357
+ kwargs = {}
358
+ if self.redir_stdin is not None and first:
359
+ kwargs['stdin_file'] = self.redir_stdin
360
+ else:
361
+ kwargs['stdin'] = generator
362
+ if self.redir_stdout is not None and last:
363
+ kwargs['stdout_file'] = self.redir_stdout
364
+ generator = method(env=e, **kwargs)
365
+ elif callable(item):
366
+ generator = item(generator)
367
+ else:
368
+ generator = item
369
+ return generator
370
+
371
+ def __call__(self, *args, **kwargs):
372
+ return self.execute(*args, **kwargs)
373
+
374
+ def __iter__(self):
375
+ return self.execute()
376
+
377
+ # Pipeline overloading
378
+ def __or__(left, right):
379
+ return Pipeline( (left, right) )
380
+
381
+ def __ror__(right, left):
382
+ return Pipeline( (left, right) )
383
+
384
+ def __rshift__(self, fn):
385
+ return Pipeline( (self,), redir_stdout=fn)
386
+
387
+ def __rrshift__(self, fn):
388
+ return Pipeline( (self,), redir_stdin=fn)
389
+
390
+ class MetaExternalMethods(MetaEgg):
391
+ def __init__(cls, name, bases, dict):
392
+ super(MetaExternalMethods, cls).__init__(name, bases, dict)
393
+ if cls.externalMethods:
394
+ cls.__bases__ = cls.__bases__ + (cls.createClassExternals(), )
395
+
396
+ def createClassExternals(cls):
397
+ class rClass(object):
398
+ pass
399
+
400
+ rClass.__name__ = '%sExternals' % cls.__name__
401
+ rClass.__module__ = cls.__module__
402
+
403
+ meth_dict = cls.unifyExternalMethods(cls.externalMethods)
404
+ done_dict = {}
405
+
406
+ for dir in cls.externalMethodDirs:
407
+ for meth_name in os.listdir(dir):
408
+ meth_path = os.path.join(dir, meth_name)
409
+ if meth_name in meth_dict:
410
+ # TODO: Check for inconsitencies in method specifiers
411
+ if Generator in meth_dict[meth_name]:
412
+ warnings.warn("Don't use Generator, use ExecGenerator instead", DeprecationWarning, 3)
413
+ meth = cls.createMethod(meth_name, meth_path, ExecGenerator)
414
+ elif OmitStdout in meth_dict[meth_name]:
415
+ warnings.warn("Don't use OmitStdout, use ExecNoStdout instead", DeprecationWarning, 3)
416
+ meth = cls.createMethod(meth_name, meth_path, ExecNoStdout)
417
+ elif AsyncMethod in meth_dict[meth_name]:
418
+ warnings.warn("Don't use AsyncMethod, use ExecAsync instead", DeprecationWarning, 3)
419
+ meth = cls.createMethod(meth_name, meth_path, ExecAsync)
420
+ elif ExecGenerator in meth_dict[meth_name]:
421
+ meth = cls.createMethod(meth_name, meth_path, ExecGenerator)
422
+ elif ExecNoStdout in meth_dict[meth_name]:
423
+ meth = cls.createMethod(meth_name, meth_path, ExecNoStdout)
424
+ elif ExecAsync in meth_dict[meth_name]:
425
+ meth = cls.createMethod(meth_name, meth_path, ExecAsync)
426
+ else:
427
+ meth = cls.createMethod(meth_name, meth_path, ExecList)
428
+
429
+ if ExScript.command in meth_dict[meth_name]:
430
+ meth = ExScript.command(meth)
431
+ child_meth = getattr(cls, meth_name, None)
432
+ if child_meth is not None and not ExScript.isCommand(child_meth):
433
+ raise TypeError("External method %r defined as ExScript.command, but method in derived class %r is not decorated" % \
434
+ (meth_name, cls.__name__))
435
+
436
+ if meth_name in done_dict and AllowOverwrite not in meth_dict[meth_name]:
437
+ raise TypeError("External method %r is defined in two different places: %r and %r" % \
438
+ (meth_name, done_dict[meth_name], dir))
439
+ setattr(rClass, meth_name, meth)
440
+
441
+ done_dict[meth_name] = dir
442
+
443
+ rest = set(meth_dict) - set(done_dict)
444
+ if rest:
445
+ raise ValueError('External method %r not found' % rest.pop())
446
+
447
+ return rClass
448
+
449
+ def unifyExternalMethods(cls, em):
450
+ ret = {}
451
+ for meth_name, specifiers in em.iteritems():
452
+ if issequence(specifiers):
453
+ ret[meth_name] = set(specifiers)
454
+ else:
455
+ ret[meth_name] = set([specifiers])
456
+ return ret
457
+
458
+ def createMethod(cls, name, path, etype):
459
+ def rMethod(self, *args, **kwargs):
460
+ meth = ExternalMethod(path, name=name, etype=etype,
461
+ logger=self._logExternalStderr, env=self._externalEnv,
462
+ pre_func=self._logPreExec, post_func=self._logPostExec)
463
+ return meth(*args, **kwargs)
464
+
465
+ cls.adoptMethod(rMethod, name)
466
+ return rMethod
467
+
468
+ def adoptMethod(cls, meth, name):
469
+ meth.__name__ = name
470
+ meth.__module__ = cls.__module__
471
+
472
+
473
+ class ExternalMethods(PythonEgg):
474
+ __metaclass__ = MetaExternalMethods
475
+ externalMethodDirs = []
476
+ externalMethods = {}
477
+
478
+ def _logPreExec(self, method, cmdline, env):
479
+ pass
480
+
481
+ def _logPostExec(self, method, cmdline, env):
482
+ pass
483
+
484
+ def _logExternalStderr(self, method, line):
485
+ sys.stdout.write(line)
486
+
487
+ def _getExternalEnv(self):
488
+ return os.environ
489
+
490
+ class ExternalScript(ExScript, ExternalMethods):
491
+ externalMethodDirs = []
492
+ externalMethods = {}
493
+ settingsFiles = []
494
+
495
+ def __init__(self, *args, **kwargs):
496
+ super(ExternalScript, self).__init__(*args, **kwargs)
497
+ self._settings = dict(os.environ)
498
+
499
+ def getSettings(self):
500
+ return self._settings
501
+
502
+ def sourceEnv(self, fn):
503
+ SKIP = ['PWD', 'SHLVL', '_']
504
+ env = Popen(['/bin/bash', '-c', 'source %s; env' % fn], env=self.settings, stdout=PIPE).communicate()[0]
505
+ for line in env.splitlines():
506
+ try:
507
+ name, val = line.split('=', 1)
508
+ except ValueError:
509
+ continue
510
+ if val.startswith('()'):
511
+ continue
512
+ if name in SKIP: continue
513
+ self.settings[name] = val
514
+
515
+ def storeEnv(self, fn, info={}):
516
+ AU = 'Author'
517
+ DT = 'Date'
518
+ FN = 'Filename'
519
+ info = dict(info)
520
+
521
+ info[AU] = getpass.getuser()
522
+ info[DT] = time.strftime('%Y-%m-%d-%H:%M:%S')
523
+ info[FN] = os.path.basename(fn)
524
+ key_order = [AU, DT, FN]
525
+
526
+ fw = file(fn, 'w')
527
+ try:
528
+ fw.write("#!/bin/bash\n\n")
529
+ while info:
530
+ if key_order:
531
+ key = key_order.pop(0)
532
+ else:
533
+ key = sorted(info)[0]
534
+ value = info.pop(key)
535
+ fw.write("# %20s: %s\n" % (key, value))
536
+ fw.write('\n')
537
+
538
+ for key, value in sorted(self.settings.items()):
539
+ value = str(value)
540
+ if key not in os.environ or value != os.environ[key]:
541
+ if re.search(r'\s', value):
542
+ fw.write('export %s="%s"\n' % (key, value))
543
+ else:
544
+ fw.write('export %s=%s\n' % (key, value))
545
+
546
+ fw.write("\n\n###########################\n# Operating environment #\n###########################\n\n")
547
+ for key, value in sorted(os.environ.items()):
548
+ if re.search(r'\s', value):
549
+ fw.write('# %s="%s"\n' % (key, value))
550
+ else:
551
+ fw.write('# %s=%s\n' % (key, value))
552
+ finally:
553
+ fw.close()
554
+
555
+ def _getExternalEnv(self):
556
+ return self.settings
557
+
558
+ def _logPreExec(self, method, cmdline, env):
559
+ self.logger.info('Running external method %s.%s (%r)', self.__class__.__name__, method.name, ' '.join(cmdline))
560
+
561
+ def _logExternalStderr(self, method, line):
562
+ for l in line.splitlines():
563
+ self.logger.error('%s.%s: %s', self.__class__.__name__, method.name, l)
564
+
565
+ def getManagerArgs(self):
566
+ m_args = super(ExternalScript, self).getManagerArgs()
567
+ m_args['specification'].update({
568
+ '__premain__.settings': (Multiple, String),
569
+ })
570
+ m_args['docs'].update({
571
+ 'settings': "Additional shell files with settings",
572
+ })
573
+ return m_args
574
+
575
+ def premain(self, settings=[], **kwargs):
576
+ ret = super(ExternalScript, self).premain(**kwargs)
577
+ settings = self.settingsFiles + settings
578
+ for fn in settings:
579
+ self.sourceEnv(fn)
580
+ return ret