Issue751758
This issue tracker has been migrated to GitHub,
and is currently read-only.
For more information,
see the GitHub FAQs in the Python's Developer Guide.
Created on 2003-06-10 06:51 by christianmlong, last changed 2022-04-10 16:09 by admin. This issue is now closed.
Files | ||||
---|---|---|---|---|
File name | Uploaded | Description | Edit | |
ftplib_bug_report_one_file.py | christianmlong, 2003-06-10 06:51 | Broken version, with working version appended |
Messages (5) | |||
---|---|---|---|
msg16314 - (view) | Author: Christian Long (christianmlong) | Date: 2003-06-10 06:51 | |
Subject: ftplib.retrbinary() fails when called from inside retrlines() callback function I'm using ftplib to backup files from a Linux server to a Windows 2000 worksation. Client: Windows 2000 Pro ActivePython 2.2.2 Build 224 (ActiveState Corp.) based on Python 2.2.2 (#37, Nov 26 2002, 10:24:37) [MSC 32 bit (Intel)] on win32 Komodo IDE Server: ProFTP server (ProFTPd version 1.25) Mandrake Linux 9.0 Summary: When I use it like this it works fine. # Build a list of files that are on the remote server f.retrlines('NLST', makeListOfFiles) --then-- # Iterate over the list, retrieving each file for remoteFileName in listOfFiles: --snip-- f.retrbinary('RETR %s' % remoteFileName, localFile.write) --snip-- But it fails if I try to do the retrieve directly in my callback function. def transferFile(listLine): --snip-- f.retrbinary('RETR %s' % remoteFileName, localFile.write) <--fails here on first time through --snip-- # get list of files from server, adn transfer each file as it gets listed f.retrlines('LIST', transferFile) --snip-- File "D:\My Documents\Computer World\Python Resources\My Utilities\backup_remote_files.py", line 45, in ? f.retrlines('LIST', transferFile) File "C:\Python22\lib\ftplib.py", line 413, in retrlines callback(line) File "D:\My Documents\Computer World\Python Resources\My Utilities\backup_remote_files.py", line 36, in transferFile f.retrbinary('RETR mra.py', localFile.write) --snip-- File "C:\Python22\lib\ftplib.py", line 300, in makepasv host, port = parse227(self.sendcmd('PASV')) File "C:\Python22\lib\ftplib.py", line 572, in parse227 raise error_reply, resp error_reply: 200 Type set to I. It looks like the server is returning a 200 instead of a 227 when retrbinary() is called inside a callback function for retrlines(). Files: 2 Files are included: a broken version and a version that works This One Is Broken - retrbinary() called from inside a callback function for retrlines(). =================================================== import ftplib import os import time REMOTE_DIR = "/home/mydir" LOCAL_DIR = "C:\My Documents" TIME_FORMAT = "%y%m%d" # YYMMDD, like 030522 def transferFile(listLine): # Strips the file name from a line of a # directory listing, and gets it from the # server. Depends on filenames # with no embedded spaces or extra dots. if listLine.endswith('.py'): #Split file name on the dot splitFileName=remoteFileName.split('.') # Add a timestamp localFileName="%s_%s.%s" % (splitFileName[0], time.strftime(TIME_FORMAT), splitFileName[1]) # Open a local file for (over)writing, in binary mode. # print os.path.join(LOCAL_DIR,localFileName) localFile=file(os.path.join(LOCAL_DIR,localFileName), 'wb') print remoteFileName print localFile # Execute the FTP retrieve command, calling # the write() function of the local file # for each block retrieved from the FTP server # BUG: This should work, but I get the following traceback f.retrbinary('RETR %s' % remoteFileName, localFile.write) #<--- Fails # Here # mra.py #<open file 'D:\My Documents\Work\IA\Miller\MRA\Dev\Backup of remote files\mra_030610.py', mode 'wb' at 0x00886B70> #Traceback (most recent call last): # File "C:\Program Files\ActiveState Komodo 2.3\callkomodo\kdb.py", line 430, in _do_start # self.kdb.run(code_ob, locals, locals) # File "C:\Python22\lib\bdb.py", line 349, in run # exec cmd in globals, locals # File "D:\My Documents\Computer World\Python Resources\My Utilities\backup_remote_files.py", line 45, in ? # f.retrlines('LIST', transferFile) # File "C:\Python22\lib\ftplib.py", line 413, in retrlines # callback(line) # File "D:\My Documents\Computer World\Python Resources\My Utilities\backup_remote_files.py", line 36, in transferFile # f.retrbinary('RETR mra.py', localFile.write) # File "C:\Python22\lib\ftplib.py", line 385, in retrbinary # conn = self.transfercmd(cmd, rest) # File "C:\Python22\lib\ftplib.py", line 346, in transfercmd # return self.ntransfercmd(cmd, rest)[0] # File "C:\Python22\lib\ftplib.py", line 322, in ntransfercmd # host, port = self.makepasv() # File "C:\Python22\lib\ftplib.py", line 300, in makepasv # host, port = parse227(self.sendcmd('PASV')) # File "C:\Python22\lib\ftplib.py", line 572, in parse227 # raise error_reply, resp #error_reply: 200 Type set to I. # The problem is that the self.sendcmd('PASV') call is not getting a 227 # reply from the server. Rather, it is getting a 200 reply, confirming # that the type was set to I (Image). localFile.flush() localFile.close() f=ftplib.FTP('server', 'user', 'password') f.cwd(REMOTE_DIR) # List directory contents, and call the transferFile # function on each line in the listing f.retrlines('LIST', transferFile) f.close() =================================================== This One Works - retlines() builds a list, and then files are transferred by iterating over that list and calling retrbinary() for each. =================================================== import ftplib import os import time REMOTE_DIR = "/home/mydir" LOCAL_DIR = "C:\My Documents" TIME_FORMAT = "%y%m%d" # YYMMDD, like 030522 listOfFiles = [] def makeListOfFiles(remoteFileName): # Strips the file name from a line of a # directory listing, and gets file from the # server. Depends on filenames # with no embedded spaces or extra dots. if remoteFileName.endswith('.py'): listOfFiles.append(remoteFileName) f=ftplib.FTP('server', 'user', 'password') f.cwd(REMOTE_DIR) # List directory contents, and call the transferFile # function on each line in the listing f.retrlines('NLST', makeListOfFiles) print listOfFiles for remoteFileName in listOfFiles: #Split file name on the dot splitFileName=remoteFileName.split('.') # Add a timestamp localFileName="%s_%s.%s" % (splitFileName[0], time.strftime(TIME_FORMAT), splitFileName[1]) # Open a local file for (over)writing, in binary mode. # print os.path.join(LOCAL_DIR,localFileName) localFile=file(os.path.join(LOCAL_DIR,localFileName), 'wb') # Execute the FTP retrieve command, calling # the write() function of the local file # for each block retrieved from the FTP server f.retrbinary('RETR %s' % remoteFileName, localFile.write) localFile.flush() localFile.close() f.close() =================================================== |
|||
msg16315 - (view) | Author: Shannon Jones (sjones) | Date: 2003-06-15 00:45 | |
Logged In: YES user_id=589306 The problem seems to happen when you use a callback within a function that was called as a callback. Here is a much simpler case that demonstrates the problem: ----------------------------- import ftplib def transferFile(listLine): filename = listLine.split()[-1] if filename == 'README': # Note that retrlines uses a default # callback that just prints the file f.retrlines('RETR README') # <-- Fails f=ftplib.FTP('ftp.python.org', 'ftp', 'anon@') f.cwd('/pub/python') f.retrlines('LIST', transferFile) f.close() ----------------------------- This fails with the following: Traceback (most recent call last): File "ftptest.py", line 10, in ? f.retrlines('LIST', transferFile) File "/home/sjones/src/python/dist/src/Lib/ftplib.py", line 407, in retrlines callback(line) File "ftptest.py", line 6, in transferFile f.retrlines('RETR README') # <-- Fails File "/home/sjones/src/python/dist/src/Lib/ftplib.py", line 396, in retrlines conn = self.transfercmd(cmd) File "/home/sjones/src/python/dist/src/Lib/ftplib.py", line 345, in transfercmd return self.ntransfercmd(cmd, rest)[0] File "/home/sjones/src/python/dist/src/Lib/ftplib.py", line 321, in ntransfercmd host, port = self.makepasv() File "/home/sjones/src/python/dist/src/Lib/ftplib.py", line 299, in makepasv host, port = parse227(self.sendcmd('PASV')) File "/home/sjones/src/python/dist/src/Lib/ftplib.py", line 566, in parse227 raise error_reply, resp ftplib.error_reply: 200 Type set to A. Note this is with the current CVS version on Redhat 9. |
|||
msg16316 - (view) | Author: Facundo Batista (facundobatista) * | Date: 2005-01-15 18:50 | |
Logged In: YES user_id=752496 Same behaviour, Py2.4 on Win2k, sp2. |
|||
msg73552 - (view) | Author: Giampaolo Rodola' (giampaolo.rodola) * | Date: 2008-09-22 02:21 | |
In FTP every data channel is supposed to be used for a unique transfer (RFC-1123, chapter 4.1.2.6) and every client should open only one data connection at time. Actually there isn't any official RFC which explicitly states my second sentence but the common practices for servers receiving a PORT or PASV request while another transfer is in progress usually are: - processing the request when the transfer is finished - creating a new data channel closing the old one - returning a 4xx temporarily failure response code IMHO it's your use case which, even if not "officially" declared incorrect, is usually discouraged and hence should not be covered by base ftplib module. My2cents |
|||
msg104051 - (view) | Author: Giampaolo Rodola' (giampaolo.rodola) * | Date: 2010-04-23 20:48 | |
Closing this out as rejected. |
History | |||
---|---|---|---|
Date | User | Action | Args |
2022-04-10 16:09:07 | admin | set | github: 38618 |
2010-04-23 20:48:36 | giampaolo.rodola | set | status: open -> closed type: behavior resolution: rejected messages: + msg104051 |
2008-09-22 02:21:59 | giampaolo.rodola | set | messages: + msg73552 |
2008-03-19 21:59:53 | giampaolo.rodola | set | nosy: + giampaolo.rodola |
2003-06-10 06:51:09 | christianmlong | create |