2 from ftplib import FTP, all_errors, error_temp
3 from netrc import netrc, NetrcParseError
4 from cumulus.store import Store, type_patterns, NotFoundError
6 class FtpStore (Store):
7 def __init__ (self, url, **kw):
10 upw, hp = self.netloc.split ('@')
15 host, port = hp.split (':')
21 user, passwd = upw.split (':')
28 user, acct, passwd = n.authenticators (host)
31 except (IOError, NetrcParseError):
37 self.prefix = self.path [1:] # skip *only* first '/'
41 def _get_path (self, type, name):
42 # we are in right directory
46 self.ftp.connect (self.host, self.port)
47 self.ftp.login (self.user, self.passwd)
48 self.ftp.cwd (self.prefix)
51 def list (self, type):
53 files = self.ftp.nlst ()
54 return (f for f in files if type_patterns[type].match (f))
56 def get (self, type, name):
58 self.ftp.sendcmd ('TYPE I')
59 sock = self.ftp.transfercmd ('RETR %s' % self._get_path (type, name))
61 return sock.makefile ()
63 def put (self, type, name, fp):
65 self.ftp.storbinary ("STOR %s" % self._get_path (type, name), fp)
67 def delete (self, type, name):
69 self.ftp.delete (self._get_path (type, name))
71 def stat (self, type, name):
72 """ Note that the size-command is non-standard but supported by
73 most ftp servers today. If size returns an error condition we
74 try nlst to detect if the file exists and return an bogus length
77 fn = self._get_path (type, name)
80 # my client doesn't accept size in ascii-mode
81 self.ftp.sendcmd ('TYPE I')
82 size = self.ftp.size (fn)
83 self.ftp.sendcmd ('TYPE A')
84 except all_errors, err:
89 print "nlst: %s" % fn, size
90 l = self.ftp.nlst (fn)
93 raise NotFoundError, (type, name)
96 """ After a get command at end of transfer a 2XX reply is still
97 in the input-queue, we have to get rid of that.
98 We also test here that the connection is still alive. If we get
99 a temporary error 421 ("error_temp") we reconnect: It was
105 self.ftp.sendcmd ('TYPE A')
106 except error_temp, err :
107 if not err.message.startswith ('421') :