~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/__init__.py

Merge in bzrdir work to enable checkout improvements.

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
 
22
22
import errno
23
23
from copy import deepcopy
 
24
from stat import *
24
25
import sys
25
26
from unittest import TestSuite
26
27
 
227
228
        you may check via is_listable to determine if it will.
228
229
        """
229
230
        raise errors.TransportNotPossible("This transport has not "
230
 
                                          "implemented iter_files_recursive."
 
231
                                          "implemented iter_files_recursive "
231
232
                                          "(but must claim to be listable "
232
233
                                          "to trigger this error).")
233
234
 
333
334
 
334
335
        return self._iterate_over(relpaths, copy_entry, pb, 'copy_to', expand=False)
335
336
 
 
337
    def copy_tree(self, from_relpath, to_relpath):
 
338
        """Copy a subtree from one relpath to another.
 
339
 
 
340
        If a faster implementation is available, specific transports should 
 
341
        implement it.
 
342
        """
 
343
        source = self.clone(from_relpath)
 
344
        self.mkdir(to_relpath)
 
345
        target = self.clone(to_relpath)
 
346
        files = []
 
347
        directories = ['.']
 
348
        while directories:
 
349
            dir = directories.pop()
 
350
            if dir != '.':
 
351
                target.mkdir(dir)
 
352
            for path in source.list_dir(dir):
 
353
                path = dir + '/' + path
 
354
                stat = source.stat(path)
 
355
                if S_ISDIR(stat.st_mode):
 
356
                    directories.append(path)
 
357
                else:
 
358
                    files.append(path)
 
359
        source.copy_to(files, target)
336
360
 
337
361
    def move(self, rel_from, rel_to):
338
362
        """Move the item at rel_from to the location at rel_to.
340
364
        If a transport can directly implement this it is suggested that
341
365
        it do so for efficiency.
342
366
        """
343
 
        self.copy(rel_from, rel_to)
344
 
        self.delete(rel_from)
 
367
        if S_ISDIR(self.stat(rel_from).st_mode):
 
368
            self.copy_tree(rel_from, rel_to)
 
369
            self.delete_tree(rel_from)
 
370
        else:
 
371
            self.copy(rel_from, rel_to)
 
372
            self.delete(rel_from)
345
373
 
346
374
    def move_multi(self, relpaths, pb=None):
347
375
        """Move a bunch of entries.
371
399
        """
372
400
        return self._iterate_over(relpaths, self.delete, pb, 'delete', expand=False)
373
401
 
 
402
    def delete_tree(self, relpath):
 
403
        """Delete an entire tree. This may require a listable transport."""
 
404
        subtree = self.clone(relpath)
 
405
        files = []
 
406
        directories = ['.']
 
407
        pending_rmdirs = []
 
408
        while directories:
 
409
            dir = directories.pop()
 
410
            if dir != '.':
 
411
                pending_rmdirs.append(dir)
 
412
            for path in subtree.list_dir(dir):
 
413
                path = dir + '/' + path
 
414
                stat = subtree.stat(path)
 
415
                if S_ISDIR(stat.st_mode):
 
416
                    directories.append(path)
 
417
                else:
 
418
                    files.append(path)
 
419
        subtree.delete_multi(files)
 
420
        pending_rmdirs.reverse()
 
421
        for dir in pending_rmdirs:
 
422
            subtree.rmdir(dir)
 
423
        self.rmdir(relpath)
 
424
 
374
425
    def stat(self, relpath):
375
426
        """Return the stat information for a file.
376
427
        WARNING: This may not be implementable for all protocols, so use
383
434
        """
384
435
        raise NotImplementedError
385
436
 
 
437
    def rmdir(self, relpath):
 
438
        """Remove a directory at the given path."""
 
439
        raise NotImplementedError
 
440
 
386
441
    def stat_multi(self, relpaths, pb=None):
387
442
        """Stat multiple files and return the information.
388
443
        """
405
460
        it if at all possible.
406
461
        """
407
462
        raise errors.TransportNotPossible("This transport has not "
408
 
                                          "implemented list_dir."
 
463
                                          "implemented list_dir "
409
464
                                          "(but must claim to be listable "
410
465
                                          "to trigger this error).")
411
466
 
431
486
 
432
487
 
433
488
def get_transport(base):
 
489
    """Open a transport to access a URL or directory.
 
490
 
 
491
    base is either a URL or a directory name.  
 
492
    """
434
493
    global _protocol_handlers
435
494
    if base is None:
436
495
        base = u'.'
466
525
 
467
526
 
468
527
class Server(object):
469
 
    """Basic server API for paths."""
 
528
    """A Transport Server.
 
529
    
 
530
    The Server interface provides a server for a given transport. We use
 
531
    these servers as loopback testing tools. For any given transport the
 
532
    Servers it provides must either allow writing, or serve the contents
 
533
    of os.getcwdu() at the time setUp is called.
 
534
    
 
535
    Note that these are real servers - they must implement all the things
 
536
    that we want bzr transports to take advantage of.
 
537
    """
470
538
 
471
539
    def setUp(self):
472
540
        """Setup the server to service requests."""
475
543
        """Remove the server and cleanup any resources it owns."""
476
544
 
477
545
    def get_url(self):
478
 
        """Return a url for this server to os.getcwdu.
 
546
        """Return a url for this server.
479
547
        
480
548
        If the transport does not represent a disk directory (i.e. it is 
481
 
        a databse like svn, or a memory only transport, it should return
 
549
        a database like svn, or a memory only transport, it should return
482
550
        a connection to a newly established resource for this Server.
 
551
        Otherwise it should return a url that will provide access to the path
 
552
        that was os.getcwdu() when setUp() was called.
483
553
        
484
554
        Subsequent calls will return the same resource.
485
555
        """
491
561
 
492
562
 
493
563
class TransportTestProviderAdapter(object):
494
 
    """A class which can setup transport interface tests."""
 
564
    """A tool to generate a suite testing all transports for a single test.
 
565
 
 
566
    This is done by copying the test once for each transport and injecting
 
567
    the transport_class and transport_server classes into each copy. Each copy
 
568
    is also given a new id() to make it easy to identify.
 
569
    """
495
570
 
496
571
    def adapt(self, test):
497
572
        result = TestSuite()
514
589
        """Return a list of the klass, server_factory pairs to test."""
515
590
        result = []
516
591
        for module in _get_transport_modules():
517
 
            result.extend(self.get_transport_test_permutations(reduce(getattr, 
518
 
                (module).split('.')[1:],
519
 
                 __import__(module))))
 
592
            try:
 
593
                result.extend(self.get_transport_test_permutations(reduce(getattr, 
 
594
                    (module).split('.')[1:],
 
595
                     __import__(module))))
 
596
            except errors.DependencyNotPresent, e:
 
597
                # Continue even if a dependency prevents us 
 
598
                # from running this test
 
599
                pass
520
600
        return result
521
601
        
522
602
 
528
608
register_lazy_transport('https://', 'bzrlib.transport.http', 'HttpTransport')
529
609
register_lazy_transport('ftp://', 'bzrlib.transport.ftp', 'FtpTransport')
530
610
register_lazy_transport('aftp://', 'bzrlib.transport.ftp', 'FtpTransport')
531
 
register_lazy_transport('memory://', 'bzrlib.transport.memory', 'MemoryTransport')
 
611
register_lazy_transport('memory:/', 'bzrlib.transport.memory', 'MemoryTransport')
 
612
register_lazy_transport('readonly+', 'bzrlib.transport.readonly', 'ReadonlyTransportDecorator')