~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/ftp.py

  • Committer: John Arbash Meinel
  • Date: 2006-11-10 15:38:16 UTC
  • mto: This revision was merged to the branch mainline in revision 2129.
  • Revision ID: john@arbash-meinel.com-20061110153816-46acf76fc86a512b
use try/finally to clean up a nested progress bar during weave fetching

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2005 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
29
29
import errno
30
30
import ftplib
31
31
import os
32
 
import os.path
33
32
import urllib
34
33
import urlparse
35
 
import select
36
34
import stat
37
35
import threading
38
36
import time
41
39
 
42
40
from bzrlib import (
43
41
    errors,
44
 
    osutils,
45
42
    urlutils,
46
43
    )
47
44
from bzrlib.trace import mutter, warning
50
47
    split_url,
51
48
    Transport,
52
49
    )
53
 
from bzrlib.transport.local import LocalURLServer
54
50
import bzrlib.ui
55
51
 
56
52
_have_medusa = False
165
161
        if ('no such file' in s
166
162
            or 'could not open' in s
167
163
            or 'no such dir' in s
168
 
            or 'could not create file' in s # vsftpd
169
 
            or 'file doesn\'t exist' in s
170
164
            ):
171
165
            raise errors.NoSuchFile(path, extra=extra)
172
166
        if ('file exists' in s):
306
300
        :param retries: Number of retries after temporary failures so far
307
301
                        for this operation.
308
302
 
309
 
        TODO: jam 20051215 ftp as a protocol seems to support chmod, but
310
 
        ftplib does not
 
303
        TODO: jam 20051215 ftp as a protocol seems to support chmod, but ftplib does not
311
304
        """
312
305
        abspath = self._abspath(relpath)
313
306
        tmp_abspath = '%s.tmp.%.9f.%d.%d' % (abspath, time.time(),
319
312
            f = self._get_FTP()
320
313
            try:
321
314
                f.storbinary('STOR '+tmp_abspath, fp)
322
 
                self._rename_and_overwrite(tmp_abspath, abspath, f)
 
315
                f.rename(tmp_abspath, abspath)
323
316
            except (ftplib.error_temp,EOFError), e:
324
317
                warning("Failure during ftp PUT. Deleting temporary file.")
325
318
                try:
330
323
                    raise e
331
324
                raise
332
325
        except ftplib.error_perm, e:
333
 
            self._translate_perm_error(e, abspath, extra='could not store',
334
 
                                       unknown_exc=errors.NoSuchFile)
 
326
            self._translate_perm_error(e, abspath, extra='could not store')
335
327
        except ftplib.error_temp, e:
336
328
            if retries > _number_of_retries:
337
329
                raise errors.TransportError("FTP temporary error during PUT %s. Aborting."
439
431
    #       to give it its own address as the 'to' location.
440
432
    #       So implement a fancier 'copy()'
441
433
 
442
 
    def rename(self, rel_from, rel_to):
443
 
        abs_from = self._abspath(rel_from)
444
 
        abs_to = self._abspath(rel_to)
445
 
        mutter("FTP rename: %s => %s", abs_from, abs_to)
446
 
        f = self._get_FTP()
447
 
        return self._rename(abs_from, abs_to, f)
448
 
 
449
 
    def _rename(self, abs_from, abs_to, f):
450
 
        try:
451
 
            f.rename(abs_from, abs_to)
452
 
        except ftplib.error_perm, e:
453
 
            self._translate_perm_error(e, abs_from,
454
 
                ': unable to rename to %r' % (abs_to))
455
 
 
456
434
    def move(self, rel_from, rel_to):
457
435
        """Move the item at rel_from to the location at rel_to"""
458
436
        abs_from = self._abspath(rel_from)
460
438
        try:
461
439
            mutter("FTP mv: %s => %s", abs_from, abs_to)
462
440
            f = self._get_FTP()
463
 
            self._rename_and_overwrite(abs_from, abs_to, f)
 
441
            f.rename(abs_from, abs_to)
464
442
        except ftplib.error_perm, e:
465
443
            self._translate_perm_error(e, abs_from,
466
444
                extra='unable to rename to %r' % (rel_to,), 
467
445
                unknown_exc=errors.PathError)
468
446
 
469
 
    def _rename_and_overwrite(self, abs_from, abs_to, f):
470
 
        """Do a fancy rename on the remote server.
471
 
 
472
 
        Using the implementation provided by osutils.
