~rmcbride/ubuntu/lucid/ubuntuone-client/fixucg

« back to all changes in this revision

Viewing changes to ubuntuone/u1sync/main.py

  • Committer: Bazaar Package Importer
  • Author(s): Rodney Dawes
  • Date: 2009-08-26 12:15:00 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20090826121500-rivjec5146epys62
Tags: 0.93.0-0ubuntu1
* New upstream release.
  - Fix crash with KeyError in __getitem__ (LP: #404934)
  - Fix fatal error constant occurrances (LP: #414635)
  - Fix ability to reauthorize (LP: #406897)
  - Get rid of animated icon, and false activity status (LP: #414925)
* Update Standards-Version to 3.8.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
 
23
23
from __future__ import with_statement
24
24
 
 
25
import signal
25
26
import os
 
27
import sys
26
28
from errno import EEXIST
27
29
 
28
30
from optparse import OptionParser, SUPPRESS_HELP
31
33
gobject.set_application_name('u1sync')
32
34
 
33
35
from twisted.internet import reactor
34
 
from threading import Thread
 
36
from Queue import Queue
35
37
 
36
38
from ubuntuone.storageprotocol.oauth import OAuthToken
37
39
import ubuntuone.storageprotocol.dircontent_pb2 as dircontent_pb2
38
 
from ubuntuone.storageprotocol.dircontent_pb2 import DIRECTORY
 
40
from ubuntuone.storageprotocol.dircontent_pb2 import DIRECTORY, SYMLINK
39
41
from ubuntuone.u1sync.genericmerge import (
40
42
    show_tree, generic_merge)
41
43
from ubuntuone.u1sync.client import (
42
44
    ConnectionError, AuthenticationError, NoSuchShareError,
43
 
    Client)
 
45
    ForcedShutdown, Client)
44
46
from ubuntuone.u1sync.scan import scan_directory
45
47
from ubuntuone.u1sync.merge import (
46
48
    SyncMerge, ClobberServerMerge, ClobberLocalMerge, merge_trees)
76
78
 
77
79
class TreesDiffer(Exception):
78
80
    """Raised when diff tree differs."""
 
81
    def __init__(self, quiet):
 
82
        self.quiet = quiet
79
83
 
80
84
 
81
85
MERGE_ACTIONS = {
216
220
        user = user.encode("utf-8")
217
221
        print "%s  %s (from %s) [%s]%s" % (id, name, user, access, status)
218
222
 
219
 
def do_diff(client, share_spec, directory, quiet, subtree_path):
 
223
def do_diff(client, share_spec, directory, quiet, subtree_path,
 
224
            ignore_symlinks=True):
220
225
    """Diffs a local directory with the server."""
221
226
    if share_spec is not None:
222
227
        share_uuid = client.find_share(share_spec)
241
246
            if not quiet:
242
247
                print "%s missing from client" % display_path
243
248
        elif remote_node is None:
244
 
            if not quiet:
 
249
            if ignore_symlinks and local_node.node_type == SYMLINK:
 
250
                differs = False
 
251
            elif not quiet:
245
252
                print "%s missing from server" % display_path
246
253
        elif local_node.node_type != remote_node.node_type:
247
254
            local_type = node_type_str(local_node.node_type)
270
277
                            pre_merge=pre_merge, post_merge=post_merge,
271
278
                            partial_parent=("", False), name=u"")
272
279
    if differs:
273
 
        raise TreesDiffer()
 
280
        raise TreesDiffer(quiet=quiet)
274
281
 
275
282
def do_main(argv):
276
283
    """The main user-facing portion of the script."""
373
380
 
374
381
 
375
382
    if options.oauth is None:
376
 
        token = None
 
383
        passed_token = None
377
384
    else:
378
385
        try:
379
386
            (key, secret) = options.oauth.split(':', 2)
380
387
        except ValueError:
381
388
            parser.error("--oauth requires a key and secret together in the "
382
389
                         " form KEY:SECRET")
383
 
        token = OAuthToken(key, secret)
 
390
        passed_token = OAuthToken(key, secret)
384
391
 
385
392
    if options.share is not None:
386
393
        try:
391
398
    else:
392
399
        share_spec = None
393
400
 
394
 
    reactor_thread = Thread(target=
395
 
                            lambda: reactor.run(installSignalHandlers=False))
396
 
    reactor_thread.setDaemon(True)
397
 
    reactor_thread.start()
398
 
 
399
401
    client = Client(realm=options.realm, reactor=reactor)
400
402
 
401
 
    if token is None:
402
 
        should_create_token = (options.mode == "authorize")
403
 
        token = client.obtain_oauth_token(create_token=should_create_token)
404
 
 
405
 
    client.connect_ssl(options.host, int(options.port), options.no_ssl_verify)
406
 
 
407
 
    try:
408
 
        client.set_capabilities()
409
 
        client.oauth_from_token(token)
410
 
 
411
 
        if options.mode == "sync":
412
 
            do_sync(client=client, directory=directory, action=options.action,
413
 
                    dry_run=options.dry_run, quiet=options.quiet)
414
 
        elif options.mode == "init":
415
 
            do_init(client=client, share_spec=share_spec, directory=directory,
416
 
                    quiet=options.quiet, subtree_path=options.subtree)
417
 
        elif options.mode == "list-shares":
418
 
            do_list_shares(client=client)
419
 
        elif options.mode == "diff":
420
 
            do_diff(client=client, share_spec=share_spec, directory=directory,
421
 
                    quiet=options.quiet, subtree_path=options.subtree)
422
 
        elif options.mode == "authorize":
423
 
            if not options.quiet:
424
 
                print "Authorized."
425
 
    finally:
 
403
    signal.signal(signal.SIGINT, lambda s, f: client.force_shutdown())
 
404
    signal.signal(signal.SIGTERM, lambda s, f: client.force_shutdown())
 
405
 
 
406
    def run_client():
 
407
        """Run the blocking client."""
 
408
        if passed_token is None:
 
409
            should_create_token = (options.mode == "authorize")
 
410
            token = client.obtain_oauth_token(create_token=should_create_token)
 
411
        else:
 
412
            token = passed_token
 
413
 
 
414
        client.connect_ssl(options.host, int(options.port), options.no_ssl_verify)
 
415
 
426
416
        try:
 
417
            client.set_capabilities()
 
418
            client.oauth_from_token(token)
 
419
 
 
420
            if options.mode == "sync":
 
421
                do_sync(client=client, directory=directory,
 
422
                        action=options.action,
 
423
                        dry_run=options.dry_run, quiet=options.quiet)
 
424
            elif options.mode == "init":
 
425
                do_init(client=client, share_spec=share_spec,
 
426
                        directory=directory,
 
427
                        quiet=options.quiet, subtree_path=options.subtree)
 
428
            elif options.mode == "list-shares":
 
429
                do_list_shares(client=client)
 
430
            elif options.mode == "diff":
 
431
                do_diff(client=client, share_spec=share_spec,
 
432
                        directory=directory,
 
433
                        quiet=options.quiet, subtree_path=options.subtree,
 
434
                        ignore_symlinks=False)
 
435
            elif options.mode == "authorize":
 
436
                if not options.quiet:
 
437
                    print "Authorized."
 
438
        finally:
427
439
            client.disconnect()
 
440
 
 
441
    def capture_exception(queue, func):
 
442
        """Capture the exception from calling func."""
 
443
        try:
 
444
            func()
 
445
        except Exception, e:
 
446
            queue.put(sys.exc_info())
 
447
        else:
 
448
            queue.put(None)
428
449
        finally:
429
450
            reactor.callWhenRunning(reactor.stop)
430
 
            reactor_thread.join(1.0)
 
451
 
 
452
    queue = Queue()
 
453
    reactor.callInThread(capture_exception, queue, run_client)
 
454
    reactor.run(installSignalHandlers=False)
 
455
    exc_info = queue.get(True, 0.1)
 
456
    if exc_info:
 
457
        raise exc_info[0], exc_info[1], exc_info[2]
431
458
 
432
459
def main(*argv):
433
460
    """Top-level main function."""
446
473
        print "No matching share found."
447
474
    except ReadOnlyShareError:
448
475
        print "The selected action isn't possible on a read-only share."
449
 
    except KeyboardInterrupt:
 
476
    except (ForcedShutdown, KeyboardInterrupt):
450
477
        print "Interrupted!"
451
 
    except TreesDiffer:
452
 
        print "Trees differ."
 
478
    except TreesDiffer, e:
 
479
        if not e.quiet:
 
480
            print "Trees differ."
453
481
    else:
454
482
        return 0
455
483
    return 1