473
 
        """
474
 
        osutils.fancy_rename(abs_from, abs_to,
475
 
            rename_func=lambda p1, p2: self._rename(p1, p2, f),
476
 
            unlink_func=lambda p: self._delete(p, f))
 
447
    rename = move
477
448
 
478
449
    def delete(self, relpath):
479
450
        """Delete the item at relpath"""
480
451
        abspath = self._abspath(relpath)
481
 
        f = self._get_FTP()
482
 
        self._delete(abspath, f)
483
 
 
484
 
    def _delete(self, abspath, f):
485
452
        try:
486
453
            mutter("FTP rm: %s", abspath)
 
454
            f = self._get_FTP()
487
455
            f.delete(abspath)
488
456
        except ftplib.error_perm, e:
489
457
            self._translate_perm_error(e, abspath, 'error deleting',
490
458
                unknown_exc=errors.NoSuchFile)
491
459
 
492
 
    def external_url(self):
493
 
        """See bzrlib.transport.Transport.external_url."""
494
 
        # FTP URL's are externally usable.
495
 
        return self.base
496
 
 
497
460
    def listable(self):
498
461
        """See Transport.listable."""
499
462
        return True
564
527
 
565
528
 
566
529
class FtpServer(Server):
567
 
    """Common code for FTP server facilities."""
 
530
    """Common code for SFTP server facilities."""
568
531
 
569
532
    def __init__(self):
570
533
        self._root = None
586
549
        """This is used by medusa.ftp_server to log connections, etc."""
587
550
        self.logs.append(message)
588
551
 
589
 
    def setUp(self, vfs_server=None):
 
552
    def setUp(self):
 
553
 
590
554
        if not _have_medusa:
591
555
            raise RuntimeError('Must have medusa to run the FtpServer')
592
556
 
593
 
        assert vfs_server is None or isinstance(vfs_server, LocalURLServer), \
594
 
            "FtpServer currently assumes local transport, got %s" % vfs_server
595
 
 
596
557
        self._root = os.getcwdu()
597
558
        self._ftp_server = _ftp_server(
598
559
            authorizer=_test_authorizer(root=self._root),
604
565
        self._port = self._ftp_server.getsockname()[1]
605
566
        # Don't let it loop forever, or handle an infinite number of requests.
606
567
        # In this case it will run for 100s, or 1000 requests
607
 
        self._async_thread = threading.Thread(
608
 
                target=FtpServer._asyncore_loop_ignore_EBADF,
 
568
        self._async_thread = threading.Thread(target=asyncore.loop,
609
569
                kwargs={'timeout':0.1, 'count':1000})
610
570
        self._async_thread.setDaemon(True)
611
571
        self._async_thread.start()
617
577
        asyncore.close_all()
618
578
        self._async_thread.join()
619
579
 
620
 
    @staticmethod
621
 
    def _asyncore_loop_ignore_EBADF(*args, **kwargs):
622
 
        """Ignore EBADF during server shutdown.
623
 
 
624
 
        We close the socket to get the server to shutdown, but this causes
625
 
        select.select() to raise EBADF.
626
 
        """
627
 
        try:
628
 
            asyncore.loop(*args, **kwargs)
629
 
        except select.error, e:
630
 
            if e.args[0] != errno.EBADF:
631
 
                raise
632
 
 
633
580
 
634
581
_ftp_channel = None
635
582
_ftp_server = None
700
647
            pfrom = self.filesystem.translate(self._renaming)
701
648
            self._renaming = None
702
649
            pto = self.filesystem.translate(line[1])
703
 
            if os.path.exists(pto):
704
 
                self.respond('550 RNTO failed: file exists')
705
 
                return
706
650
            try:
707
651
                os.rename(pfrom, pto)
708
652
            except (IOError, OSError), e:
709
653
                # TODO: jam 20060516 return custom responses based on
710
654
                #       why the command failed
711
 
                # (bialix 20070418) str(e) on Python 2.5 @ Windows
712
 
                # sometimes don't provide expected error message;
713
 
                # so we obtain such message via os.strerror()
714
 
                self.respond('550 RNTO failed: %s' % os.strerror(e.errno))
 
655
                self.respond('550 RNTO failed: %s' % (e,))
715
656
            except:
716
657
                self.respond('550 RNTO failed')
717
658
                # For a test server, we will go ahead and just die
749
690
                    self.filesystem.mkdir (path)
750
691
                    self.respond ('257 MKD command successful.')
751
692
                except (IOError, OSError), e:
752
 
                    # (bialix 20070418) str(e) on Python 2.5 @ Windows
753
 
                    # sometimes don't provide expected error message;
754
 
                    # so we obtain such message via os.strerror()
755
 
                    self.respond ('550 error creating directory: %s' %
756
 
                                  os.strerror(e.errno))
 
693
                    self.respond ('550 error creating directory: %s' % (e,))
757
694
                except:
758
695
                    self.respond ('550 error creating directory.')
759
696